1 /*
2 Copyright (c) 2017-2018 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.graphics.material;
29 
30 import std.math;
31 import std.algorithm;
32 import dlib.core.memory;
33 import dlib.math.vector;
34 import dlib.image.color;
35 import dlib.container.dict;
36 import derelict.opengl;
37 import dagon.core.ownership;
38 import dagon.graphics.texture;
39 import dagon.graphics.rc;
40 
41 enum
42 {
43     CBlack = Color4f(0.0f, 0.0f, 0.0f, 1.0f),
44     CWhite = Color4f(1.0f, 1.0f, 1.0f, 1.0f),
45     CRed = Color4f(1.0f, 0.0f, 0.0f, 1.0f),
46     COrange = Color4f(1.0f, 0.5f, 0.0f, 1.0f),
47     CYellow = Color4f(1.0f, 1.0f, 0.0f, 1.0f),
48     CGreen = Color4f(0.0f, 1.0f, 0.0f, 1.0f),
49     CCyan = Color4f(0.0f, 1.0f, 1.0f, 1.0f),
50     CBlue = Color4f(0.0f, 0.0f, 1.0f, 1.0f),
51     CPurple = Color4f(0.5f, 0.0f, 1.0f, 1.0f),
52     CMagenta = Color4f(1.0f, 0.0f, 1.0f, 1.0f)
53 }
54 
55 enum MaterialInputType
56 {
57     Undefined,
58     Bool,
59     Integer,
60     Float,
61     Vec2,
62     Vec3,
63     Vec4
64 }
65 
66 struct MaterialInput
67 {
68     MaterialInputType type;
69     union
70     {
71         bool asBool;
72         int asInteger;
73         float asFloat;
74         Vector2f asVector2f;
75         Vector3f asVector3f;
76         Vector4f asVector4f;
77     }
78     Texture texture;
79     
80     float getNumericValue()
81     {
82         float res;
83         if (type == MaterialInputType.Bool ||
84             type == MaterialInputType.Integer)
85         {
86             res = asInteger;
87         }
88         else if (type == MaterialInputType.Float)
89         {
90             res = asFloat;
91         }
92         return res;
93     }
94     
95     Color4f sample(float u, float v)
96     {
97         if (texture !is null)
98         {
99             int x = cast(int)floor(u * texture.width);
100             int y = cast(int)floor(v * texture.height);
101             return texture.image[x, y];
102         }
103         else if (type == MaterialInputType.Vec4)
104             return Color4f(asVector4f);
105         else if (type == MaterialInputType.Vec3)
106             return Color4f(asVector3f.x, asVector3f.y, asVector3f.z, 1.0f);
107         else if (type == MaterialInputType.Vec2)
108             return Color4f(asVector2f.x, asVector2f.y, 1.0f, 1.0f);
109         else if (type == MaterialInputType.Float)
110             return Color4f(asFloat, 1.0f, 1.0f, 1.0f);
111         else if (type == MaterialInputType.Bool ||
112                  type == MaterialInputType.Integer)
113             return Color4f(cast(float)asInteger, 1.0f, 1.0f, 1.0f);
114         else
115             return Color4f(0.0f, 0.0f, 0.0f, 0.0f);
116     }
117 }
118 
119 MaterialInput materialInput(float v)
120 {
121     MaterialInput mi;
122     mi.asFloat = v;
123     mi.type = MaterialInputType.Float;
124     return mi;
125 }
126 
127 abstract class Material: Owner
128 {
129     Dict!(MaterialInput, string) inputs;
130 
131     this(Owner o)
132     {
133         super(o);
134 
135         inputs = New!(Dict!(MaterialInput, string));
136     }
137 
138     ~this()
139     {
140         Delete(inputs);
141     }
142 
143     final void opDispatch(string name, T)(T value) @property
144     {
145         setInput(name, value);
146     }
147 
148     final MaterialInput* setInput(T)(string name, T value)
149     {
150         MaterialInput input;
151         static if (is(T == bool))
152         {
153             input.type = MaterialInputType.Bool;
154             input.asBool = value;
155         }
156         else static if (is(T == int))
157         {
158             input.type = MaterialInputType.Integer;
159             input.asInteger = value;
160         }
161         else static if (is(T == float) || is(T == double))
162         {
163             input.type = MaterialInputType.Float;
164             input.asFloat = value;
165         }
166         else static if (is(T == Vector2f))
167         {
168             input.type = MaterialInputType.Vec2;
169             input.asVector2f = value;
170         }
171         else static if (is(T == Vector3f))
172         {
173             input.type = MaterialInputType.Vec3;
174             input.asVector3f = value;
175         }
176         else static if (is(T == Vector4f))
177         {
178             input.type = MaterialInputType.Vec4;
179             input.asVector4f = value;
180         }
181         else static if (is(T == Color4f))
182         {
183             input.type = MaterialInputType.Vec4;
184             input.asVector4f = value;
185         }
186         else static if (is(T == Texture))
187         {
188             input.texture = value;
189             if (value.format == GL_RED)
190                 input.type = MaterialInputType.Float;
191             else if (value.format == GL_RG)
192                 input.type = MaterialInputType.Vec2;
193             else if (value.format == GL_RGB)
194                 input.type = MaterialInputType.Vec3;
195             else if (value.format == GL_RGBA)
196                 input.type = MaterialInputType.Vec4;
197         }
198         else
199         {
200             input.type = MaterialInputType.Undefined;
201         }
202 
203         inputs[name] = input;
204         return (name in inputs);
205     }
206 
207     void bind(RenderingContext* rc);
208     void unbind(RenderingContext* rc);
209 }
210