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 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 _lights.reserve(max_lights); 62 _comps.reserve(max_lights); 63 } 64 65 override void update() { 66 // update camera view pos in light shader 67 float[3] camera_pos = [ 68 (cast(Scene3D) scene).cam.transform.position.x, (cast(Scene3D) scene) 69 .cam.transform.position.y, (cast(Scene3D) scene).cam.transform.position.z 70 ]; 71 raylib.SetShaderValue(shader, shader.locs[raylib.ShaderLocationIndex.LOC_VECTOR_VIEW], 72 &camera_pos, raylib.ShaderUniformDataType.UNIFORM_VEC3); 73 74 // update lights 75 for (int i = 0; i < light_count; i++) { 76 // sync fields 77 _lights[i].position = _comps[i].transform.position; 78 _lights[i].color = _comps[i].color; 79 _lights[i].enabled = _comps[i].light_enabled; 80 81 // update shader values 82 update_shader_lights(shader, _lights[i]); 83 } 84 } 85 86 override void destroy() { 87 while (light_count > 0) { 88 unregister(_comps[0]); 89 } 90 } 91 92 private void register(Light3D light_comp) { 93 assert(light_count < max_lights, "maximum light count exceeded."); 94 // add a light 95 _lights.insertBack(set_light(light_count, ShaderLightType.LIGHT_POINT, 96 light_comp.transform.position, Vector3Zero, light_comp.color, shader)); 97 _comps.insertBack(light_comp); 98 // set internal light reference 99 light_comp._light = _lights[light_count]; 100 light_count++; 101 } 102 103 private void unregister(Light3D light_comp) { 104 import std.range : dropExactly, takeOne; 105 106 auto removed_index = cast(int) _comps[].countUntil(light_comp); 107 // clear all lights 108 for (int i = 0; i < light_count; i++) { 109 clear_light(i, shader); 110 } 111 _comps.linearRemove(_comps[].dropExactly(removed_index).takeOne); 112 _lights.linearRemove(_lights[].dropExactly(removed_index).takeOne); 113 light_count--; // we're removing a light 114 // ensure our lengths match 115 assert(_lights.length == light_count); 116 assert(_lights.length == _comps.length); 117 // reactivate the lights 118 for (int i = 0; i < light_count; i++) { 119 // update shader 120 _lights[i] = set_light(i, ShaderLightType.LIGHT_POINT, 121 _comps[i].transform.position, Vector3Zero, _comps[i].color, shader); 122 // set associated light 123 _comps[i]._light = _lights[i]; 124 } 125 } 126 127 // - ported from rlights 128 129 private static ShaderLight set_light(int index, ShaderLightType type, 130 Vector3 pos, Vector3 target, Color color, Shader shader, bool enabled = true) { 131 ShaderLight light; 132 133 light.enabled = enabled; 134 light.type = type; 135 light.position = pos; 136 light.target = target; 137 light.color = color; 138 139 char[32] enabledName = "lights[x].enabled\0"; 140 char[32] typeName = "lights[x].type\0"; 141 char[32] posName = "lights[x].position\0"; 142 char[32] targetName = "lights[x].target\0"; 143 char[32] colorName = "lights[x].color\0"; 144 145 // Set location name [x] depending on lights count 146 enabledName[7] = cast(char)('0' + index); 147 typeName[7] = cast(char)('0' + index); 148 posName[7] = cast(char)('0' + index); 149 targetName[7] = cast(char)('0' + index); 150 colorName[7] = cast(char)('0' + index); 151 152 light.enabledLoc = raylib.GetShaderLocation(shader, cast(char*) enabledName); 153 light.typeLoc = raylib.GetShaderLocation(shader, cast(char*) typeName); 154 light.posLoc = raylib.GetShaderLocation(shader, cast(char*) posName); 155 light.targetLoc = raylib.GetShaderLocation(shader, cast(char*) targetName); 156 light.colorLoc = raylib.GetShaderLocation(shader, cast(char*) colorName); 157 158 update_shader_lights(shader, light); 159 160 return light; 161 } 162 163 private static void clear_light(int index, Shader shader) { 164 // reset the light 165 set_light(index, ShaderLightType.LIGHT_POINT, Vector3Zero, Vector3Zero, 166 Colors.BLANK, shader, false); 167 } 168 169 // Send light properties to shader 170 // NOTE: ShaderLight shader locations should be available 171 private static void update_shader_lights(Shader shader, ShaderLight light) { 172 // Send to shader light enabled state and type 173 raylib.SetShaderValue(shader, light.enabledLoc, &light.enabled, 174 raylib.ShaderUniformDataType.UNIFORM_INT); 175 raylib.SetShaderValue(shader, light.typeLoc, &light.type, 176 raylib.ShaderUniformDataType.UNIFORM_INT); 177 178 // Send to shader light position values 179 float[3] position = [ 180 light.position.x, light.position.y, light.position.z 181 ]; 182 raylib.SetShaderValue(shader, light.posLoc, &position, 183 raylib.ShaderUniformDataType.UNIFORM_VEC3); 184 185 // Send to shader light target position values 186 float[3] target = [light.target.x, light.target.y, light.target.z]; 187 raylib.SetShaderValue(shader, light.targetLoc, &target, 188 raylib.ShaderUniformDataType.UNIFORM_VEC3); 189 190 // Send to shader light color values 191 float[4] color = [ 192 cast(float) light.color.r / cast(float) 255, 193 cast(float) light.color.g / cast(float) 255, 194 cast(float) light.color.b / cast(float) 255, 195 cast(float) light.color.a / cast(float) 255 196 ]; 197 raylib.SetShaderValue(shader, light.colorLoc, &color, 198 raylib.ShaderUniformDataType.UNIFORM_VEC4); 199 } 200 } 201 202 /// represents a 3D light 203 class Light3D : Component, Renderable3D { 204 private SceneLightManager _mgr; 205 private enum phys_size = 0.2; 206 private SceneLightManager.ShaderLight _light; 207 208 /// the color of the light 209 public Color color; 210 211 /// whether the light is enabled 212 public bool light_enabled = true; 213 214 /// creates a new light with a given color 215 this(Color color = Colors.WHITE) { 216 this.color = color; 217 } 218 219 override void setup() { 220 // register the light in the manager 221 auto mgr = entity.scene.get_manager!SceneLightManager(); 222 assert(!mgr.isNull, "scene did not have SceneLightManager registered." 223 ~ "please add that to the scene before creating this component."); 224 _mgr = mgr.get; 225 _mgr.register(this); 226 } 227 228 override void destroy() { 229 _mgr.unregister(this); 230 } 231 232 @property BoundingBox bounds() { 233 auto size = Vector3(phys_size, phys_size, phys_size); 234 return BoundingBox(entity.position - size, entity.position + size); 235 } 236 237 void render() { 238 } 239 240 void debug_render() { 241 import re.ng.diag.render; 242 243 raylib.DrawSphereEx(entity.position, phys_size, 8, 8, color); 244 raylib.DrawSphereWires(entity.position, phys_size * 1.5, 2, 2, DebugRender.debug_color); 245 } 246 }