1 module re.gfx.lighting.scene_lights;
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 import rlights = re.gfx.lighting.rlights;
13 
14 /// acts as a manager for Light3D components
15 class SceneLightManager : Manager, Updatable {
16     alias max_lights = rlights.MAX_LIGHTS;
17     private Array!(rlights.Light) _lights;
18     private Array!Light3D _comps;
19     private int light_count;
20     public Shader shader;
21 
22     this() {
23         // load the shader
24         shader = Core.content.load_shader("shader/basic_lighting.vert",
25                 "shader/basic_lighting.frag");
26         // get some shader locations
27         shader.locs[raylib.ShaderLocationIndex.LOC_MATRIX_MODEL] = raylib.GetShaderLocation(shader,
28                 "matModel");
29         shader.locs[raylib.ShaderLocationIndex.LOC_VECTOR_VIEW] = raylib.GetShaderLocation(shader,
30                 "viewPos");
31 
32         // ambient light level
33         auto ambient_loc = raylib.GetShaderLocation(shader, "ambient");
34         auto col_ambient = 0.4;
35         float[4] ambient_val = [col_ambient, col_ambient, col_ambient, 1];
36         raylib.SetShaderValue(shader, ambient_loc, &ambient_val,
37                 raylib.ShaderUniformDataType.UNIFORM_VEC4);
38 
39         _lights.reserve(max_lights);
40         _comps.reserve(max_lights);
41     }
42 
43     override void update() {
44         // update camera view pos in light shader
45         float[3] camera_pos = [
46             (cast(Scene3D) scene).cam.transform.position.x, (cast(Scene3D) scene)
47             .cam.transform.position.y, (cast(Scene3D) scene).cam.transform.position.z
48         ];
49         raylib.SetShaderValue(shader, shader.locs[raylib.ShaderLocationIndex.LOC_VECTOR_VIEW],
50                 &camera_pos, raylib.ShaderUniformDataType.UNIFORM_VEC3);
51 
52         // update lights
53         for (int i = 0; i < light_count; i++) {
54             // sync fields
55             _lights[i].position = _comps[i].transform.position;
56             _lights[i].color = _comps[i].color;
57             _lights[i].enabled = _comps[i].light_enabled;
58 
59             // update shader values
60             rlights.UpdateLightValues(shader, _lights[i]);
61         }
62     }
63 
64     override void destroy() {
65         while (light_count > 0) {
66             unregister(_comps[0]);
67         }
68     }
69 
70     private void register(Light3D light_comp) {
71         assert(light_count < max_lights, "maximum light count exceeded.");
72         // add a light
73         _lights.insertBack(rlights.set_light(light_count, rlights.LightType.LIGHT_POINT,
74                 light_comp.transform.position, Vector3Zero, light_comp.color, shader));
75         _comps.insertBack(light_comp);
76         // set internal light reference
77         light_comp._light = _lights[light_count];
78         light_count++;
79     }
80 
81     private void unregister(Light3D light_comp) {
82         import std.range : dropExactly, takeOne;
83 
84         auto removed_index = cast(int) _comps[].countUntil(light_comp);
85         // clear all lights
86         for (int i = 0; i < light_count; i++) {
87             rlights.clear_light(i, shader);
88         }
89         _comps.linearRemove(_comps[].dropExactly(removed_index).takeOne);
90         _lights.linearRemove(_lights[].dropExactly(removed_index).takeOne);
91         light_count--; // we're removing a light
92         // ensure our lengths match
93         assert(_lights.length == light_count);
94         assert(_lights.length == _comps.length);
95         // reactivate the lights
96         for (int i = 0; i < light_count; i++) {
97             // update shader
98             _lights[i] = rlights.set_light(i, rlights.LightType.LIGHT_POINT,
99                     _comps[i].transform.position, Vector3Zero, _comps[i].color, shader);
100             // set associated light
101             _comps[i]._light = _lights[i];
102         }
103     }
104 }
105 
106 /// represents a 3D light
107 class Light3D : Component, Renderable3D {
108     private SceneLightManager _mgr;
109     private enum phys_size = 0.2;
110     private rlights.Light _light;
111 
112     /// the color of the light
113     public Color color;
114 
115     /// whether the light is enabled
116     public bool light_enabled = true;
117 
118     /// creates a new light with a given color
119     this(Color color = Colors.WHITE) {
120         this.color = color;
121     }
122 
123     override void setup() {
124         // register the light in the manager
125         auto mgr = entity.scene.get_manager!SceneLightManager();
126         assert(!mgr.isNull, "scene did not have SceneLightManager registered."
127                 ~ "please add that to the scene before creating this component.");
128         _mgr = mgr.get;
129         _mgr.register(this);
130     }
131 
132     override void destroy() {
133         _mgr.unregister(this);
134     }
135 
136     @property BoundingBox bounds() {
137         auto size = Vector3(phys_size, phys_size, phys_size);
138         return BoundingBox(entity.position - size, entity.position + size);
139     }
140 
141     void render() {
142     }
143 
144     void debug_render() {
145         import re.ng.diag.render;
146 
147         raylib.DrawSphereEx(entity.position, phys_size, 8, 8, color);
148         raylib.DrawSphereWires(entity.position, phys_size * 1.5, 2, 2, DebugRender.debug_color);
149     }
150 }