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