1 module dlangui.graphics.scene.material;
2 
3 public import dlangui.core.config;
4 static if (ENABLE_OPENGL):
5 static if (BACKEND_GUI):
6 
7 import dlangui.core.types;
8 import dlangui.core.logger;
9 import dlangui.graphics.glsupport;
10 import dlangui.graphics.gldrawbuf;
11 import dlangui.graphics.scene.effect;
12 import dlangui.graphics.scene.node;
13 import dlangui.graphics.scene.mesh;
14 import dlangui.graphics.scene.light;
15 
16 /// Reference counted Material object
17 alias MaterialRef = Ref!Material;
18 
19 class Material : RefCountedObject {
20     // effect
21     protected EffectRef _effect;
22     protected EffectId _effectId;
23     protected string _autoEffectParams;
24     protected EffectId _autoEffectId;
25 
26     // textures
27     protected TextureRef _texture;
28     protected string _textureId;
29     protected bool _textureLinear = true;
30 
31     protected TextureRef _bumpTexture;
32     protected string _bumpTextureId;
33 
34     // colors
35     protected vec4 _diffuseColor = vec4(1, 1, 1, 1);
36     protected vec3 _ambientColor = vec3(0.2, 0.2, 0.2);
37     protected vec4 _modulateColor = vec4(1, 1, 1, 1);
38     protected float _modulateAlpha = 1;
39 
40     /// 0 - specular is disabled, 1 .. 256 - specular exponent
41     protected float _specular = 0;
42 
43     // TODO: more material properties
44 
45     this() {
46     }
47 
48     this(EffectId effectId, string textureId, string bumpTextureId = null) {
49         _effectId = effectId;
50         _autoEffectParams = null;
51         _autoEffectId = effectId;
52         _textureId = textureId;
53         _bumpTextureId = bumpTextureId;
54     }
55 
56     @property vec4 diffuseColor() { return _diffuseColor; }
57     @property Material diffuseColor(vec4 color) { _diffuseColor = color; return this; }
58     @property vec3 ambientColor() { return _ambientColor; }
59     @property Material ambientColor(vec3 color) { _ambientColor = color; return this; }
60     @property vec4 modulateColor() { return _modulateColor; }
61     @property Material modulateColor(vec4 color) { _modulateColor = color; return this; }
62     @property float modulateAlpha() { return _modulateAlpha; }
63     @property Material modulateColor(float a) { _modulateAlpha = a; return this; }
64     @property float specular() { return _specular; }
65     @property Material specular(float a) { _specular = a; return this; }
66 
67     @property EffectRef effect() {
68         if (_effect.isNull && !_autoEffectId.empty)
69             _effect = EffectCache.instance.get(_autoEffectId);
70         return _effect;
71     }
72     /// set as effect instance
73     @property Material effect(EffectRef e) {
74         _effect = e;
75         return this;
76     }
77     /// set as effect id
78     @property Material effect(EffectId effectId) {
79         if (_effectId == effectId)
80             return this; // no change
81         _effectId = effectId;
82         _autoEffectId = EffectId(_effectId, _autoEffectParams);
83         _effect.clear();
84         return this;
85     }
86 
87     protected @property Material autoEffectParams(string params) {
88         if (_autoEffectParams != params && !_effectId.empty) {
89             _autoEffectId = EffectId(_effectId, params);
90             _autoEffectParams = params;
91             _effect.clear();
92         }
93         return this;
94     }
95 
96     @property TextureRef texture() {
97         if (_texture.isNull && _textureId.length) {
98             _texture = GLTextureCache.instance.get(_textureId);
99         }
100         return _texture;
101     }
102     /// set texture
103     @property Material texture(TextureRef e) {
104         _texture = e;
105         return this;
106     }
107     /// set texture from resourceId
108     @property Material texture(string resourceId) {
109         if (_textureId == resourceId)
110             return this; // no change
111         _texture.clear();
112         _textureId = resourceId;
113         return this;
114     }
115     @property bool textureLinear() { return _textureLinear; }
116     @property Material textureLinear(bool v) { _textureLinear = v; return this; }
117 
118 
119     @property TextureRef bumpTexture() {
120         if (_bumpTexture.isNull && _bumpTextureId.length) {
121             _bumpTexture = GLTextureCache.instance.get(_bumpTextureId);
122         }
123         return _bumpTexture;
124     }
125     /// set texture
126     @property Material bumpTexture(TextureRef e) {
127         _bumpTexture = e;
128         return this;
129     }
130     /// set texture from resourceId
131     @property Material bumpTexture(string resourceId) {
132         if (_bumpTextureId == resourceId)
133             return this; // no change
134         _bumpTexture.clear();
135         _bumpTextureId = resourceId;
136         return this;
137     }
138 
139     FogParams _fogParams;
140     @property FogParams fogParams() { return _fogParams; }
141     @property Material fogParams(FogParams fogParams) { _fogParams = fogParams; return this; }
142 
143     private AutoParams _lastParams;
144     private string _lastDefs;
145     string calcAutoEffectParams(Mesh mesh, LightParams * lights) {
146         AutoParams newParams = AutoParams(mesh, lights, _specular, !bumpTexture.isNull, _fogParams);
147         if (newParams != _lastParams) {
148             _lastParams = newParams;
149             _lastDefs = _lastParams.defs;
150         }
151         return _lastDefs;
152     }
153 
154     void bind(Node3d node, Mesh mesh, LightParams * lights = null) {
155         autoEffectParams = calcAutoEffectParams(mesh, lights);
156         assert(!effect.isNull);
157         effect.bind();
158         if (!texture.isNull) {
159             texture.texture.setup();
160             texture.texture.setSamplerParams(_textureLinear, true, true);
161         }
162         if (!bumpTexture.isNull) {
163             bumpTexture.texture.setup(1);
164             bumpTexture.texture.setSamplerParams(true, true, false);
165         }
166         // matrixes, positions uniforms
167         if (_effect.hasUniform(DefaultUniform.u_worldViewProjectionMatrix))
168             _effect.setUniform(DefaultUniform.u_worldViewProjectionMatrix, node.projectionViewModelMatrix);
169         if (_effect.hasUniform(DefaultUniform.u_cameraPosition))
170             _effect.setUniform(DefaultUniform.u_cameraPosition, node.cameraPosition);
171         if (_effect.hasUniform(DefaultUniform.u_worldViewMatrix))
172             _effect.setUniform(DefaultUniform.u_worldViewMatrix, node.worldViewMatrix);
173         if (_effect.hasUniform(DefaultUniform.u_inverseTransposeWorldViewMatrix))
174             _effect.setUniform(DefaultUniform.u_inverseTransposeWorldViewMatrix, node.inverseTransposeWorldViewMatrix);
175 
176         // color uniforms
177         if (_effect.hasUniform(DefaultUniform.u_ambientColor))
178             _effect.setUniform(DefaultUniform.u_ambientColor, _ambientColor);
179         if (_effect.hasUniform(DefaultUniform.u_diffuseColor))
180             _effect.setUniform(DefaultUniform.u_diffuseColor, _diffuseColor);
181         if (_effect.hasUniform(DefaultUniform.u_modulateColor))
182             _effect.setUniform(DefaultUniform.u_modulateColor, _modulateColor);
183         if (_effect.hasUniform(DefaultUniform.u_modulateAlpha))
184             _effect.setUniform(DefaultUniform.u_modulateAlpha, _modulateAlpha);
185         if (_effect.hasUniform(DefaultUniform.u_specularExponent))
186             _effect.setUniform(DefaultUniform.u_specularExponent, _specular);
187 
188         // fog uniforms
189         if (_fogParams) {
190             if (_effect.hasUniform(DefaultUniform.u_fogColor))
191                 _effect.setUniform(DefaultUniform.u_fogColor, _fogParams.fogColor);
192             if (_effect.hasUniform(DefaultUniform.u_fogMinDistance))
193                 _effect.setUniform(DefaultUniform.u_fogMinDistance, _fogParams.fogMinDistance);
194             if (_effect.hasUniform(DefaultUniform.u_fogMaxDistance))
195                 _effect.setUniform(DefaultUniform.u_fogMaxDistance, _fogParams.fogMaxDistance);
196         }
197 
198         // lighting uniforms
199         if (lights && !lights.empty) {
200             if (lights.u_directionalLightDirection.length) {
201                 if (_effect.hasUniform(DefaultUniform.u_directionalLightDirection)) {
202                     _effect.setUniform(DefaultUniform.u_directionalLightDirection, lights.u_directionalLightDirection);
203                     //Log.d("DefaultUniform.u_directionalLightDirection: ", lights.u_directionalLightDirection);
204                 }
205                 if (_effect.hasUniform(DefaultUniform.u_directionalLightColor))
206                     _effect.setUniform(DefaultUniform.u_directionalLightColor, lights.u_directionalLightColor);
207             }
208             if (lights.u_pointLightPosition.length) {
209                 if (_effect.hasUniform(DefaultUniform.u_pointLightPosition))
210                     _effect.setUniform(DefaultUniform.u_pointLightPosition, lights.u_pointLightPosition);
211                 if (_effect.hasUniform(DefaultUniform.u_pointLightColor))
212                     _effect.setUniform(DefaultUniform.u_pointLightColor, lights.u_pointLightColor);
213                 if (_effect.hasUniform(DefaultUniform.u_pointLightRangeInverse))
214                     _effect.setUniform(DefaultUniform.u_pointLightRangeInverse, lights.u_pointLightRangeInverse);
215             }
216             if (lights.u_spotLightPosition.length) {
217                 if (_effect.hasUniform(DefaultUniform.u_spotLightPosition))
218                     _effect.setUniform(DefaultUniform.u_spotLightPosition, lights.u_spotLightPosition);
219                 if (_effect.hasUniform(DefaultUniform.u_spotLightDirection))
220                     _effect.setUniform(DefaultUniform.u_spotLightDirection, lights.u_spotLightDirection);
221                 if (_effect.hasUniform(DefaultUniform.u_spotLightColor))
222                     _effect.setUniform(DefaultUniform.u_spotLightColor, lights.u_spotLightColor);
223                 if (_effect.hasUniform(DefaultUniform.u_spotLightRangeInverse))
224                     _effect.setUniform(DefaultUniform.u_spotLightRangeInverse, lights.u_spotLightRangeInverse);
225                 if (_effect.hasUniform(DefaultUniform.u_spotLightInnerAngleCos))
226                     _effect.setUniform(DefaultUniform.u_spotLightInnerAngleCos, lights.u_spotLightInnerAngleCos);
227                 if (_effect.hasUniform(DefaultUniform.u_spotLightOuterAngleCos))
228                     _effect.setUniform(DefaultUniform.u_spotLightOuterAngleCos, lights.u_spotLightOuterAngleCos);
229             }
230         }
231     }
232 
233     void drawMesh(Mesh mesh, bool wireframe) {
234         effect.draw(mesh, wireframe);
235     }
236 
237     void unbind() {
238         if (!texture.isNull) {
239             texture.texture.unbind();
240         }
241         if (!bumpTexture.isNull) {
242             bumpTexture.texture.unbind();
243         }
244         effect.unbind();
245     }
246 }
247 
248 struct AutoParams {
249     ubyte directionalLightCount = 0;
250     ubyte pointLightCount = 0;
251     ubyte spotLightCount = 0;
252     bool vertexColor = false;
253     bool specular = false;
254     bool bumpMapping = false;
255     FogParams fogParams;
256     this(Mesh mesh, LightParams * lights, float specular, bool bumpMapping, FogParams fogParams) {
257         if (mesh)
258             vertexColor = mesh.hasElement(VertexElementType.COLOR);
259         if (lights) {
260             directionalLightCount = cast(ubyte)lights.u_directionalLightDirection.length;
261             pointLightCount = cast(ubyte)lights.u_pointLightPosition.length;
262             spotLightCount = cast(ubyte)lights.u_spotLightPosition.length;
263         }
264         this.specular = specular > 0.01;
265         this.bumpMapping = bumpMapping;
266         this.fogParams = fogParams;
267     }
268     string defs() {
269         import std.conv : to;
270         char[] buf;
271         if (fogParams) {
272             buf ~= "FOG";
273         }
274         if (directionalLightCount) {
275             if (buf.length)
276                 buf ~= ";";
277             buf ~= "DIRECTIONAL_LIGHT_COUNT ";
278             buf ~= directionalLightCount.to!string;
279         }
280         if (pointLightCount) {
281             if (buf.length)
282                 buf ~= ";";
283             buf ~= "POINT_LIGHT_COUNT ";
284             buf ~= pointLightCount.to!string;
285         }
286         if (spotLightCount) {
287             if (buf.length)
288                 buf ~= ";";
289             buf ~= "SPOT_LIGHT_COUNT ";
290             buf ~= spotLightCount.to!string;
291         }
292         if (vertexColor) {
293             if (buf.length)
294                 buf ~= ";";
295             buf ~= "VERTEX_COLOR";
296         }
297         if (specular) {
298             if (buf.length)
299                 buf ~= ";";
300             buf ~= "SPECULAR";
301         }
302         if (bumpMapping) {
303             if (buf.length)
304                 buf ~= ";";
305             buf ~= "BUMPED";
306         }
307         return buf.dup;
308     }
309 }
310 
311 class FogParams {
312     immutable vec4 fogColor;
313     immutable float fogMinDistance;
314     immutable float fogMaxDistance;
315     this(vec4 fogColor, float fogMinDistance, float fogMaxDistance) {
316         this.fogColor = fogColor;
317         this.fogMinDistance = fogMinDistance;
318         this.fogMaxDistance = fogMaxDistance;
319     }
320 }