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