1 /* 2 Copyright (c) 2017-2019 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.light; 29 30 import std.stdio; 31 import std.math; 32 import std.conv; 33 import std.random; 34 35 import dlib.core.memory; 36 import dlib.math.vector; 37 import dlib.math.matrix; 38 import dlib.math.transformation; 39 import dlib.math.quaternion; 40 import dlib.container.array; 41 import dlib.image.color; 42 43 import dagon.core.libs; 44 import dagon.core.ownership; 45 import dagon.graphics.view; 46 import dagon.graphics.rc; 47 import dagon.graphics.shadow; 48 import dagon.logics.entity; 49 import dagon.logics.behaviour; 50 import dagon.resource.scene; 51 52 enum LightType 53 { 54 AreaSphere = 1, 55 AreaTube = 2, 56 Sun = 3, 57 Spot = 4 58 } 59 60 class LightSource: Owner 61 { 62 Vector3f position; 63 Quaternionf rotation; 64 Vector3f color; 65 float radius; // max light attenuation radius 66 float areaRadius; // light's own radius 67 float tubeLength; 68 float energy; 69 float spotOuterCutoff; 70 float spotInnerCutoff; 71 //float spotExponent; 72 LightType type; 73 bool shadowEnabled; 74 ShadowMap shadowMap; 75 76 this(Owner o) 77 { 78 super(o); 79 this.position = Vector3f(0.0f, 0.0f, 0.0f); 80 this.rotation = Quaternionf.identity; 81 this.tubeLength = 1.0f; 82 this.color = Vector3f(1.0f, 1.0f, 1.0f); 83 this.radius = 1.0f; 84 this.areaRadius = 0.0f; 85 this.energy = 1.0f; 86 this.type = LightType.AreaSphere; 87 this.shadowEnabled = false; 88 } 89 90 this(Vector3f pos, Vector3f col, float attRadius, float areaRadius, float energy, Owner o) 91 { 92 super(o); 93 this.position = pos; 94 this.rotation = Quaternionf.identity; 95 this.tubeLength = 1.0f; 96 this.color = col; 97 this.radius = attRadius; 98 this.areaRadius = areaRadius; 99 this.energy = energy; 100 this.type = LightType.AreaSphere; 101 this.shadowEnabled = false; 102 } 103 104 Vector3f direction() @property 105 { 106 return rotation.rotate(Vector3f(0, 0, 1)); 107 } 108 109 Vector3f directionEye(Matrix4x4f viewMatrix) 110 { 111 Vector4f dirHGVector = Vector4f(direction); 112 dirHGVector.w = 0.0; 113 return (dirHGVector * viewMatrix).xyz; 114 } 115 116 void shadow(bool mode) @property 117 { 118 if (mode) 119 { 120 if (type == LightType.Sun) 121 { 122 if (shadowMap is null) 123 shadowMap = New!CascadedShadowMap(this, 1024, 10, 30, 200, -100, 100, this); 124 } 125 } 126 127 shadowEnabled = mode; 128 } 129 130 bool shadow() @property 131 { 132 return shadowEnabled; 133 } 134 135 void updateShadow(Vector3f cameraPosition, Vector3f cameraDirection, RenderingContext* rc, double timeStep) 136 { 137 if (type == LightType.Sun) 138 { 139 if (shadowMap && shadowEnabled) 140 shadowMap.update(cameraPosition, cameraDirection, rc, timeStep); 141 } 142 } 143 144 void renderShadow(Scene scene, RenderingContext* rc) 145 { 146 if (type == LightType.Sun) 147 { 148 if (shadowMap && shadowEnabled) 149 shadowMap.render(scene, rc); 150 } 151 } 152 } 153 154 class LightManager: Owner 155 { 156 DynamicArray!LightSource lightSources; 157 158 this(Owner o) 159 { 160 super(o); 161 } 162 163 ~this() 164 { 165 lightSources.free(); 166 } 167 168 LightSource addPointLight(Vector3f position, Color4f color, float energy, float radius, float areaRadius = 0.0f) 169 { 170 lightSources.append(New!LightSource(position, color.rgb, radius, areaRadius, energy, this)); 171 return lightSources.data[$-1]; 172 } 173 174 LightSource addSunLight(Quaternionf rotation, Color4f color, float energy) 175 { 176 LightSource light = New!LightSource(this); 177 light.rotation = rotation; 178 light.color = color.rgb; 179 light.energy = energy; 180 light.type = LightType.Sun; 181 lightSources.append(light); 182 return light; 183 } 184 185 LightSource addSpotLight(Vector3f position, Color4f color, float energy, Quaternionf rotation, float outerCutoff, float innerCutoff, float volumeRadius) 186 { 187 LightSource light = New!LightSource(this); 188 light.position = position; 189 light.rotation = rotation; 190 light.color = color.rgb; 191 light.energy = energy; 192 light.spotOuterCutoff = outerCutoff; 193 light.spotInnerCutoff = innerCutoff; 194 //light.spotExponent = exponent; 195 light.radius = volumeRadius; 196 light.type = LightType.Spot; 197 lightSources.append(light); 198 return light; 199 } 200 201 void updateShadows(View view, RenderingContext* rc, double timeStep) 202 { 203 Vector3f cameraDirection = -view.invViewMatrix.forward; 204 cameraDirection.y = 0.0f; 205 cameraDirection = cameraDirection.normalized; 206 updateShadows(view.cameraPosition, cameraDirection, rc, timeStep); 207 } 208 209 void updateShadows(Vector3f cameraPosition, Vector3f cameraDirection, RenderingContext* rc, double timeStep) 210 { 211 foreach(light; lightSources.data) 212 light.updateShadow(cameraPosition, cameraDirection, rc, timeStep); 213 } 214 215 void renderShadows(Scene scene, RenderingContext* rc) 216 { 217 foreach(light; lightSources.data) 218 light.renderShadow(scene, rc); 219 } 220 } 221 222 // Attach a light to Entity 223 class LightBehaviour: Behaviour 224 { 225 LightSource light; 226 227 this(Entity e, LightSource light) 228 { 229 super(e); 230 231 this.light = light; 232 } 233 234 override void update(double dt) 235 { 236 light.position = entity.position; 237 } 238 }