1 /** basic lighting */ 2 3 module re.gfx.lighting.basic; 4 5 import re.core; 6 import re.ecs; 7 import re.ng.manager; 8 import re.ng.scene3d; 9 import re.gfx; 10 import re.math; 11 import std.algorithm; 12 import std.container.array; 13 import optional; 14 static import raylib; 15 16 /// acts as a manager for Light3D components 17 class BasicSceneLightManager : Manager, Updatable { 18 /// max lights supported by shader 19 private enum max_lights = 4; 20 private Array!(ShaderLight) _lights; 21 private Array!Light3D _comps; 22 private int light_count; 23 24 /// the lighting shader 25 public Shader shader; 26 27 public float ambient = 0.4; 28 public float light_clamp = 1.0; 29 public float shine_amount = 16; 30 public bool light_quantize = false; 31 32 private enum ShaderLightType { 33 LIGHT_DIRECTIONAL, 34 LIGHT_POINT 35 } 36 37 private struct ShaderLight { 38 int type; 39 Vector3 position; 40 Vector3 target; 41 Color color; 42 bool enabled; 43 44 // Shader locations 45 int enabledLoc; 46 int typeLoc; 47 int posLoc; 48 int targetLoc; 49 int colorLoc; 50 } 51 52 this() { 53 // load the shader 54 auto maybe_shader = Core.content.load_shader("shader/basic_lighting.vert", 55 "shader/basic_lighting.frag"); 56 if (maybe_shader == none) { 57 assert(0, "failed to load basic lighting shaders"); 58 } 59 shader = maybe_shader.front; 60 // get some shader locations 61 shader.locs[raylib.ShaderLocationIndex.SHADER_LOC_MATRIX_MODEL] = raylib.GetShaderLocation(shader, 62 "matModel"); 63 shader.locs[raylib.ShaderLocationIndex.SHADER_LOC_VECTOR_VIEW] = raylib.GetShaderLocation(shader, 64 "viewPos"); 65 66 _lights.reserve(max_lights); 67 _comps.reserve(max_lights); 68 } 69 70 override void update() { 71 // update camera view pos in light shader 72 float[3] camera_pos = [ 73 (cast(Scene3D) scene).cam.transform.position.x, (cast(Scene3D) scene) 74 .cam.transform.position.y, (cast(Scene3D) scene).cam.transform.position.z 75 ]; 76 raylib.SetShaderValue(shader, shader.locs[raylib.ShaderLocationIndex.SHADER_LOC_VECTOR_VIEW], 77 &camera_pos, raylib.ShaderUniformDataType.SHADER_UNIFORM_VEC3); 78 79 // update lights 80 for (int i = 0; i < light_count; i++) { 81 // sync fields 82 _lights[i].position = _comps[i].transform.position; 83 _lights[i].color = _comps[i].color; 84 _lights[i].enabled = _comps[i].light_enabled; 85 86 // update shader values 87 update_shader_lights(shader, _lights[i]); 88 } 89 90 update_light_options(); 91 } 92 93 override void destroy() { 94 while (light_count > 0) { 95 unregister(_comps[0]); 96 } 97 } 98 99 private void register(Light3D light_comp) { 100 assert(light_count < max_lights, "maximum light count exceeded."); 101 // add a light 102 _lights.insertBack(set_light(light_count, ShaderLightType.LIGHT_POINT, 103 light_comp.transform.position, Vector3Zero, light_comp.color, shader)); 104 _comps.insertBack(light_comp); 105 // set internal light reference 106 light_comp._light = _lights[light_count]; 107 light_count++; 108 } 109 110 private void unregister(Light3D light_comp) { 111 import std.range : dropExactly, takeOne; 112 113 auto removed_index = cast(int) _comps[].countUntil(light_comp); 114 // clear all lights 115 for (int i = 0; i < light_count; i++) { 116 clear_light(i, shader); 117 } 118 _comps.linearRemove(_comps[].dropExactly(removed_index).takeOne); 119 _lights.linearRemove(_lights[].dropExactly(removed_index).takeOne); 120 light_count--; // we're removing a light 121 // ensure our lengths match 122 assert(_lights.length == light_count); 123 assert(_lights.length == _comps.length); 124 // reactivate the lights 125 for (int i = 0; i < light_count; i++) { 126 // update shader 127 _lights[i] = set_light(i, ShaderLightType.LIGHT_POINT, 128 _comps[i].transform.position, Vector3Zero, _comps[i].color, shader); 129 // set associated light 130 _comps[i]._light = _lights[i]; 131 } 132 } 133 134 private void update_light_options() { 135 // ambient light level 136 auto ambient_loc = raylib.GetShaderLocation(shader, "ambient"); 137 float[4] ambient_val = [ambient, ambient, ambient, 1]; 138 raylib.SetShaderValue(shader, ambient_loc, &ambient_val, 139 raylib.ShaderUniformDataType.SHADER_UNIFORM_VEC4); 140 141 // light clamp 142 auto light_clamp_loc = raylib.GetShaderLocation(shader, "light_clamp"); 143 raylib.SetShaderValue(shader, light_clamp_loc, &light_clamp, 144 raylib.ShaderUniformDataType.SHADER_UNIFORM_FLOAT); 145 146 // specular shine 147 auto shine_loc = raylib.GetShaderLocation(shader, "shine"); 148 raylib.SetShaderValue(shader, shine_loc, &shine_amount, 149 raylib.ShaderUniformDataType.SHADER_UNIFORM_FLOAT); 150 151 // light quantize 152 auto light_quantize_loc = raylib.GetShaderLocation(shader, "light_quantize"); 153 raylib.SetShaderValue(shader, light_quantize_loc, &light_quantize, 154 raylib.ShaderUniformDataType.SHADER_UNIFORM_INT); 155 } 156 157 // - ported from rlights 158 159 private static ShaderLight set_light(int index, ShaderLightType type, 160 Vector3 pos, Vector3 target, Color color, Shader shader, bool enabled = true) { 161 ShaderLight light; 162 163 light.enabled = enabled; 164 light.type = type; 165 light.position = pos; 166 light.target = target; 167 light.color = color; 168 169 char[32] enabledName = "lights[x].enabled\0"; 170 char[32] typeName = "lights[x].type\0"; 171 char[32] posName = "lights[x].position\0"; 172 char[32] targetName = "lights[x].target\0"; 173 char[32] colorName = "lights[x].color\0"; 174 175 // Set location name [x] depending on lights count 176 enabledName[7] = cast(char)('0' + index); 177 typeName[7] = cast(char)('0' + index); 178 posName[7] = cast(char)('0' + index); 179 targetName[7] = cast(char)('0' + index); 180 colorName[7] = cast(char)('0' + index); 181 182 light.enabledLoc = raylib.GetShaderLocation(shader, cast(char*) enabledName); 183 light.typeLoc = raylib.GetShaderLocation(shader, cast(char*) typeName); 184 light.posLoc = raylib.GetShaderLocation(shader, cast(char*) posName); 185 light.targetLoc = raylib.GetShaderLocation(shader, cast(char*) targetName); 186 light.colorLoc = raylib.GetShaderLocation(shader, cast(char*) colorName); 187 188 update_shader_lights(shader, light); 189 190 return light; 191 } 192 193 private static void clear_light(int index, Shader shader) { 194 // reset the light 195 set_light(index, ShaderLightType.LIGHT_POINT, Vector3Zero, Vector3Zero, 196 Colors.BLANK, shader, false); 197 } 198 199 // Send light properties to shader 200 // NOTE: ShaderLight shader locations should be available 201 private static void update_shader_lights(Shader shader, ShaderLight light) { 202 // Send to shader light enabled state and type 203 raylib.SetShaderValue(shader, light.enabledLoc, &light.enabled, 204 raylib.ShaderUniformDataType.SHADER_UNIFORM_INT); 205 raylib.SetShaderValue(shader, light.typeLoc, &light.type, 206 raylib.ShaderUniformDataType.SHADER_UNIFORM_INT); 207 208 // Send to shader light position values 209 float[3] position = [ 210 light.position.x, light.position.y, light.position.z 211 ]; 212 raylib.SetShaderValue(shader, light.posLoc, &position, 213 raylib.ShaderUniformDataType.SHADER_UNIFORM_VEC3); 214 215 // Send to shader light target position values 216 float[3] target = [light.target.x, light.target.y, light.target.z]; 217 raylib.SetShaderValue(shader, light.targetLoc, &target, 218 raylib.ShaderUniformDataType.SHADER_UNIFORM_VEC3); 219 220 // Send to shader light color values 221 float[4] color = [ 222 cast(float) light.color.r / cast(float) 255, 223 cast(float) light.color.g / cast(float) 255, 224 cast(float) light.color.b / cast(float) 255, 225 cast(float) light.color.a / cast(float) 255 226 ]; 227 raylib.SetShaderValue(shader, light.colorLoc, &color, 228 raylib.ShaderUniformDataType.SHADER_UNIFORM_VEC4); 229 } 230 } 231 232 /// represents a 3D light 233 class Light3D : Component, Renderable3D { 234 mixin Reflect; 235 private BasicSceneLightManager _mgr; 236 private enum phys_size = 0.2; 237 private BasicSceneLightManager.ShaderLight _light; 238 239 /// the color of the light 240 public Color color; 241 242 /// whether the light is enabled 243 public bool light_enabled = true; 244 245 /// creates a new light with a given color 246 this(Color color = Colors.WHITE) { 247 this.color = color; 248 } 249 250 override void setup() { 251 // register the light in the manager 252 auto mgr = entity.scene.get_manager!BasicSceneLightManager(); 253 assert(!mgr.isNull, "scene did not have BasicSceneLightManager registered." 254 ~ "please add that to the scene before creating this component."); 255 _mgr = mgr.get; 256 _mgr.register(this); 257 } 258 259 override void destroy() { 260 _mgr.unregister(this); 261 } 262 263 @property BoundingBox bounds() { 264 auto size = Vector3(phys_size, phys_size, phys_size); 265 return BoundingBox(entity.position - size, entity.position + size); 266 } 267 268 void render() { 269 } 270 271 void debug_render() { 272 import re.ng.diag.render; 273 274 raylib.DrawSphereEx(entity.position, phys_size, 8, 8, color); 275 raylib.DrawSphereWires(entity.position, phys_size * 1.5, 2, 2, DebugRender.debug_color); 276 } 277 }