1 module dlangui.graphics.scene.light;
2 
3 public import dlangui.core.config;
4 static if (ENABLE_OPENGL):
5 static if (BACKEND_GUI):
6 
7 import dlangui.core.math3d;
8 import dlangui.core.types;
9 
10 import std.conv : to;
11 
12 enum LightType : ubyte {
13     directional,
14     point,
15     spot
16 }
17 
18 /// Reference counted Light object
19 alias LightRef = Ref!Light;
20 
21 class Light : RefCountedObject {
22 
23     import dlangui.graphics.scene.node;
24 
25     protected Node3d _node;
26 
27     protected vec3 _color;
28 
29     protected bool _autobind = true;
30     protected bool _enabled = true;
31 
32     protected this(vec3 color) { _color = color; }
33 
34     @property vec3 color() const { return _color; }
35     @property Light color(vec3 c) { _color = c; return this; }
36     @property LightType type() const { return LightType.directional; }
37 
38     @property bool autobind() const { return _autobind; }
39     @property Light autobind(bool flg) { _autobind = flg; return this; }
40 
41     @property bool enabled() const { return _enabled; }
42     @property Light enabled(bool flg) { _enabled = flg; return this; }
43 
44     @property Node3d node() { return _node; }
45     @property Light node(Node3d n) { _node = n; return this; }
46 
47     /// direction in world coordinates
48     @property vec3 direction() { return _node ? _node.forwardVectorView : vec3(0, 0, 1); }
49     /// position in world coordinates
50     @property vec3 position() { return _node ? _node.translationView : vec3(0, 0, 0); }
51 
52     @property float range() const { return 1.0; }
53     @property void range(float v) { assert(false); }
54     @property float rangeInverse() const { return 1.0; }
55 
56     @property float innerAngle() const { assert(false); }
57     @property float innerAngleCos() const { assert(false); }
58     @property float outerAngle() const { assert(false); }
59     @property float outerAngleCos() const { assert(false); }
60 
61     /// create new directional light
62     static Light createDirectional(vec3 color) {
63         return new DirectionalLight(color);
64     }
65     /// create new point light
66     static Light createPoint(vec3 color, float range) {
67         return new PointLight(color, range);
68     }
69     /// create new point light
70     static Light createSpot(vec3 color, float range, float innerAngle, float outerAngle) {
71         return new SpotLight(color, range, innerAngle, outerAngle);
72     }
73 }
74 
75 protected class DirectionalLight : Light {
76     protected this(vec3 color) {
77         super(color);
78     }
79 }
80 
81 protected class PointLight : Light {
82     protected float _range = 1;
83     protected float _rangeInverse = 1;
84     protected this(vec3 color, float range) {
85         super(color);
86         _range = range;
87         _rangeInverse = 1 / range;
88     }
89 
90     override @property LightType type() const { return LightType.point; }
91 
92     override @property float range() const { return _range; }
93     override @property void range(float v) {
94         _range = v;
95         _rangeInverse = 1 / v;
96     }
97     override @property float rangeInverse() const { return _rangeInverse; }
98 }
99 
100 protected class SpotLight : PointLight {
101     protected float _innerAngle;
102     protected float _innerAngleCos;
103     protected float _outerAngle;
104     protected float _outerAngleCos;
105     protected this(vec3 color, float range, float innerAngle, float outerAngle) {
106         import std.math;
107         super(color, range);
108         _innerAngle = innerAngle;
109         _outerAngle = outerAngle;
110         _innerAngleCos = cos(innerAngle);
111         _outerAngleCos = cos(outerAngle);
112     }
113 
114     override @property LightType type() const { return LightType.spot; }
115 
116     override @property float innerAngle() const { return _innerAngle; }
117     override @property float innerAngleCos() const { return _innerAngleCos; }
118     override @property float outerAngle() const { return _outerAngle; }
119     override @property float outerAngleCos() const { return _outerAngleCos; }
120 }
121 
122 alias LightCounts = int[3];
123 
124 /// light collection
125 struct Lights {
126     Light[] directional;
127     Light[] point;
128     Light[] spot;
129     void reset() {
130         directional.length = 0;
131         point.length = 0;
132         spot.length = 0;
133     }
134     @property bool empty() const { return directional.length + point.length + spot.length == 0; }
135     /// returns point types by type
136     @property LightCounts counts() const { return [cast(int)directional.length, cast(int)point.length, cast(int)spot.length]; }
137     @property int directionalCount() const { return cast(int)directional.length; }
138     @property int pointCount() const { return cast(int)point.length; }
139     @property int spotCount() const { return cast(int)spot.length; }
140     ///// return light count definition for shaders, e.g. "DIRECTIONAL_LIGHT_COUNT 2;POINT_LIGHT_COUNT 1"
141     //@property string defs() const {
142     //    if (!directional.length && !point.length && !spot.length)
143     //        return null;
144     //    static __gshared char[] buf;
145     //    buf.length = 0; // reset buffer
146     //    if (directional.length) {
147     //        buf ~= "DIRECTIONAL_LIGHT_COUNT ";
148     //        buf ~= directional.length.to!string;
149     //    }
150     //    if (point.length) {
151     //        if (buf.length)
152     //            buf ~= ";";
153     //        buf ~= "POINT_LIGHT_COUNT ";
154     //        buf ~= point.length.to!string;
155     //    }
156     //    if (spot.length) {
157     //        if (buf.length)
158     //            buf ~= ";";
159     //        buf ~= "SPOT_LIGHT_COUNT ";
160     //        buf ~= spot.length.to!string;
161     //    }
162     //    return buf.dup;
163     //}
164     void remove(Light light) {
165         import std.algorithm : remove;
166         switch(light.type) {
167             case LightType.directional:
168                 foreach(index, v; directional)
169                     if (v is light) {
170                         directional = directional.remove(index);
171                         return;
172                     }
173                 directional ~= light;
174                 break;
175             case LightType.point:
176                 foreach(index, v; point)
177                     if (v is light) {
178                         point = point.remove(index);
179                         return;
180                     }
181                 point ~= light;
182                 break;
183             case LightType.spot:
184                 foreach(index, v; spot)
185                     if (v is light) {
186                         spot = spot.remove(index);
187                         return;
188                     }
189                 spot ~= light;
190                 break;
191             default:
192                 break;
193         }
194     }
195     /// returns true if light is added (not a duplicate, and enabled)
196     bool add(Light light) {
197         switch(light.type) {
198             case LightType.directional:
199                 foreach(v; directional)
200                     if (v is light)
201                         return false;
202                 directional ~= light;
203                 return true;
204             case LightType.point:
205                 foreach(v; point)
206                     if (v is light)
207                         return false;
208                 point ~= light;
209                 return true;
210             case LightType.spot:
211                 foreach(v; spot)
212                     if (v is light)
213                         return false;
214                 spot ~= light;
215                 return true;
216             default:
217                 return false;
218         }
219     }
220     Lights clone() {
221         Lights res;
222         if (directional.length)
223             res.directional ~= directional;
224         if (point.length)
225             res.point ~= point;
226         if (spot.length)
227             res.spot ~= spot;
228         return res;
229     }
230 }
231 
232 struct LightParams {
233     Lights _lights;
234 
235     @property bool empty() const { return _lights.empty; }
236     //@property string defs() const { return _lights.defs; }
237 
238     void reset() {
239         _lights.reset();
240         u_directionalLightDirection.length = 0;
241         u_directionalLightColor.length = 0;
242 
243         u_pointLightPosition.length = 0;
244         u_pointLightColor.length = 0;
245         u_pointLightRangeInverse.length = 0;
246 
247         u_spotLightPosition.length = 0;
248         u_spotLightDirection.length = 0;
249         u_spotLightColor.length = 0;
250         u_spotLightRangeInverse.length = 0;
251         u_spotLightInnerAngleCos.length = 0;
252         u_spotLightOuterAngleCos.length = 0;
253     }
254 
255     void reset(ref LightParams params) {
256         reset();
257         if (params._lights.directional.length)
258             _lights.directional ~= params._lights.directional;
259         if (params._lights.point.length)
260             _lights.point ~= params._lights.point;
261         if (params._lights.spot.length)
262             _lights.spot ~= params._lights.spot;
263 
264         u_directionalLightDirection ~= params.u_directionalLightDirection;
265         u_directionalLightColor ~= params.u_directionalLightColor;
266 
267         u_pointLightPosition ~= params.u_pointLightPosition;
268         u_pointLightColor ~= params.u_pointLightColor;
269         u_pointLightRangeInverse ~= params.u_pointLightRangeInverse;
270 
271         u_spotLightPosition ~= params.u_spotLightPosition;
272         u_spotLightDirection ~= params.u_spotLightDirection;
273         u_spotLightColor ~= params.u_spotLightColor;
274         u_spotLightRangeInverse ~= params.u_spotLightRangeInverse;
275         u_spotLightInnerAngleCos ~= params.u_spotLightInnerAngleCos;
276         u_spotLightOuterAngleCos ~= params.u_spotLightOuterAngleCos;
277     }
278 
279     void add(ref Lights lights) {
280         foreach(light; lights.directional)
281             add(light);
282         foreach(light; lights.point)
283             add(light);
284         foreach(light; lights.spot)
285             add(light);
286     }
287 
288     /// returns true if light is added (not a duplicate, and enabled)
289     bool add(Light light) {
290         if (!light.node || !light.enabled || !_lights.add(light))
291             return false;
292         switch(light.type) {
293             case LightType.directional:
294                 u_directionalLightDirection ~= light.direction;
295                 u_directionalLightColor ~= light.color;
296                 return true;
297             case LightType.point:
298                 u_pointLightPosition ~= light.position;
299                 u_pointLightColor ~= light.color;
300                 u_pointLightRangeInverse ~= light.rangeInverse;
301                 return true;
302             case LightType.spot:
303                 u_spotLightPosition ~= light.position;
304                 u_spotLightDirection ~= light.direction;
305                 u_spotLightColor ~= light.color;
306                 u_spotLightRangeInverse ~= light.rangeInverse;
307                 u_spotLightInnerAngleCos ~= light.innerAngleCos;
308                 u_spotLightOuterAngleCos ~= light.outerAngleCos;
309                 return true;
310             default:
311                 return false;
312         }
313     }
314 
315     vec3[] u_directionalLightDirection;
316     vec3[] u_directionalLightColor;
317 
318     vec3[] u_pointLightPosition;
319     vec3[] u_pointLightColor;
320     float[] u_pointLightRangeInverse;
321 
322     vec3[] u_spotLightPosition;
323     vec3[] u_spotLightDirection;
324     vec3[] u_spotLightColor;
325     float[] u_spotLightRangeInverse;
326     float[] u_spotLightInnerAngleCos;
327     float[] u_spotLightOuterAngleCos;
328 }