diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua
index d981a171e..0950df1e2 100644
--- a/autogen/lua_definitions/constants.lua
+++ b/autogen/lua_definitions/constants.lua
@@ -3635,7 +3635,7 @@ HUD_DISPLAY_DEFAULT = HUD_DISPLAY_FLAG_LIVES | HUD_DISPLAY_FLAG_CO
--- | `HUD_DISPLAY_DEFAULT`
--- @type integer
-LE_MAX_LIGHTS = 512
+LE_MAX_LIGHTS = 1024
LE_MODE_AFFECT_ALL_SHADED_AND_COLORED = 0 --- @type LEMode
LE_MODE_AFFECT_ALL_SHADED = 1 --- @type LEMode
diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua
index cc10149fe..700781429 100644
--- a/autogen/lua_definitions/functions.lua
+++ b/autogen/lua_definitions/functions.lua
@@ -5103,6 +5103,12 @@ function le_set_ambient_color(r, g, b)
-- ...
end
+--- @param count integer
+--- Sets the max amount of lights that can affect a vertex
+function le_set_max_lights_per_vertex(count)
+ -- ...
+end
+
--- @param pos Vec3f
--- @param out Color
--- @param lightIntensityScalar number
diff --git a/docs/lua/functions-4.md b/docs/lua/functions-4.md
index 545c0d19f..18fa49891 100644
--- a/docs/lua/functions-4.md
+++ b/docs/lua/functions-4.md
@@ -147,6 +147,29 @@ Sets the lighting engine ambient color
+## [le_set_max_lights_per_vertex](#le_set_max_lights_per_vertex)
+
+### Description
+Sets the max amount of lights that can affect a vertex
+
+### Lua Example
+`le_set_max_lights_per_vertex(count)`
+
+### Parameters
+| Field | Type |
+| ----- | ---- |
+| count | `integer` |
+
+### Returns
+- None
+
+### C Prototype
+`void le_set_max_lights_per_vertex(u8 count);`
+
+[:arrow_up_small:](#)
+
+
+
## [le_calculate_lighting_color](#le_calculate_lighting_color)
### Description
diff --git a/docs/lua/functions.md b/docs/lua/functions.md
index ef8fb4125..6407c26ed 100644
--- a/docs/lua/functions.md
+++ b/docs/lua/functions.md
@@ -976,6 +976,7 @@
- [le_set_tone_mapping](functions-4.md#le_set_tone_mapping)
- [le_get_ambient_color](functions-4.md#le_get_ambient_color)
- [le_set_ambient_color](functions-4.md#le_set_ambient_color)
+ - [le_set_max_lights_per_vertex](functions-4.md#le_set_max_lights_per_vertex)
- [le_calculate_lighting_color](functions-4.md#le_calculate_lighting_color)
- [le_calculate_lighting_color_with_normal](functions-4.md#le_calculate_lighting_color_with_normal)
- [le_calculate_lighting_dir](functions-4.md#le_calculate_lighting_dir)
diff --git a/src/engine/lighting_engine.c b/src/engine/lighting_engine.c
deleted file mode 100644
index 7466a5d80..000000000
--- a/src/engine/lighting_engine.c
+++ /dev/null
@@ -1,382 +0,0 @@
-#include "lighting_engine.h"
-#include "math_util.h"
-
-struct LELight
-{
- f32 posX;
- f32 posY;
- f32 posZ;
- u8 colorR;
- u8 colorG;
- u8 colorB;
- f32 radius;
- f32 intensity;
- bool added;
- bool useSurfaceNormals;
-};
-
-Color gLEAmbientColor = { 127, 127, 127 };
-static struct LELight sLights[LE_MAX_LIGHTS] = { 0 };
-static enum LEMode sMode = LE_MODE_AFFECT_ALL_SHADED_AND_COLORED;
-static enum LEToneMapping sToneMapping = LE_TONE_MAPPING_WEIGHTED;
-static bool sEnabled = false;
-
-static inline void color_set(Color color, u8 r, u8 g, u8 b) {
- color[0] = r;
- color[1] = g;
- color[2] = b;
-}
-
-static inline void color_copy(Color dest, Color src) {
- dest[0] = src[0];
- dest[1] = src[1];
- dest[2] = src[2];
-}
-
-bool le_is_enabled(void) {
- // this is needed because we don't want to make vanilla darker,
- // and we don't want to set the ambient color to { 255, 255, 255 }
- // because then no one could see the effect of their lights
- return sEnabled;
-}
-
-void le_set_mode(enum LEMode mode) {
- sMode = mode;
-}
-
-enum LEMode le_get_mode(void) {
- return sMode;
-}
-
-void le_set_tone_mapping(enum LEToneMapping toneMapping) {
- sToneMapping = toneMapping;
-}
-
-void le_get_ambient_color(VEC_OUT Color out) {
- color_copy(out, gLEAmbientColor);
-}
-
-void le_set_ambient_color(u8 r, u8 g, u8 b) {
- color_set(gLEAmbientColor, r, g, b);
- sEnabled = true;
-}
-
-static inline void le_tone_map_total_weighted(Color out, Color inAmbient, Vec3f inColor, float weight) {
- out[0] = clamp((inAmbient[0] + inColor[0]) / weight, 0, 255);
- out[1] = clamp((inAmbient[1] + inColor[1]) / weight, 0, 255);
- out[2] = clamp((inAmbient[2] + inColor[2]) / weight, 0, 255);
-}
-
-static inline void le_tone_map_weighted(Color out, Color inAmbient, Vec3f inColor, float weight) {
- out[0] = clamp(inAmbient[0] + (inColor[0] / weight), 0, 255);
- out[1] = clamp(inAmbient[1] + (inColor[1] / weight), 0, 255);
- out[2] = clamp(inAmbient[2] + (inColor[2] / weight), 0, 255);
-}
-
-static inline void le_tone_map_clamp(Color out, Color inAmbient, Vec3f inColor) {
- out[0] = clamp(inAmbient[0] + inColor[0], 0, 255);
- out[1] = clamp(inAmbient[1] + inColor[1], 0, 255);
- out[2] = clamp(inAmbient[2] + inColor[2], 0, 255);
-}
-
-static inline void le_tone_map_reinhard(Color out, Color inAmbient, Vec3f inColor) {
- inColor[0] += inAmbient[0];
- inColor[1] += inAmbient[1];
- inColor[2] += inAmbient[2];
-
- out[0] = clamp((inColor[0] / (inColor[0] + 255.0f)) * 255.0f, 0, 255);
- out[1] = clamp((inColor[1] / (inColor[1] + 255.0f)) * 255.0f, 0, 255);
- out[2] = clamp((inColor[2] / (inColor[2] + 255.0f)) * 255.0f, 0, 255);
-}
-
-static inline void le_tone_map(Color out, Color inAmbient, Vec3f inColor, float weight) {
- switch (sToneMapping) {
- case LE_TONE_MAPPING_TOTAL_WEIGHTED: le_tone_map_total_weighted(out, inAmbient, inColor, weight); break;
- case LE_TONE_MAPPING_WEIGHTED: le_tone_map_weighted(out, inAmbient, inColor, weight); break;
- case LE_TONE_MAPPING_CLAMP: le_tone_map_clamp(out, inAmbient, inColor); break;
- case LE_TONE_MAPPING_REINHARD: le_tone_map_reinhard(out, inAmbient, inColor); break;
- }
-}
-
-static inline void le_calculate_light_contribution(struct LELight* light, Vec3f pos, Vec3f normal, f32 lightIntensityScalar, Vec3f out_color, f32* weight) {
- // skip 'inactive' lights
- if (light->intensity <= 0 || light->radius <= 0) { return; }
-
- // vector to light
- f32 diffX = light->posX - pos[0];
- f32 diffY = light->posY - pos[1];
- f32 diffZ = light->posZ - pos[2];
-
- // squared distance check
- f32 dist2 = (diffX * diffX) + (diffY * diffY) + (diffZ * diffZ);
- f32 radius2 = light->radius * light->radius;
- if (dist2 > radius2 || dist2 <= 0) { return; }
-
- // attenuation & intensity
- f32 att = 1.0f - (dist2 / radius2);
- f32 brightness = att * light->intensity * lightIntensityScalar;
-
- // normalize diff
- f32 invLen = 1.0f / sqrtf(dist2);
- diffX *= invLen;
- diffY *= invLen;
- diffZ *= invLen;
-
- if (light->useSurfaceNormals && normal) {
- // lambert term
- f32 nl = (normal[0] * diffX) + (normal[1] * diffY) + (normal[2] * diffZ);
- if (nl <= 0.0f) { return; }
-
- // modulate by normal
- brightness *= nl;
- }
-
- // accumulate
- out_color[0] += light->colorR * brightness;
- out_color[1] += light->colorG * brightness;
- out_color[2] += light->colorB * brightness;
- *weight += brightness;
-}
-
-void le_calculate_vertex_lighting(Vtx_t* v, Vec3f pos, Color out) {
- // clear color
- Vec3f color = { 0 };
-
- // accumulate lighting
- f32 weight = 1.0f;
- for (s16 i = 0; i < LE_MAX_LIGHTS; i++) {
- struct LELight* light = &sLights[i];
- if (!light->added) { continue; }
-
- le_calculate_light_contribution(light, pos, NULL, 1.0f, color, &weight);
- }
-
- // tone map and output
- Color vtxAmbient = {
- v->cn[0] * (gLEAmbientColor[0] / 255.0f),
- v->cn[1] * (gLEAmbientColor[1] / 255.0f),
- v->cn[2] * (gLEAmbientColor[2] / 255.0f),
- };
- le_tone_map(out, vtxAmbient, color, weight);
-}
-
-void le_calculate_lighting_color(Vec3f pos, Color out, f32 lightIntensityScalar) {
- // clear color
- Vec3f color = { 0 };
-
- // accumulate lighting
- f32 weight = 1.0f;
- for (s16 i = 0; i < LE_MAX_LIGHTS; i++) {
- struct LELight* light = &sLights[i];
- if (!light->added) { continue; }
-
- le_calculate_light_contribution(light, pos, NULL, lightIntensityScalar, color, &weight);
- }
-
- // tone map and output
- le_tone_map(out, gLEAmbientColor, color, weight);
-}
-
-void le_calculate_lighting_color_with_normal(Vec3f pos, Vec3f normal, Color out, f32 lightIntensityScalar) {
- // normalize normal
- if (normal) { vec3f_normalize(normal); }
-
- // clear color
- Vec3f color = { 0 };
-
- // accumulate lighting
- f32 weight = 1.0f;
- for (s16 i = 0; i < LE_MAX_LIGHTS; i++) {
- struct LELight* light = &sLights[i];
- if (!light->added) { continue; }
-
- le_calculate_light_contribution(light, pos, normal, lightIntensityScalar, color, &weight);
- }
-
- // tone map and output
- le_tone_map(out, gLEAmbientColor, color, weight);
-}
-
-void le_calculate_lighting_dir(Vec3f pos, Vec3f out) {
- Vec3f lightingDir = { 0, 0, 0 };
- s16 count = 1;
-
- for (s16 i = 0; i < LE_MAX_LIGHTS; i++) {
- struct LELight* light = &sLights[i];
- if (!light->added) { continue; }
-
- f32 diffX = light->posX - pos[0];
- f32 diffY = light->posY - pos[1];
- f32 diffZ = light->posZ - pos[2];
- f32 dist = (diffX * diffX) + (diffY * diffY) + (diffZ * diffZ);
- f32 radius = light->radius * light->radius;
- if (dist > radius) { continue; }
-
- Vec3f dir = {
- pos[0] - light->posX,
- pos[1] - light->posY,
- pos[2] - light->posZ,
- };
- vec3f_normalize(dir);
-
- f32 intensity = (1 - (dist / radius)) * light->intensity;
- lightingDir[0] += dir[0] * intensity;
- lightingDir[1] += dir[1] * intensity;
- lightingDir[2] += dir[2] * intensity;
-
- count++;
- }
-
- out[0] = lightingDir[0] / (f32)(count);
- out[1] = lightingDir[1] / (f32)(count);
- out[2] = lightingDir[2] / (f32)(count);
- vec3f_normalize(out);
-}
-
-s16 le_add_light(f32 x, f32 y, f32 z, u8 r, u8 g, u8 b, f32 radius, f32 intensity) {
- struct LELight* newLight = NULL;
- s16 lightID = -1;
-
- for (s16 i = 0; i < LE_MAX_LIGHTS; i++) {
- struct LELight* light = &sLights[i];
- if (!light->added) {
- newLight = light;
- lightID = i;
- break;
- }
- }
- if (newLight == NULL) { return -1; }
-
- newLight->posX = x;
- newLight->posY = y;
- newLight->posZ = z;
- newLight->colorR = r;
- newLight->colorG = g;
- newLight->colorB = b;
- newLight->radius = radius;
- newLight->intensity = intensity;
- newLight->added = true;
- newLight->useSurfaceNormals = true;
-
- sEnabled = true;
- return lightID;
-}
-
-void le_remove_light(s16 id) {
- if (id < 0 || id >= LE_MAX_LIGHTS) { return; }
-
- memset(&sLights[id], 0, sizeof(struct LELight));
-}
-
-s16 le_get_light_count(void) {
- s16 count = 0;
- for (s16 i = 0; i < LE_MAX_LIGHTS; i++) {
- if (sLights[i].added) { count++; }
- }
-
- return count;
-}
-
-bool le_light_exists(s16 id) {
- if (id < 0 || id >= LE_MAX_LIGHTS) { return false; }
- return sLights[id].added;
-}
-
-void le_get_light_pos(s16 id, VEC_OUT Vec3f out) {
- if (id < 0 || id >= LE_MAX_LIGHTS) { return; }
-
- struct LELight* light = &sLights[id];
- if (!light->added) { return; }
- vec3f_set(out, light->posX, light->posY, light->posZ);
-}
-
-void le_set_light_pos(s16 id, f32 x, f32 y, f32 z) {
- if (id < 0 || id >= LE_MAX_LIGHTS) { return; }
-
- struct LELight* light = &sLights[id];
- if (!light->added) { return; }
- light->posX = x;
- light->posY = y;
- light->posZ = z;
-}
-
-void le_get_light_color(s16 id, VEC_OUT Color out) {
- if (id < 0 || id >= LE_MAX_LIGHTS) { return; }
-
- struct LELight* light = &sLights[id];
- if (!light->added) { return; }
- color_set(out, light->colorR, light->colorG, light->colorB);
-}
-
-void le_set_light_color(s16 id, u8 r, u8 g, u8 b) {
- if (id < 0 || id >= LE_MAX_LIGHTS) { return; }
-
- struct LELight* light = &sLights[id];
- if (!light->added) { return; }
- light->colorR = r;
- light->colorG = g;
- light->colorB = b;
-}
-
-f32 le_get_light_radius(s16 id) {
- if (id < 0 || id >= LE_MAX_LIGHTS) { return 0.0f; }
-
- struct LELight* light = &sLights[id];
- if (!light->added) { return 0.0f; }
- return light->radius;
-}
-
-void le_set_light_radius(s16 id, f32 radius) {
- if (id < 0 || id >= LE_MAX_LIGHTS) { return; }
-
- struct LELight* light = &sLights[id];
- if (!light->added) { return; }
- light->radius = radius;
-}
-
-f32 le_get_light_intensity(s16 id) {
- if (id < 0 || id >= LE_MAX_LIGHTS) { return 0.0f; }
-
- struct LELight* light = &sLights[id];
- if (!light->added) { return 0.0f; }
- return light->intensity;
-}
-
-void le_set_light_intensity(s16 id, f32 intensity) {
- if (id < 0 || id >= LE_MAX_LIGHTS) { return; }
-
- struct LELight* light = &sLights[id];
- if (!light->added) { return; }
- light->intensity = intensity;
-}
-
-bool le_get_light_use_surface_normals(s16 id) {
- if (id < 0 || id >= LE_MAX_LIGHTS) { return false; }
-
- struct LELight* light = &sLights[id];
- if (!light->added) { return false; }
- return light->useSurfaceNormals;
-}
-
-void le_set_light_use_surface_normals(s16 id, bool useSurfaceNormals) {
- if (id < 0 || id >= LE_MAX_LIGHTS) { return; }
-
- struct LELight* light = &sLights[id];
- if (!light->added) { return; }
- light->useSurfaceNormals = useSurfaceNormals;
-}
-
-void le_clear(void) {
- memset(&sLights, 0, sizeof(struct LELight) * LE_MAX_LIGHTS);
-
- gLEAmbientColor[0] = 127;
- gLEAmbientColor[1] = 127;
- gLEAmbientColor[2] = 127;
-}
-
-void le_shutdown(void) {
- sEnabled = false;
- sMode = LE_MODE_AFFECT_ALL_SHADED_AND_COLORED;
- sToneMapping = LE_TONE_MAPPING_WEIGHTED;
- le_clear();
-}
diff --git a/src/engine/lighting_engine.cpp b/src/engine/lighting_engine.cpp
new file mode 100644
index 000000000..0a12b55d5
--- /dev/null
+++ b/src/engine/lighting_engine.cpp
@@ -0,0 +1,444 @@
+#include "lighting_engine.h"
+extern "C" {
+#include "math_util.h"
+#include "pc/lua/smlua.h"
+}
+#undef clamp
+#undef min
+#undef max
+#include
+#include
+#ifdef __SSE__
+#include
+#endif
+
+#define C_FIELD extern "C"
+
+struct LELight
+{
+ s16 id;
+ Vec3f pos;
+ Color color;
+ f32 radius;
+ f32 intensity;
+ bool useSurfaceNormals;
+};
+
+Color gLEAmbientColor = { 127, 127, 127 };
+static std::vector sLightPool;
+static std::vector sActiveLights;
+static s16 sLightID = -1;
+static enum LEMode sMode = LE_MODE_AFFECT_ALL_SHADED_AND_COLORED;
+static enum LEToneMapping sToneMapping = LE_TONE_MAPPING_WEIGHTED;
+static bool sEnabled = false;
+static u8 sMaxLightsPerVertex = 4;
+
+static inline f32 rsqrt(f32 value) {
+#ifdef __SSE__
+ return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(value)));
+#else
+ return 1.0f / sqrtf(value);
+#endif
+}
+
+static inline void color_set(Color color, u8 r, u8 g, u8 b) {
+ color[0] = r;
+ color[1] = g;
+ color[2] = b;
+}
+
+static inline void color_copy(Color dest, Color src) {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+}
+
+static inline u8 clamp_u8(f32 value) {
+ s32 v = (s32)value;
+ v = v < 0 ? 0 : v;
+ v = v > 255 ? 255 : v;
+ return (u8)v;
+}
+
+C_FIELD bool le_is_enabled(void) {
+ // this is needed because we don't want to make vanilla darker,
+ // and we don't want to set the ambient color to { 255, 255, 255 }
+ // because then no one could see the effect of their lights
+ return sEnabled;
+}
+
+C_FIELD void le_set_mode(enum LEMode mode) {
+ sMode = mode;
+}
+
+C_FIELD enum LEMode le_get_mode(void) {
+ return sMode;
+}
+
+C_FIELD void le_set_tone_mapping(enum LEToneMapping toneMapping) {
+ sToneMapping = toneMapping;
+}
+
+C_FIELD void le_get_ambient_color(VEC_OUT Color out) {
+ color_copy(out, gLEAmbientColor);
+}
+
+C_FIELD void le_set_ambient_color(u8 r, u8 g, u8 b) {
+ color_set(gLEAmbientColor, r, g, b);
+ sEnabled = true;
+}
+
+C_FIELD void le_set_max_lights_per_vertex(u8 count) {
+ sMaxLightsPerVertex = count;
+}
+
+static inline void le_tone_map_total_weighted(Color out, Color inAmbient, Vec3f inColor, f32 weight) {
+ out[0] = clamp_u8((inAmbient[0] + inColor[0]) / weight);
+ out[1] = clamp_u8((inAmbient[1] + inColor[1]) / weight);
+ out[2] = clamp_u8((inAmbient[2] + inColor[2]) / weight);
+}
+
+static inline void le_tone_map_weighted(Color out, Color inAmbient, Vec3f inColor, f32 weight) {
+ out[0] = clamp_u8(inAmbient[0] + (inColor[0] / weight));
+ out[1] = clamp_u8(inAmbient[1] + (inColor[1] / weight));
+ out[2] = clamp_u8(inAmbient[2] + (inColor[2] / weight));
+}
+
+static inline void le_tone_map_clamp(Color out, Color inAmbient, Vec3f inColor) {
+ out[0] = clamp_u8(inAmbient[0] + inColor[0]);
+ out[1] = clamp_u8(inAmbient[1] + inColor[1]);
+ out[2] = clamp_u8(inAmbient[2] + inColor[2]);
+}
+
+static inline void le_tone_map_reinhard(Color out, Color inAmbient, Vec3f inColor) {
+ inColor[0] += inAmbient[0];
+ inColor[1] += inAmbient[1];
+ inColor[2] += inAmbient[2];
+
+ out[0] = clamp_u8((inColor[0] / (inColor[0] + 255.0f)) * 255.0f);
+ out[1] = clamp_u8((inColor[1] / (inColor[1] + 255.0f)) * 255.0f);
+ out[2] = clamp_u8((inColor[2] / (inColor[2] + 255.0f)) * 255.0f);
+}
+
+static void le_tone_map(Color out, Color inAmbient, Vec3f inColor, f32 weight) {
+ switch (sToneMapping) {
+ case LE_TONE_MAPPING_TOTAL_WEIGHTED: le_tone_map_total_weighted(out, inAmbient, inColor, weight); break;
+ case LE_TONE_MAPPING_WEIGHTED: le_tone_map_weighted(out, inAmbient, inColor, weight); break;
+ case LE_TONE_MAPPING_CLAMP: le_tone_map_clamp(out, inAmbient, inColor); break;
+ case LE_TONE_MAPPING_REINHARD: le_tone_map_reinhard(out, inAmbient, inColor); break;
+ }
+}
+
+static void le_update_active_lights() {
+ sActiveLights.clear();
+ for (auto& light : sLightPool) {
+ if (light.intensity > 0.0f && light.radius > 0.0f) {
+ sActiveLights.push_back(&light);
+ }
+ }
+}
+
+static inline OPTIMIZE_O3 void le_calculate_light_contribution(const LELight& light, Vec3f pos, Vec3f normal, f32 lightIntensityScalar, Vec3f outColor, f32& weight, u8& contribution) {
+ // vector to light
+ f32 diffX = light.pos[0] - pos[0];
+ f32 diffY = light.pos[1] - pos[1];
+ f32 diffZ = light.pos[2] - pos[2];
+
+ // squared distance check
+ f32 dist2 = (diffX * diffX) + (diffY * diffY) + (diffZ * diffZ);
+ f32 radius2 = light.radius * light.radius;
+ if (dist2 > radius2 || dist2 <= 0) { return; }
+
+ // attenuation & intensity
+ f32 att = 1.0f - (dist2 / radius2);
+ f32 brightness = att * light.intensity * lightIntensityScalar;
+
+ if (light.useSurfaceNormals && normal) {
+ // normalize diff
+ f32 invLen = rsqrt(dist2);
+ diffX *= invLen;
+ diffY *= invLen;
+ diffZ *= invLen;
+
+ // lambert term
+ f32 nl = (normal[0] * diffX) + (normal[1] * diffY) + (normal[2] * diffZ);
+ if (nl <= 0.0f) { return; }
+
+ // modulate by normal
+ brightness *= nl;
+ }
+
+ // accumulate
+ outColor[0] += light.color[0] * brightness;
+ outColor[1] += light.color[1] * brightness;
+ outColor[2] += light.color[2] * brightness;
+ weight += brightness;
+ contribution++;
+}
+
+C_FIELD OPTIMIZE_O3 void le_calculate_vertex_lighting(const Vtx_t* v, Vec3f pos, VEC_OUT Color out) {
+ // clear color
+ Vec3f color = { 0 };
+
+ // accumulate lighting
+ f32 weight = 1.0f;
+ u8 contribution = 0;
+ for (LELight* light : sActiveLights) {
+ le_calculate_light_contribution(*light, pos, NULL, 1.0f, color, weight, contribution);
+ if (contribution == sMaxLightsPerVertex) { break; }
+ }
+
+ // tone map and output
+ Color vtxAmbient = {
+ (u8)(v->cn[0] * (gLEAmbientColor[0] / 255.0f)),
+ (u8)(v->cn[1] * (gLEAmbientColor[1] / 255.0f)),
+ (u8)(v->cn[2] * (gLEAmbientColor[2] / 255.0f)),
+ };
+ le_tone_map(out, vtxAmbient, color, weight);
+}
+
+C_FIELD OPTIMIZE_O3 void le_calculate_lighting_color(Vec3f pos, VEC_OUT Color out, f32 lightIntensityScalar) {
+ // clear color
+ Vec3f color = { 0 };
+
+ // accumulate lighting
+ f32 weight = 1.0f;
+ u8 contribution = 0;
+ for (LELight* light : sActiveLights) {
+ le_calculate_light_contribution(*light, pos, NULL, lightIntensityScalar, color, weight, contribution);
+ if (contribution == sMaxLightsPerVertex) { break; }
+ }
+
+ // tone map and output
+ le_tone_map(out, gLEAmbientColor, color, weight);
+}
+
+C_FIELD OPTIMIZE_O3 void le_calculate_lighting_color_with_normal(Vec3f pos, Vec3f normal, VEC_OUT Color out, f32 lightIntensityScalar) {
+ // normalize normal
+ if (normal) { vec3f_normalize(normal); }
+
+ // clear color
+ Vec3f color = { 0 };
+
+ // accumulate lighting
+ f32 weight = 1.0f;
+ u8 contribution = 0;
+ for (LELight* light : sActiveLights) {
+ le_calculate_light_contribution(*light, pos, normal, lightIntensityScalar, color, weight, contribution);
+ if (contribution == sMaxLightsPerVertex) { break; }
+ }
+
+ // tone map and output
+ le_tone_map(out, gLEAmbientColor, color, weight);
+}
+
+C_FIELD void le_calculate_lighting_dir(Vec3f pos, VEC_OUT Vec3f out) {
+ Vec3f lightingDir = { 0, 0, 0 };
+ s16 count = 1;
+
+ for (LELight* light : sActiveLights) {
+ f32 diffX = light->pos[0] - pos[0];
+ f32 diffY = light->pos[1] - pos[1];
+ f32 diffZ = light->pos[2] - pos[2];
+ f32 dist = (diffX * diffX) + (diffY * diffY) + (diffZ * diffZ);
+ f32 radius = light->radius * light->radius;
+ if (dist > radius) { continue; }
+
+ Vec3f dir = {
+ pos[0] - light->pos[0],
+ pos[1] - light->pos[1],
+ pos[2] - light->pos[2],
+ };
+ vec3f_normalize(dir);
+
+ f32 intensity = (1 - (dist / radius)) * light->intensity;
+ lightingDir[0] += dir[0] * intensity;
+ lightingDir[1] += dir[1] * intensity;
+ lightingDir[2] += dir[2] * intensity;
+
+ count++;
+ }
+
+ out[0] = lightingDir[0] / (f32)(count);
+ out[1] = lightingDir[1] / (f32)(count);
+ out[2] = lightingDir[2] / (f32)(count);
+ vec3f_normalize(out);
+}
+
+C_FIELD s16 le_add_light(f32 x, f32 y, f32 z, u8 r, u8 g, u8 b, f32 radius, f32 intensity) {
+ if (sLightPool.size() >= LE_MAX_LIGHTS) {
+ LOG_LUA_LINE("LE light count cannot exceed %d lights!", LE_MAX_LIGHTS);
+ return -1;
+ }
+
+ LELight newLight;
+ newLight.id = ++sLightID;
+ newLight.pos[0] = x;
+ newLight.pos[1] = y;
+ newLight.pos[2] = z;
+ newLight.color[0] = r;
+ newLight.color[1] = g;
+ newLight.color[2] = b;
+ newLight.radius = radius;
+ newLight.intensity = intensity;
+ newLight.useSurfaceNormals = true;
+
+ sLightPool.push_back(newLight);
+
+ le_update_active_lights();
+
+ sEnabled = true;
+ return sLightID;
+}
+
+C_FIELD void le_remove_light(s16 id) {
+ if (id < 0) { return; }
+
+ auto it = std::find_if(sLightPool.begin(), sLightPool.end(),
+ [id](const LELight& light) {
+ return light.id == id;
+ }
+ );
+
+ if (it != sLightPool.end()) {
+ sLightPool.erase(it);
+ }
+
+ le_update_active_lights();
+}
+
+C_FIELD s16 le_get_light_count(void) {
+ return sLightPool.size();
+}
+
+C_FIELD bool le_light_exists(s16 id) {
+ if (id < 0) { return false; }
+
+ return std::any_of(sLightPool.begin(), sLightPool.end(),
+ [id](const LELight& light) {
+ return light.id == id;
+ }
+ );
+}
+
+static LELight* le_find_light(s16 id) {
+ if (id < 0) { return nullptr; }
+
+ auto it = std::find_if(sLightPool.begin(), sLightPool.end(),
+ [id](const LELight& light) {
+ return light.id == id;
+ }
+ );
+
+ // kinda cursed syntax but it works
+ return (it != sLightPool.end()) ? &(*it) : nullptr;
+}
+
+C_FIELD void le_get_light_pos(s16 id, VEC_OUT Vec3f out) {
+ if (id < 0) { return; }
+
+ if (auto* light = le_find_light(id)) {
+ vec3f_set(out, light->pos[0], light->pos[1], light->pos[2]);
+ }
+}
+
+C_FIELD void le_set_light_pos(s16 id, f32 x, f32 y, f32 z) {
+ if (id < 0) { return; }
+
+ if (auto* light = le_find_light(id)) {
+ light->pos[0] = x;
+ light->pos[1] = y;
+ light->pos[2] = z;
+ }
+}
+
+C_FIELD void le_get_light_color(s16 id, VEC_OUT Color out) {
+ if (id < 0) { return; }
+
+ if (auto* light = le_find_light(id)) {
+ color_set(out, light->color[0], light->color[1], light->color[2]);
+ }
+}
+
+C_FIELD void le_set_light_color(s16 id, u8 r, u8 g, u8 b) {
+ if (id < 0) { return; }
+
+ if (auto* light = le_find_light(id)) {
+ light->color[0] = r;
+ light->color[1] = g;
+ light->color[2] = b;
+ }
+}
+
+C_FIELD f32 le_get_light_radius(s16 id) {
+ if (id < 0) { return 0.0f; }
+
+ if (auto* light = le_find_light(id)) {
+ return light->radius;
+ }
+
+ return 0.0f;
+}
+
+C_FIELD void le_set_light_radius(s16 id, f32 radius) {
+ if (id < 0) { return; }
+
+ if (auto* light = le_find_light(id)) {
+ light->radius = radius;
+ }
+
+ le_update_active_lights();
+}
+
+C_FIELD f32 le_get_light_intensity(s16 id) {
+ if (id < 0) { return 0.0f; }
+
+ if (auto* light = le_find_light(id)) {
+ return light->intensity;
+ }
+
+ return 0.0f;
+}
+
+C_FIELD void le_set_light_intensity(s16 id, f32 intensity) {
+ if (id < 0) { return; }
+
+ if (auto* light = le_find_light(id)) {
+ light->intensity = intensity;
+ }
+
+ le_update_active_lights();
+}
+
+C_FIELD bool le_get_light_use_surface_normals(s16 id) {
+ if (id < 0) { return false; }
+
+ if (auto* light = le_find_light(id)) {
+ return light->useSurfaceNormals;
+ }
+
+ return false;
+}
+
+C_FIELD void le_set_light_use_surface_normals(s16 id, bool useSurfaceNormals) {
+ if (id < 0) { return; }
+
+ if (auto* light = le_find_light(id)) {
+ light->useSurfaceNormals = useSurfaceNormals;
+ }
+}
+
+void le_clear(void) {
+ sLightPool.clear();
+ sLightID = -1;
+
+ color_set(gLEAmbientColor, 127, 127, 127);
+}
+
+void le_shutdown(void) {
+ sEnabled = false;
+ sMode = LE_MODE_AFFECT_ALL_SHADED_AND_COLORED;
+ sToneMapping = LE_TONE_MAPPING_WEIGHTED;
+ sMaxLightsPerVertex = 4;
+ le_clear();
+}
diff --git a/src/engine/lighting_engine.h b/src/engine/lighting_engine.h
index 669e31d63..cc0d4d725 100644
--- a/src/engine/lighting_engine.h
+++ b/src/engine/lighting_engine.h
@@ -1,9 +1,12 @@
#ifndef LIGHTING_ENGINE_H
#define LIGHTING_ENGINE_H
+#ifdef __cplusplus
+extern "C" {
+#endif
#include "types.h"
-#define LE_MAX_LIGHTS 512
+#define LE_MAX_LIGHTS 1024
enum LEMode {
LE_MODE_AFFECT_ALL_SHADED_AND_COLORED,
@@ -32,8 +35,10 @@ void le_set_tone_mapping(enum LEToneMapping toneMapping);
void le_get_ambient_color(VEC_OUT Color out);
/* |description|Sets the lighting engine ambient color|descriptionEnd| */
void le_set_ambient_color(u8 r, u8 g, u8 b);
+/* |description|Sets the max amount of lights that can affect a vertex|descriptionEnd| */
+void le_set_max_lights_per_vertex(u8 count);
-void le_calculate_vertex_lighting(Vtx_t* v, Vec3f pos, VEC_OUT Color out);
+void le_calculate_vertex_lighting(const Vtx_t* v, Vec3f pos, VEC_OUT Color out);
/* |description|Calculates the lighting with `lightIntensityScalar` at a position and outputs the color in `out`|descriptionEnd|*/
void le_calculate_lighting_color(Vec3f pos, VEC_OUT Color out, f32 lightIntensityScalar);
/* |description|Calculates the lighting with `lightIntensityScalar` at a position and with a normal and outputs the color in `out`|descriptionEnd|*/
@@ -71,4 +76,8 @@ void le_set_light_use_surface_normals(s16 id, bool useSurfaceNormals);
void le_clear(void);
void le_shutdown(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif // LIGHTING_ENGINE_H
diff --git a/src/pc/gfx/gfx_pc.c b/src/pc/gfx/gfx_pc.c
index a89bcac74..1184eaba1 100644
--- a/src/pc/gfx/gfx_pc.c
+++ b/src/pc/gfx/gfx_pc.c
@@ -892,13 +892,14 @@ static void OPTIMIZE_O3 gfx_sp_vertex(size_t n_vertices, size_t dest_index, cons
}
// if lighting engine is enabled and either we want to affect all shaded surfaces or the lighting engine geometry mode is on
- if (le_is_enabled() && ((le_get_mode() != LE_MODE_AFFECT_ONLY_GEOMETRY_MODE) || (rsp.geometry_mode & G_LIGHTING_ENGINE_EXT))) {
+ if (le_is_enabled() && luaVertexColor && ((le_get_mode() != LE_MODE_AFFECT_ONLY_GEOMETRY_MODE) || (rsp.geometry_mode & G_LIGHTING_ENGINE_EXT))) {
Color color = { gLEAmbientColor[0], gLEAmbientColor[1], gLEAmbientColor[2] };
- CTX_BEGIN(CTX_LIGHTING);
Vec3f vpos = { v->ob[0], v->ob[1], v->ob[2] };
Vec3f vnormal = { nx, ny, nz };
+ CTX_BEGIN(CTX_LIGHTING);
+
// transform vpos and vnormal to world space
gfx_local_to_world_space(vpos, vnormal);
@@ -913,10 +914,11 @@ static void OPTIMIZE_O3 gfx_sp_vertex(size_t n_vertices, size_t dest_index, cons
// if lighting engine is enabled and we should affect all vertex colored surfaces or the lighting engine geometry mode is on
} else if (le_is_enabled() && !(rsp.geometry_mode & G_LIGHT_MAP_EXT) && (affectAllVertexColored || (rsp.geometry_mode & G_LIGHTING_ENGINE_EXT))) {
Color color = { gLEAmbientColor[0], gLEAmbientColor[1], gLEAmbientColor[2] };
- CTX_BEGIN(CTX_LIGHTING);
Vec3f vpos = { v->ob[0], v->ob[1], v->ob[2] };
+ CTX_BEGIN(CTX_LIGHTING);
+
// transform vpos to world space
gfx_local_to_world_space(vpos, NULL);
@@ -927,7 +929,7 @@ static void OPTIMIZE_O3 gfx_sp_vertex(size_t n_vertices, size_t dest_index, cons
if (affectAllVertexColored && !(rsp.geometry_mode & G_LIGHTING_ENGINE_EXT)) {
le_calculate_lighting_color(vpos, color, 1.0f);
} else {
- le_calculate_vertex_lighting((Vtx_t*)v, vpos, color);
+ le_calculate_vertex_lighting(v, vpos, color);
}
CTX_END(CTX_LIGHTING);
diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c
index 129a8f876..6936315ef 100644
--- a/src/pc/lua/smlua_constants_autogen.c
+++ b/src/pc/lua/smlua_constants_autogen.c
@@ -1794,7 +1794,7 @@ char gSmluaConstants[] = ""
"HUD_DISPLAY_FLAG_EMPHASIZE_POWER=0x8000\n"
"HUD_DISPLAY_NONE=0x0000\n"
"HUD_DISPLAY_DEFAULT=HUD_DISPLAY_FLAG_LIVES | HUD_DISPLAY_FLAG_COIN_COUNT | HUD_DISPLAY_FLAG_STAR_COUNT | HUD_DISPLAY_FLAG_CAMERA_AND_POWER | HUD_DISPLAY_FLAG_CAMERA | HUD_DISPLAY_FLAG_POWER | HUD_DISPLAY_FLAG_KEYS | HUD_DISPLAY_FLAG_UNKNOWN_0020\n"
-"LE_MAX_LIGHTS=512\n"
+"LE_MAX_LIGHTS=1024\n"
"LE_MODE_AFFECT_ALL_SHADED_AND_COLORED=0\n"
"LE_MODE_AFFECT_ALL_SHADED=1\n"
"LE_MODE_AFFECT_ONLY_GEOMETRY_MODE=2\n"
diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c
index 5ed2cd525..559c81c63 100644
--- a/src/pc/lua/smlua_functions_autogen.c
+++ b/src/pc/lua/smlua_functions_autogen.c
@@ -15509,6 +15509,23 @@ int smlua_func_le_set_ambient_color(lua_State* L) {
return 1;
}
+int smlua_func_le_set_max_lights_per_vertex(lua_State* L) {
+ if (L == NULL) { return 0; }
+
+ int top = lua_gettop(L);
+ if (top != 1) {
+ LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "le_set_max_lights_per_vertex", 1, top);
+ return 0;
+ }
+
+ u8 count = smlua_to_integer(L, 1);
+ if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "le_set_max_lights_per_vertex"); return 0; }
+
+ le_set_max_lights_per_vertex(count);
+
+ return 1;
+}
+
int smlua_func_le_calculate_lighting_color(lua_State* L) {
if (L == NULL) { return 0; }
@@ -37539,6 +37556,7 @@ void smlua_bind_functions_autogen(void) {
smlua_bind_function(L, "le_set_tone_mapping", smlua_func_le_set_tone_mapping);
smlua_bind_function(L, "le_get_ambient_color", smlua_func_le_get_ambient_color);
smlua_bind_function(L, "le_set_ambient_color", smlua_func_le_set_ambient_color);
+ smlua_bind_function(L, "le_set_max_lights_per_vertex", smlua_func_le_set_max_lights_per_vertex);
smlua_bind_function(L, "le_calculate_lighting_color", smlua_func_le_calculate_lighting_color);
smlua_bind_function(L, "le_calculate_lighting_color_with_normal", smlua_func_le_calculate_lighting_color_with_normal);
smlua_bind_function(L, "le_calculate_lighting_dir", smlua_func_le_calculate_lighting_dir);
diff --git a/src/pc/mods/mod_storage.h b/src/pc/mods/mod_storage.h
index a3762e3f7..ee41af4a4 100644
--- a/src/pc/mods/mod_storage.h
+++ b/src/pc/mods/mod_storage.h
@@ -1,11 +1,11 @@
#ifndef MOD_STORAGE_H
#define MOD_STORAGE_H
-#include
-
#ifdef __cplusplus
extern "C" {
#endif
+#include
+
#include "pc/lua/smlua_utils.h"
#define MAX_KEYS 4096