diff --git a/autogen/convert_constants.py b/autogen/convert_constants.py index 565cc4efb..db338d9fb 100644 --- a/autogen/convert_constants.py +++ b/autogen/convert_constants.py @@ -51,6 +51,7 @@ in_files = [ "src/game/player_palette.h", "src/pc/network/lag_compensation.h", "src/pc/djui/djui_panel_menu.h", + "src/engine/lighting_engine.h", "include/PR/gbi.h" ] diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 285ebe9fb..ceca13102 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -3608,6 +3608,24 @@ HUD_DISPLAY_DEFAULT = HUD_DISPLAY_FLAG_LIVES | HUD_DISPLAY_FLAG_CO --- | `HUD_DISPLAY_NONE` --- | `HUD_DISPLAY_DEFAULT` +LE_MODE_AFFECT_ALL_SHADED = 0 --- @type LEMode +LE_MODE_AFFECT_ONLY_GEOMETRY_MODE = 1 --- @type LEMode + +--- @alias LEMode +--- | `LE_MODE_AFFECT_ALL_SHADED` +--- | `LE_MODE_AFFECT_ONLY_GEOMETRY_MODE` + +LE_TONE_MAPPING_TOTAL_WEIGHTED = 0 --- @type LEToneMapping +LE_TONE_MAPPING_WEIGHTED = 1 --- @type LEToneMapping +LE_TONE_MAPPING_CLAMP = 2 --- @type LEToneMapping +LE_TONE_MAPPING_REINHARD = 3 --- @type LEToneMapping + +--- @alias LEToneMapping +--- | `LE_TONE_MAPPING_TOTAL_WEIGHTED` +--- | `LE_TONE_MAPPING_WEIGHTED` +--- | `LE_TONE_MAPPING_CLAMP` +--- | `LE_TONE_MAPPING_REINHARD` + MARIO_ANIM_SLOW_LEDGE_GRAB = 0 --- @type MarioAnimID MARIO_ANIM_FALL_OVER_BACKWARDS = 1 --- @type MarioAnimID MARIO_ANIM_BACKWARD_AIR_KB = 2 --- @type MarioAnimID diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index 72671d418..e9c5d54ff 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -5022,6 +5022,30 @@ function lvl_set_current_level(param, levelNum) -- ... end +--- @param mode LEMode +--- Sets the lighting engine mode to `mode` +function le_set_mode(mode) + -- ... +end + +--- @return LEMode +--- Gets the lighting engine mode +function le_get_mode() + -- ... +end + +--- @param toneMapping LEToneMapping +--- Sets the lighting engine's tone mapping mode to `toneMapping` +function le_set_tone_mapping(toneMapping) + -- ... +end + +--- @return boolean +--- Gets whether the lighting engine has been enabled or not. It becomes enabled once a light is added. +function le_is_enabled() + -- ... +end + --- @param pos Vec3f --- @param out Color --- @param lightIntensityScalar number @@ -5030,6 +5054,15 @@ function le_calculate_lighting_color(pos, out, lightIntensityScalar) -- ... end +--- @param pos Vec3f +--- @param normal Vec3f +--- @param out Color +--- @param lightIntensityScalar number +--- Calculates the lighting with `lightIntensityScalar` at a position and with a normal and outputs the color in `out` +function le_calculate_lighting_color_with_normal(pos, normal, out, lightIntensityScalar) + -- ... +end + --- @param pos Vec3f --- @param out Vec3f --- Calculates the lighting direction from a position and outputs the result in `out` @@ -5103,6 +5136,13 @@ function le_set_light_intensity(id, intensity) -- ... end +--- @param id integer +--- @param useSurfaceNormals boolean +--- Sets whether a lighting engine point light will use a surface's normals to determine its brightness with `useSurfaceNormals` +function le_set_light_use_surface_normals(id, useSurfaceNormals) + -- ... +end + --- @param m MarioState --- @return integer --- Checks if Mario's current animation has reached its final frame (i.e., the last valid frame in the animation). Useful for deciding when to transition out of an animation-driven action @@ -6589,11 +6629,19 @@ end --- @param dest Mat4 --- @param src Mat4 ---- Inverts the 4x4 floating-point matrix `src` and stores the inverse in `dest`. Applying the inverse transformation undoes whatever `src` did, returning points back to their original coordinate space +--- Inverts the 4x4 floating-point matrix `src` and stores the inverse in `dest`. Applying the inverse transformation undoes whatever `src` did, returning points back to their original coordinate space. The `src` matrix *must* be affine! function mtxf_inverse(dest, src) -- ... end +--- @param dest Mat4 +--- @param src Mat4 +--- @return boolean +--- Inverts the 4x4 floating-point matrix `src` and stores the inverse in `dest`. Applying the inverse transformation undoes whatever `src` did, returning points back to their original coordinate space. Returns `false` if the inversion failed. +function mtxf_inverse_non_affine(dest, src) + -- ... +end + --- @param dest Vec3f --- @param objMtx Mat4 --- @param camMtx Mat4 diff --git a/developer/compile.sh b/developer/compile.sh index 85af76929..1dc460bb8 100755 --- a/developer/compile.sh +++ b/developer/compile.sh @@ -13,4 +13,4 @@ if [ ! -f "$FILE" ]; then FILE=./build/us_pc/sm64coopdx fi -$FILE & +$FILE --console --server 7777 & diff --git a/docs/lua/constants.md b/docs/lua/constants.md index 5c3b1385e..0cdb90306 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -40,6 +40,9 @@ - [level_update.h](#level_updateh) - [enum MarioSpawnType](#enum-MarioSpawnType) - [enum HUDDisplayFlag](#enum-HUDDisplayFlag) +- [lighting_engine.h](#lighting_engineh) + - [enum LEMode](#enum-LEMode) + - [enum LEToneMapping](#enum-LEToneMapping) - [mario_animation_ids.h](#mario_animation_idsh) - [enum MarioAnimID](#enum-MarioAnimID) - [enum CharacterAnimID](#enum-CharacterAnimID) @@ -1619,6 +1622,26 @@
+## [lighting_engine.h](#lighting_engine.h) + +### [enum LEMode](#LEMode) +| Identifier | Value | +| :--------- | :---- | +| LE_MODE_AFFECT_ALL_SHADED | 0 | +| LE_MODE_AFFECT_ONLY_GEOMETRY_MODE | 1 | + +### [enum LEToneMapping](#LEToneMapping) +| Identifier | Value | +| :--------- | :---- | +| LE_TONE_MAPPING_TOTAL_WEIGHTED | 0 | +| LE_TONE_MAPPING_WEIGHTED | 1 | +| LE_TONE_MAPPING_CLAMP | 2 | +| LE_TONE_MAPPING_REINHARD | 3 | + +[:arrow_up_small:](#) + +
+ ## [mario_animation_ids.h](#mario_animation_ids.h) ### [enum MarioAnimID](#MarioAnimID) diff --git a/docs/lua/functions-3.md b/docs/lua/functions-3.md index b65f95308..789ab7b81 100644 --- a/docs/lua/functions-3.md +++ b/docs/lua/functions-3.md @@ -6633,6 +6633,94 @@ Sets the level number and handles the act select screen. `param` is used for ove
+## [le_set_mode](#le_set_mode) + +### Description +Sets the lighting engine mode to `mode` + +### Lua Example +`le_set_mode(mode)` + +### Parameters +| Field | Type | +| ----- | ---- | +| mode | [enum LEMode](constants.md#enum-LEMode) | + +### Returns +- None + +### C Prototype +`void le_set_mode(enum LEMode mode);` + +[:arrow_up_small:](#) + +
+ +## [le_get_mode](#le_get_mode) + +### Description +Gets the lighting engine mode + +### Lua Example +`local enumValue = le_get_mode()` + +### Parameters +- None + +### Returns +[enum LEMode](constants.md#enum-LEMode) + +### C Prototype +`enum LEMode le_get_mode(void);` + +[:arrow_up_small:](#) + +
+ +## [le_set_tone_mapping](#le_set_tone_mapping) + +### Description +Sets the lighting engine's tone mapping mode to `toneMapping` + +### Lua Example +`le_set_tone_mapping(toneMapping)` + +### Parameters +| Field | Type | +| ----- | ---- | +| toneMapping | [enum LEToneMapping](constants.md#enum-LEToneMapping) | + +### Returns +- None + +### C Prototype +`void le_set_tone_mapping(enum LEToneMapping toneMapping);` + +[:arrow_up_small:](#) + +
+ +## [le_is_enabled](#le_is_enabled) + +### Description +Gets whether the lighting engine has been enabled or not. It becomes enabled once a light is added. + +### Lua Example +`local booleanValue = le_is_enabled()` + +### Parameters +- None + +### Returns +- `boolean` + +### C Prototype +`bool le_is_enabled(void);` + +[:arrow_up_small:](#) + +
+ ## [le_calculate_lighting_color](#le_calculate_lighting_color) ### Description @@ -6658,6 +6746,32 @@ Calculates the lighting with `lightIntensityScalar` at a position and outputs th
+## [le_calculate_lighting_color_with_normal](#le_calculate_lighting_color_with_normal) + +### Description +Calculates the lighting with `lightIntensityScalar` at a position and with a normal and outputs the color in `out` + +### Lua Example +`le_calculate_lighting_color_with_normal(pos, normal, out, lightIntensityScalar)` + +### Parameters +| Field | Type | +| ----- | ---- | +| pos | [Vec3f](structs.md#Vec3f) | +| normal | [Vec3f](structs.md#Vec3f) | +| out | [Color](structs.md#Color) | +| lightIntensityScalar | `number` | + +### Returns +- None + +### C Prototype +`void le_calculate_lighting_color_with_normal(Vec3f pos, Vec3f normal, OUT Color out, f32 lightIntensityScalar);` + +[:arrow_up_small:](#) + +
+ ## [le_calculate_lighting_dir](#le_calculate_lighting_dir) ### Description @@ -6879,6 +6993,30 @@ Sets a lighting engine point light's `intensity` [:arrow_up_small:](#) +
+ +## [le_set_light_use_surface_normals](#le_set_light_use_surface_normals) + +### Description +Sets whether a lighting engine point light will use a surface's normals to determine its brightness with `useSurfaceNormals` + +### Lua Example +`le_set_light_use_surface_normals(id, useSurfaceNormals)` + +### Parameters +| Field | Type | +| ----- | ---- | +| id | `integer` | +| useSurfaceNormals | `boolean` | + +### Returns +- None + +### C Prototype +`void le_set_light_use_surface_normals(s32 id, bool useSurfaceNormals);` + +[:arrow_up_small:](#) +
--- diff --git a/docs/lua/functions-4.md b/docs/lua/functions-4.md index e21c1fa09..636e195d5 100644 --- a/docs/lua/functions-4.md +++ b/docs/lua/functions-4.md @@ -4803,7 +4803,7 @@ Rotates the matrix `mtx` in the XY plane by the given `angle`. Rotating in the X ## [mtxf_inverse](#mtxf_inverse) ### Description -Inverts the 4x4 floating-point matrix `src` and stores the inverse in `dest`. Applying the inverse transformation undoes whatever `src` did, returning points back to their original coordinate space +Inverts the 4x4 floating-point matrix `src` and stores the inverse in `dest`. Applying the inverse transformation undoes whatever `src` did, returning points back to their original coordinate space. The `src` matrix *must* be affine! ### Lua Example `mtxf_inverse(dest, src)` @@ -4824,6 +4824,30 @@ Inverts the 4x4 floating-point matrix `src` and stores the inverse in `dest`. Ap
+## [mtxf_inverse_non_affine](#mtxf_inverse_non_affine) + +### Description +Inverts the 4x4 floating-point matrix `src` and stores the inverse in `dest`. Applying the inverse transformation undoes whatever `src` did, returning points back to their original coordinate space. Returns `false` if the inversion failed. + +### Lua Example +`local booleanValue = mtxf_inverse_non_affine(dest, src)` + +### Parameters +| Field | Type | +| ----- | ---- | +| dest | [Mat4](structs.md#Mat4) | +| src | [Mat4](structs.md#Mat4) | + +### Returns +- `boolean` + +### C Prototype +`bool mtxf_inverse_non_affine(OUT Mat4 dest, Mat4 src);` + +[:arrow_up_small:](#) + +
+ ## [get_pos_from_transform_mtx](#get_pos_from_transform_mtx) ### Description diff --git a/docs/lua/functions.md b/docs/lua/functions.md index 3d4ded489..7ad8bc456 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -964,7 +964,12 @@
- lighting_engine.h + - [le_set_mode](functions-3.md#le_set_mode) + - [le_get_mode](functions-3.md#le_get_mode) + - [le_set_tone_mapping](functions-3.md#le_set_tone_mapping) + - [le_is_enabled](functions-3.md#le_is_enabled) - [le_calculate_lighting_color](functions-3.md#le_calculate_lighting_color) + - [le_calculate_lighting_color_with_normal](functions-3.md#le_calculate_lighting_color_with_normal) - [le_calculate_lighting_dir](functions-3.md#le_calculate_lighting_dir) - [le_add_light](functions-3.md#le_add_light) - [le_remove_light](functions-3.md#le_remove_light) @@ -974,6 +979,7 @@ - [le_set_light_color](functions-3.md#le_set_light_color) - [le_set_light_radius](functions-3.md#le_set_light_radius) - [le_set_light_intensity](functions-3.md#le_set_light_intensity) + - [le_set_light_use_surface_normals](functions-3.md#le_set_light_use_surface_normals)
@@ -1217,6 +1223,7 @@ - [mtxf_mul_vec3s](functions-4.md#mtxf_mul_vec3s) - [mtxf_rotate_xy](functions-4.md#mtxf_rotate_xy) - [mtxf_inverse](functions-4.md#mtxf_inverse) + - [mtxf_inverse_non_affine](functions-4.md#mtxf_inverse_non_affine) - [get_pos_from_transform_mtx](functions-4.md#get_pos_from_transform_mtx)
diff --git a/include/PR/gbi_extension.h b/include/PR/gbi_extension.h index 2382267e3..fb08a5a0d 100644 --- a/include/PR/gbi_extension.h +++ b/include/PR/gbi_extension.h @@ -19,6 +19,8 @@ #define G_TEXADDR_DJUI 0x13 #define G_EXECUTE_DJUI 0xdd +#define G_MTX_INVERSE_CAMERA_EXT 0x08 + #define gsSPTextureAddrDjui(c) \ {{ \ (_SHIFTL(G_TEXADDR_DJUI,24,8)|_SHIFTL(~(u32)(c),0,24)),(u32)(0) \ diff --git a/src/engine/lighting_engine.c b/src/engine/lighting_engine.c index 9c3af3f10..9f7d785f2 100644 --- a/src/engine/lighting_engine.c +++ b/src/engine/lighting_engine.c @@ -5,11 +5,13 @@ #include "data/dynos_cmap.cpp.h" #define LE_MAX_LIGHTS 256 -#define LE_TOTAL_WEIGHTED_LIGHTING -static Color sAmbientColor; +static Color sAmbientColor = { 127, 127, 127 }; static void* sLights = NULL; static s32 sLightID = 0; +static enum LEMode sMode = LE_MODE_AFFECT_ALL_SHADED; +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; @@ -17,82 +19,156 @@ static inline void color_set(Color color, u8 r, u8 g, u8 b) { color[2] = b; } -void le_calculate_vertex_lighting(Vtx_t* v, OUT Color out) { - if (sLights == NULL) { return; } +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; +} -#ifdef LE_TOTAL_WEIGHTED_LIGHTING - f32 r = v->cn[0] * (sAmbientColor[0] / 255.0f); - f32 g = v->cn[1] * (sAmbientColor[1] / 255.0f); - f32 b = v->cn[2] * (sAmbientColor[2] / 255.0f); -#else - f32 r = 0; - f32 g = 0; - f32 b = 0; -#endif - f32 weight = 1.0f; - for (struct LELight* light = hmap_begin(sLights); light != NULL; light = hmap_next(sLights)) { - f32 diffX = light->posX - v->ob[0]; - f32 diffY = light->posY - v->ob[1]; - f32 diffZ = light->posZ - v->ob[2]; - f32 dist = (diffX * diffX) + (diffY * diffY) + (diffZ * diffZ); - f32 radius = light->radius * light->radius; - if (dist > radius) { continue; } +void le_set_mode(enum LEMode mode) { + sMode = mode; +} - f32 brightness = (1 - (dist / radius)) * light->intensity; - r += light->colorR * brightness; - g += light->colorG * brightness; - b += light->colorB * brightness; - weight += brightness; +enum LEMode le_get_mode(void) { + return sMode; +} + +void le_set_tone_mapping(enum LEToneMapping toneMapping) { + sToneMapping = toneMapping; +} + +static inline void le_tone_map_total_weighted(OUT Color out, Color in_ambient, Vec3f in_color, float weight) { + out[0] = clamp((in_ambient[0] + in_color[0]) / weight, 0, 255); + out[1] = clamp((in_ambient[1] + in_color[1]) / weight, 0, 255); + out[2] = clamp((in_ambient[2] + in_color[2]) / weight, 0, 255); +} + +static inline void le_tone_map_weighted(OUT Color out, Color in_ambient, Vec3f in_color, float weight) { + out[0] = clamp(in_ambient[0] + (in_color[0] / weight), 0, 255); + out[1] = clamp(in_ambient[1] + (in_color[1] / weight), 0, 255); + out[2] = clamp(in_ambient[2] + (in_color[2] / weight), 0, 255); +} + +static inline void le_tone_map_clamp(OUT Color out, Color in_ambient, Vec3f in_color) { + out[0] = clamp(in_ambient[0] + in_color[0], 0, 255); + out[1] = clamp(in_ambient[1] + in_color[1], 0, 255); + out[2] = clamp(in_ambient[2] + in_color[2], 0, 255); +} + +static inline void le_tone_map_reinhard(OUT Color out, Color in_ambient, Vec3f in_color) { + in_color[0] += in_ambient[0]; + in_color[1] += in_ambient[1]; + in_color[2] += in_ambient[2]; + + out[0] = clamp((in_color[0] / (in_color[0] + 255.0f)) * 255.0f, 0, 255); + out[1] = clamp((in_color[1] / (in_color[1] + 255.0f)) * 255.0f, 0, 255); + out[2] = clamp((in_color[2] / (in_color[2] + 255.0f)) * 255.0f, 0, 255); +} + +static inline void le_tone_map(OUT Color out, Color in_ambient, Vec3f in_color, float weight) { + switch (sToneMapping) { + case LE_TONE_MAPPING_TOTAL_WEIGHTED: le_tone_map_total_weighted(out, in_ambient, in_color, weight); break; + case LE_TONE_MAPPING_WEIGHTED: le_tone_map_weighted(out, in_ambient, in_color, weight); break; + case LE_TONE_MAPPING_CLAMP: le_tone_map_clamp(out, in_ambient, in_color); break; + case LE_TONE_MAPPING_REINHARD: le_tone_map_reinhard(out, in_ambient, in_color); break; + } +} + +static inline void le_calculate_light_contribution(struct LELight* light, Vec3f pos, Vec3f normal, f32 lightIntensityScalar, OUT Vec3f out_color, OUT 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; } -#ifdef LE_TOTAL_WEIGHTED_LIGHTING - out[0] = min(r / weight, 255); - out[1] = min(g / weight, 255); - out[2] = min(b / weight, 255); -#else - out[0] = min((v->cn[0] * (sAmbientColor[0] / 255.0f)) + (r / weight), 255); - out[1] = min((v->cn[1] * (sAmbientColor[1] / 255.0f)) + (g / weight), 255); - out[2] = min((v->cn[2] * (sAmbientColor[2] / 255.0f)) + (b / weight), 255); -#endif + // 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, OUT Color out) { + if (sLights == NULL) { return; } + + // clear color + Vec3f color = { 0 }; + + // accumulate lighting + f32 weight = 1.0f; + for (struct LELight* light = hmap_begin(sLights); light != NULL; light = hmap_next(sLights)) { + le_calculate_light_contribution(light, pos, NULL, 1, color, &weight); + } + + // tone map and output + Color vtxAmbient = { + v->cn[0] * (sAmbientColor[0] / 255.0f), + v->cn[1] * (sAmbientColor[1] / 255.0f), + v->cn[2] * (sAmbientColor[2] / 255.0f), + }; + le_tone_map(out, vtxAmbient, color, weight); } void le_calculate_lighting_color(Vec3f pos, OUT Color out, f32 lightIntensityScalar) { if (sLights == NULL) { return; } -#ifdef LE_TOTAL_WEIGHTED_LIGHTING - f32 r = sAmbientColor[0]; - f32 g = sAmbientColor[1]; - f32 b = sAmbientColor[2]; -#else - f32 r = 0; - f32 g = 0; - f32 b = 0; -#endif + // clear color + Vec3f color = { 0 }; + + // accumulate lighting f32 weight = 1.0f; for (struct LELight* light = hmap_begin(sLights); light != NULL; light = hmap_next(sLights)) { - 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; } - - f32 brightness = (1 - (dist / radius)) * light->intensity * lightIntensityScalar; - r += light->colorR * brightness; - g += light->colorG * brightness; - b += light->colorB * brightness; - weight += brightness; + le_calculate_light_contribution(light, pos, NULL, lightIntensityScalar, color, &weight); } -#ifdef LE_TOTAL_WEIGHTED_LIGHTING - out[0] = min(r / weight, 255); - out[1] = min(g / weight, 255); - out[2] = min(b / weight, 255); -#else - out[0] = min(sAmbientColor[0] + (r / weight), 255); - out[1] = min(sAmbientColor[1] + (g / weight), 255); - out[2] = min(sAmbientColor[2] + (b / weight), 255); -#endif + // tone map and output + le_tone_map(out, sAmbientColor, color, weight); +} + +void le_calculate_lighting_color_with_normal(Vec3f pos, Vec3f normal, OUT Color out, f32 lightIntensityScalar) { + if (sLights == NULL) { return; } + + // normalize normal + if (normal) { vec3f_normalize(normal); } + + // clear color + Vec3f color = { 0 }; + + // accumulate lighting + f32 weight = 1.0f; + for (struct LELight* light = hmap_begin(sLights); light != NULL; light = hmap_next(sLights)) { + le_calculate_light_contribution(light, pos, normal, lightIntensityScalar, color, &weight); + } + + // tone map and output + le_tone_map(out, sAmbientColor, color, weight); } void le_calculate_lighting_dir(Vec3f pos, OUT Vec3f out) { @@ -145,7 +221,10 @@ s32 le_add_light(f32 x, f32 y, f32 z, u8 r, u8 g, u8 b, f32 radius, f32 intensit light->colorB = b; light->radius = radius; light->intensity = intensity; + light->useSurfaceNormals = true; hmap_put(sLights, ++sLightID, light); + + sEnabled = true; return sLightID; } @@ -163,6 +242,7 @@ s32 le_get_light_count(void) { void le_set_ambient_color(u8 r, u8 g, u8 b) { color_set(sAmbientColor, r, g, b); + sEnabled = true; } void le_set_light_pos(s32 id, f32 x, f32 y, f32 z) { @@ -201,6 +281,14 @@ void le_set_light_intensity(s32 id, f32 intensity) { light->intensity = intensity; } +void le_set_light_use_surface_normals(s32 id, bool useSurfaceNormals) { + if (sLights == NULL || id <= 0) { return; } + + struct LELight* light = hmap_get(sLights, id); + if (light == NULL) { return; } + light->useSurfaceNormals = useSurfaceNormals; +} + void le_clear(void) { if (sLights == NULL) { return; } @@ -209,14 +297,17 @@ void le_clear(void) { } hmap_clear(sLights); sLightID = 0; - sAmbientColor[0] = 0; - sAmbientColor[1] = 0; - sAmbientColor[2] = 0; + sAmbientColor[0] = 127; + sAmbientColor[1] = 127; + sAmbientColor[2] = 127; } void le_shutdown(void) { if (sLights == NULL) { return; } + sEnabled = false; + sMode = LE_MODE_AFFECT_ALL_SHADED; + sToneMapping = LE_TONE_MAPPING_WEIGHTED; le_clear(); hmap_destroy(sLights); sLights = NULL; diff --git a/src/engine/lighting_engine.h b/src/engine/lighting_engine.h index 72540cc31..e5cc842b1 100644 --- a/src/engine/lighting_engine.h +++ b/src/engine/lighting_engine.h @@ -13,11 +13,35 @@ struct LELight u8 colorB; f32 radius; f32 intensity; + bool useSurfaceNormals; }; -void le_calculate_vertex_lighting(Vtx_t* v, OUT Color out); +enum LEMode { + LE_MODE_AFFECT_ALL_SHADED, + LE_MODE_AFFECT_ONLY_GEOMETRY_MODE, +}; + +enum LEToneMapping { + LE_TONE_MAPPING_TOTAL_WEIGHTED, + LE_TONE_MAPPING_WEIGHTED, + LE_TONE_MAPPING_CLAMP, + LE_TONE_MAPPING_REINHARD, +}; + +/* |description|Sets the lighting engine mode to `mode`|descriptionEnd|*/ +void le_set_mode(enum LEMode mode); +/* |description|Gets the lighting engine mode|descriptionEnd|*/ +enum LEMode le_get_mode(void); +/* |description|Sets the lighting engine's tone mapping mode to `toneMapping`|descriptionEnd|*/ +void le_set_tone_mapping(enum LEToneMapping toneMapping); +/* |description|Gets whether the lighting engine has been enabled or not. It becomes enabled once a light is added.|descriptionEnd|*/ +bool le_is_enabled(void); + +void le_calculate_vertex_lighting(Vtx_t* v, Vec3f pos, 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, 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|*/ +void le_calculate_lighting_color_with_normal(Vec3f pos, Vec3f normal, OUT Color out, f32 lightIntensityScalar); /* |description|Calculates the lighting direction from a position and outputs the result in `out`|descriptionEnd| */ void le_calculate_lighting_dir(Vec3f pos, OUT Vec3f out); /* |description|Adds a lighting engine point light at `x`, `y`, `z` with color `r`, `g`, `b` and `radius` with `intensity`|descriptionEnd| */ @@ -36,6 +60,8 @@ void le_set_light_color(s32 id, u8 r, u8 g, u8 b); void le_set_light_radius(s32 id, f32 radius); /* |description|Sets a lighting engine point light's `intensity`|descriptionEnd| */ void le_set_light_intensity(s32 id, f32 intensity); +/* |description|Sets whether a lighting engine point light will use a surface's normals to determine its brightness with `useSurfaceNormals`|descriptionEnd| */ +void le_set_light_use_surface_normals(s32 id, bool useSurfaceNormals); void le_clear(void); void le_shutdown(void); diff --git a/src/engine/math_util.c b/src/engine/math_util.c index 0cb6231c6..fc3562799 100644 --- a/src/engine/math_util.c +++ b/src/engine/math_util.c @@ -781,6 +781,64 @@ OPTIMIZE_O3 void mtxf_inverse(OUT Mat4 dest, Mat4 src) { mtxf_copy(dest, buf); } +/** + * Compute the inverse of 'src' and put it into 'dest' but it can be a non-affine matrix. + * Obtains the inverse via Gauss-Jordan elimination. + */ +OPTIMIZE_O3 bool mtxf_inverse_non_affine(OUT Mat4 dest, Mat4 src) { + // augmented matrix [ src | I ] + f32 aug[4][8]; + for (s32 i = 0; i < 4; i++) { + for (s32 j = 0; j < 4; j++) { + aug[i][j] = src[i][j]; + aug[i][j + 4] = (i == j) ? 1.0f : 0.0f; + } + } + + // forward elimination + for (s32 k = 0; k < 4; k++) { + // find pivot row + s32 piv = k; + for (s32 i = k + 1; i < 4; i++) { + if (fabsf(aug[i][k]) > fabsf(aug[piv][k])) { piv = i; } + } + + if (fabsf(aug[piv][k]) < FLT_EPSILON) { return false; } + + // swap pivot row into place + if (piv != k) { + for (s32 j = 0; j < 8; j++) { + f32 tmp = aug[k][j]; + aug[k][j] = aug[piv][j]; + aug[piv][j] = tmp; + } + } + + // scale pivot row to make pivot = 1 + f32 inv_p = 1.0f / aug[k][k]; + for (s32 j = k; j < 8; j++) { aug[k][j] *= inv_p; } + + // eliminate below + for (s32 i = k+1; i < 4; i++) { + f32 f = aug[i][k]; + for (s32 j = k; j < 8; j++) { aug[i][j] -= f * aug[k][j]; } + } + } + + // backward substitution + for (s32 k = 3; k >= 0; k--) { + for (s32 i = 0; i < k; i++) { + f32 f = aug[i][k]; + for (s32 j = k; j < 8; j++) { aug[i][j] -= f * aug[k][j]; } + } + } + + // copy right half (the inverse) into dest + for (s32 i = 0; i < 4; i++) { memcpy(dest[i], &aug[i][4], 4 * sizeof(f32)); } + + return true; +} + /** * Extract a position given an object's transformation matrix and a camera matrix. * This is used for determining the world position of the held object: since objMtx @@ -800,3 +858,4 @@ OPTIMIZE_O3 Vec3fp get_pos_from_transform_mtx(OUT Vec3f dest, Mat4 objMtx, Mat4 return dest; } + diff --git a/src/engine/math_util.h b/src/engine/math_util.h index 838fa58ce..72ead474d 100644 --- a/src/engine/math_util.h +++ b/src/engine/math_util.h @@ -253,10 +253,15 @@ Rotates the matrix `mtx` in the XY plane by the given `angle`. Rotating in the X OPTIMIZE_O3 void mtxf_rotate_xy(OUT Mat4 mtx, s16 angle); /* |description| -Inverts the 4x4 floating-point matrix `src` and stores the inverse in `dest`. Applying the inverse transformation undoes whatever `src` did, returning points back to their original coordinate space +Inverts the 4x4 floating-point matrix `src` and stores the inverse in `dest`. Applying the inverse transformation undoes whatever `src` did, returning points back to their original coordinate space. The `src` matrix *must* be affine! |descriptionEnd| */ OPTIMIZE_O3 void mtxf_inverse(OUT Mat4 dest, Mat4 src); +/* |description| +Inverts the 4x4 floating-point matrix `src` and stores the inverse in `dest`. Applying the inverse transformation undoes whatever `src` did, returning points back to their original coordinate space. Returns `false` if the inversion failed. +|descriptionEnd| */ +OPTIMIZE_O3 bool mtxf_inverse_non_affine(OUT Mat4 dest, Mat4 src); + /* |description| Extracts the position (translation component) from the transformation matrix `objMtx` relative to the coordinate system defined by `camMtx` and stores that 3D position in `dest`. This can be used to get the object's coordinates in camera space |descriptionEnd| */ diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index 8b0749e07..0d71ab204 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -2,6 +2,7 @@ #include "area.h" #include "engine/math_util.h" +#include "engine/lighting_engine.h" #include "game_init.h" #include "gfx_dimensions.h" #include "main.h" @@ -403,9 +404,12 @@ static void geo_process_master_list_sub(struct GraphNodeMasterList *node) { gMtxTbl[gMtxTblSize].displayList = currList->displayList; gMtxTbl[gMtxTblSize++].usingCamSpace = currList->usingCamSpace; } + gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transformPrev), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); + gSPDisplayList(gDisplayListHead++, currList->displayList); + currList = currList->next; } } @@ -609,6 +613,16 @@ static void geo_process_camera(struct GraphNodeCamera *node) { mtxf_copy(gCamera->mtx, gMatStack[gMatStackIndex]); } + // compute inverse matrix for lighting engine + if (le_is_enabled()) { + Mat4 invCameraMatrix; + if (mtxf_inverse_non_affine(invCameraMatrix, gCamera->mtx)) { + Mtx *invMtx = alloc_display_list(sizeof(Mtx)); + mtxf_to_mtx(invMtx, invCameraMatrix); + gSPMatrix(gDisplayListHead++, invMtx, G_MTX_INVERSE_CAMERA_EXT); + } + } + if (node->fnNode.node.children != 0) { gCurGraphNodeCamera = node; sUsingCamSpace = TRUE; diff --git a/src/pc/gfx/gfx_pc.c b/src/pc/gfx/gfx_pc.c index 483caff2e..59425e67b 100644 --- a/src/pc/gfx/gfx_pc.c +++ b/src/pc/gfx/gfx_pc.c @@ -124,6 +124,10 @@ Color gVertexColor = { 0xFF, 0xFF, 0xFF }; Color gFogColor = { 0xFF, 0xFF, 0xFF }; f32 gFogIntensity = 1; +// need inverse camera matrix to compute world space for lighting engine +static Mat4 sInverseCameraMatrix; +static bool sHasInverseCameraMatrix = false; + // 4x4 pink-black checkerboard texture to indicate missing textures #define MISSING_W 4 #define MISSING_H 4 @@ -627,7 +631,18 @@ static void calculate_normal_dir(const Light_t *light, Vec3f coeffs, bool applyL } static void OPTIMIZE_O3 gfx_sp_matrix(uint8_t parameters, const int32_t *addr) { + Mat4 matrix; + + // remember inverse camera matrix to use for the lighting engine + if (parameters == G_MTX_INVERSE_CAMERA_EXT) { + if (addr) { + memcpy(sInverseCameraMatrix, addr, sizeof(sInverseCameraMatrix)); + sHasInverseCameraMatrix = true; + } + return; + } + #if 0 // Original code when fixed point matrices were used for (int32_t i = 0; i < 4; i++) { @@ -642,6 +657,7 @@ static void OPTIMIZE_O3 gfx_sp_matrix(uint8_t parameters, const int32_t *addr) { memcpy(matrix, addr, sizeof(matrix)); #endif + if (parameters & G_MTX_PROJECTION) { if (parameters & G_MTX_LOAD) { mtxf_copy(rsp.P_matrix, matrix); @@ -678,6 +694,37 @@ static float gfx_adjust_x_for_aspect_ratio(float x) { return x * gfx_current_dimensions.x_adjust_ratio; } +static OPTIMIZE_O3 void gfx_local_to_world_space(OUT Vec3f pos, OUT Vec3f normal) { + if (!sHasInverseCameraMatrix) { return; } + + // strip view matrix off of the model-view matrix + Mat4 model; + mtxf_mul(model, rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size-1], sInverseCameraMatrix); + + // transform position to world + Vec3f worldPos; + worldPos[0] = pos[0] * model[0][0] + pos[1] * model[1][0] + pos[2] * model[2][0] + model[3][0]; + worldPos[1] = pos[0] * model[0][1] + pos[1] * model[1][1] + pos[2] * model[2][1] + model[3][1]; + worldPos[2] = pos[0] * model[0][2] + pos[1] * model[1][2] + pos[2] * model[2][2] + model[3][2]; + + pos[0] = worldPos[0]; + pos[1] = worldPos[1]; + pos[2] = worldPos[2]; + + // transform normal to world + if (normal) { + Vec3f worldNormal; + worldNormal[0] = normal[0] * model[0][0] + normal[1] * model[1][0] + normal[2] * model[2][0]; + worldNormal[1] = normal[0] * model[0][1] + normal[1] * model[1][1] + normal[2] * model[2][1]; + worldNormal[2] = normal[0] * model[0][2] + normal[1] * model[1][2] + normal[2] * model[2][2]; + + normal[0] = worldNormal[0]; + normal[1] = worldNormal[1]; + normal[2] = worldNormal[2]; + } +} + + static void OPTIMIZE_O3 gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx *vertices, bool luaVertexColor) { if (!vertices) { return; } @@ -814,21 +861,37 @@ static void OPTIMIZE_O3 gfx_sp_vertex(size_t n_vertices, size_t dest_index, cons V = (int32_t)((doty / 127.0f + 1.0f) / 4.0f * rsp.texture_scaling_factor.t); } - if (rsp.geometry_mode & G_LIGHTING_ENGINE_EXT) { + if (le_is_enabled() && ((le_get_mode() == LE_MODE_AFFECT_ALL_SHADED) || (rsp.geometry_mode & G_LIGHTING_ENGINE_EXT))) { Color color; CTX_BEGIN(CTX_LIGHTING); - le_calculate_lighting_color(((Vtx_t*)v)->ob, color, 1.0f); + + Vec3f vpos = { v->ob[0], v->ob[1], v->ob[2] }; + Vec3f vnormal = { nx, ny, nz }; + + // transform vpos and vnormal to world space + gfx_local_to_world_space(vpos, vnormal); + + le_calculate_lighting_color_with_normal(vpos, vnormal, color, 1.0f); + CTX_END(CTX_LIGHTING); d->color.r *= color[0] / 255.0f; d->color.g *= color[1] / 255.0f; d->color.b *= color[2] / 255.0f; } - } else if (rsp.geometry_mode & G_LIGHTING_ENGINE_EXT) { + } else if (le_is_enabled() && (rsp.geometry_mode & G_LIGHTING_ENGINE_EXT)) { Color color; CTX_BEGIN(CTX_LIGHTING); - le_calculate_vertex_lighting((Vtx_t*)v, color); + + Vec3f vpos = { v->ob[0], v->ob[1], v->ob[2] }; + + // transform vpos to world space + gfx_local_to_world_space(vpos, NULL); + + le_calculate_vertex_lighting((Vtx_t*)v, vpos, color); + CTX_END(CTX_LIGHTING); + if (luaVertexColor) { d->color.r = color[0] * vertexColorCached[0]; d->color.g = color[1] * vertexColorCached[1]; @@ -1875,6 +1938,8 @@ void gfx_start_frame(void) { void gfx_run(Gfx *commands) { gfx_sp_reset(); + sHasInverseCameraMatrix = false; + //puts("New frame"); if (!gfx_wapi->start_frame()) { diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index 10c85cdfb..bbda80aa3 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -1777,6 +1777,12 @@ 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_MODE_AFFECT_ALL_SHADED=0\n" +"LE_MODE_AFFECT_ONLY_GEOMETRY_MODE=1\n" +"LE_TONE_MAPPING_TOTAL_WEIGHTED=0\n" +"LE_TONE_MAPPING_WEIGHTED=1\n" +"LE_TONE_MAPPING_CLAMP=2\n" +"LE_TONE_MAPPING_REINHARD=3\n" "MARIO_ANIM_SLOW_LEDGE_GRAB=0\n" "MARIO_ANIM_FALL_OVER_BACKWARDS=1\n" "MARIO_ANIM_BACKWARD_AIR_KB=2\n" diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index 18e3d3019..b227a3ce1 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -15515,6 +15515,70 @@ int smlua_func_lvl_set_current_level(lua_State* L) { // lighting_engine.h // /////////////////////// +int smlua_func_le_set_mode(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_mode", 1, top); + return 0; + } + + int mode = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "le_set_mode"); return 0; } + + le_set_mode(mode); + + return 1; +} + +int smlua_func_le_get_mode(UNUSED lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "le_get_mode", 0, top); + return 0; + } + + + lua_pushinteger(L, le_get_mode()); + + return 1; +} + +int smlua_func_le_set_tone_mapping(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_tone_mapping", 1, top); + return 0; + } + + int toneMapping = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "le_set_tone_mapping"); return 0; } + + le_set_tone_mapping(toneMapping); + + return 1; +} + +int smlua_func_le_is_enabled(UNUSED lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "le_is_enabled", 0, top); + return 0; + } + + + lua_pushboolean(L, le_is_enabled()); + + return 1; +} + int smlua_func_le_calculate_lighting_color(lua_State* L) { if (L == NULL) { return 0; } @@ -15542,6 +15606,37 @@ int smlua_func_le_calculate_lighting_color(lua_State* L) { return 1; } +int smlua_func_le_calculate_lighting_color_with_normal(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 4) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "le_calculate_lighting_color_with_normal", 4, top); + return 0; + } + + + Vec3f pos; + smlua_get_vec3f(pos, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "le_calculate_lighting_color_with_normal"); return 0; } + + Vec3f normal; + smlua_get_vec3f(normal, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "le_calculate_lighting_color_with_normal"); return 0; } + + Color out; + smlua_get_color(out, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 3, "le_calculate_lighting_color_with_normal"); return 0; } + f32 lightIntensityScalar = smlua_to_number(L, 4); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 4, "le_calculate_lighting_color_with_normal"); return 0; } + + le_calculate_lighting_color_with_normal(pos, normal, out, lightIntensityScalar); + + smlua_push_color(out, 3); + + return 1; +} + int smlua_func_le_calculate_lighting_dir(lua_State* L) { if (L == NULL) { return 0; } @@ -15735,6 +15830,25 @@ int smlua_func_le_set_light_intensity(lua_State* L) { return 1; } +int smlua_func_le_set_light_use_surface_normals(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "le_set_light_use_surface_normals", 2, top); + return 0; + } + + s32 id = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "le_set_light_use_surface_normals"); return 0; } + bool useSurfaceNormals = smlua_to_boolean(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "le_set_light_use_surface_normals"); return 0; } + + le_set_light_use_surface_normals(id, useSurfaceNormals); + + return 1; +} + ///////////// // mario.h // ///////////// @@ -19890,6 +20004,31 @@ int smlua_func_mtxf_inverse(lua_State* L) { return 1; } +int smlua_func_mtxf_inverse_non_affine(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mtxf_inverse_non_affine", 2, top); + return 0; + } + + + Mat4 dest; + smlua_get_mat4(dest, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mtxf_inverse_non_affine"); return 0; } + + Mat4 src; + smlua_get_mat4(src, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mtxf_inverse_non_affine"); return 0; } + + lua_pushboolean(L, mtxf_inverse_non_affine(dest, src)); + + smlua_push_mat4(dest, 1); + + return 1; +} + int smlua_func_get_pos_from_transform_mtx(lua_State* L) { if (L == NULL) { return 0; } @@ -36380,7 +36519,12 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "lvl_set_current_level", smlua_func_lvl_set_current_level); // lighting_engine.h + smlua_bind_function(L, "le_set_mode", smlua_func_le_set_mode); + smlua_bind_function(L, "le_get_mode", smlua_func_le_get_mode); + smlua_bind_function(L, "le_set_tone_mapping", smlua_func_le_set_tone_mapping); + smlua_bind_function(L, "le_is_enabled", smlua_func_le_is_enabled); 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); smlua_bind_function(L, "le_add_light", smlua_func_le_add_light); smlua_bind_function(L, "le_remove_light", smlua_func_le_remove_light); @@ -36390,6 +36534,7 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "le_set_light_color", smlua_func_le_set_light_color); smlua_bind_function(L, "le_set_light_radius", smlua_func_le_set_light_radius); smlua_bind_function(L, "le_set_light_intensity", smlua_func_le_set_light_intensity); + smlua_bind_function(L, "le_set_light_use_surface_normals", smlua_func_le_set_light_use_surface_normals); // mario.h smlua_bind_function(L, "is_anim_at_end", smlua_func_is_anim_at_end); @@ -36616,6 +36761,7 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "mtxf_mul_vec3s", smlua_func_mtxf_mul_vec3s); smlua_bind_function(L, "mtxf_rotate_xy", smlua_func_mtxf_rotate_xy); smlua_bind_function(L, "mtxf_inverse", smlua_func_mtxf_inverse); + smlua_bind_function(L, "mtxf_inverse_non_affine", smlua_func_mtxf_inverse_non_affine); smlua_bind_function(L, "get_pos_from_transform_mtx", smlua_func_get_pos_from_transform_mtx); // math_util.inl