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 }