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