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