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 }