Fix graph node interpolation (#917)
Some checks are pending
Build coop / build-linux (push) Waiting to run
Build coop / build-steamos (push) Waiting to run
Build coop / build-windows-opengl (push) Waiting to run
Build coop / build-windows-directx (push) Waiting to run
Build coop / build-macos-arm (push) Waiting to run
Build coop / build-macos-intel (push) Waiting to run

Shared graph nodes were incorrectly interpolated, only the first object with a shared graph node was interpolated properly.
Thanks to @Cooliokid956 for noticing that most of node types were **never** interpolated.

- Use a double hashmap to store interpolated data for each graph node and object. All translations, rotations and scales are now interpolated correctly.
- Add `GraphNodeScaleXYZ` type to scale to all 3 dimensions; Add `GEO_SCALE_XYZ` command.

gMtxTbl was also reaching it's limit, so dynamic allocation was added.
This commit is contained in:
PeachyPeach 2025-08-19 00:15:30 +02:00 committed by GitHub
parent b671458f81
commit 6e4373adc9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 428 additions and 144 deletions

View file

@ -3050,6 +3050,9 @@ GRAPH_NODE_TYPE_DISPLAY_LIST = 0x01B
--- @type integer
GRAPH_NODE_TYPE_SCALE = 0x01C
--- @type integer
GRAPH_NODE_TYPE_SCALE_XYZ = 0x01D
--- @type integer
GRAPH_NODE_TYPE_SHADOW = 0x028

View file

@ -969,16 +969,18 @@
--- @class GraphNodeRotation
--- @field public displayList Pointer_Gfx
--- @field public node GraphNode
--- @field public prevRotation Vec3s
--- @field public prevTimestamp integer
--- @field public rotation Vec3s
--- @class GraphNodeScale
--- @field public displayList Pointer_Gfx
--- @field public node GraphNode
--- @field public prevScale number
--- @field public scale number
--- @class GraphNodeScaleXYZ
--- @field public displayList Pointer_Gfx
--- @field public node GraphNode
--- @field public scale Vec3f
--- @class GraphNodeShadow
--- @field public node GraphNode
--- @field public shadowScale integer

View file

@ -317,6 +317,8 @@ static void ParseGeoSymbol(GfxData* aGfxData, DataNode<GeoLayout>* aNode, GeoLay
geo_symbol_5(GEO_HELD_OBJECT, 2);
geo_symbol_2(GEO_SCALE, 0);
geo_symbol_3(GEO_SCALE_WITH_DL, 2);
geo_symbol_4(GEO_SCALE_XYZ, 0);
geo_symbol_5(GEO_SCALE_XYZ_WITH_DL, 4);
geo_symbol_0(GEO_NOP_1E);
geo_symbol_0(GEO_NOP_1F);
geo_symbol_1(GEO_CULLING_RADIUS, 0);

View file

@ -135,8 +135,8 @@ private:
};
extern "C" {
void* hmap_create(MapType type) {
return new HMap(type);
void* hmap_create(bool useUnordered) {
return new HMap(useUnordered ? MapType::Unordered : MapType::Ordered);
}
void* hmap_get(void* map, int64_t key) {

View file

@ -257,6 +257,7 @@ static std::unordered_map<s16, size_t> sGraphNodeSizeMap = {
{ GRAPH_NODE_TYPE_TRANSLATION, sizeof(GraphNodeTranslation) },
{ GRAPH_NODE_TYPE_ROTATION, sizeof(GraphNodeRotation) },
{ GRAPH_NODE_TYPE_SCALE, sizeof(GraphNodeScale) },
{ GRAPH_NODE_TYPE_SCALE_XYZ, sizeof(GraphNodeScaleXYZ) },
{ GRAPH_NODE_TYPE_OBJECT, sizeof(GraphNodeObject) },
{ GRAPH_NODE_TYPE_CULLING_RADIUS, sizeof(GraphNodeCullingRadius) },
{ GRAPH_NODE_TYPE_ANIMATED_PART, sizeof(GraphNodeAnimatedPart) },

View file

@ -1352,6 +1352,7 @@
- GRAPH_NODE_TYPE_BILLBOARD
- GRAPH_NODE_TYPE_DISPLAY_LIST
- GRAPH_NODE_TYPE_SCALE
- GRAPH_NODE_TYPE_SCALE_XYZ
- GRAPH_NODE_TYPE_SHADOW
- GRAPH_NODE_TYPE_OBJECT_PARENT
- GRAPH_NODE_TYPE_GENERATED_LIST

View file

@ -56,6 +56,7 @@
- [GraphNodeRoot](#GraphNodeRoot)
- [GraphNodeRotation](#GraphNodeRotation)
- [GraphNodeScale](#GraphNodeScale)
- [GraphNodeScaleXYZ](#GraphNodeScaleXYZ)
- [GraphNodeShadow](#GraphNodeShadow)
- [GraphNodeStart](#GraphNodeStart)
- [GraphNodeSwitchCase](#GraphNodeSwitchCase)
@ -1477,8 +1478,6 @@
| ----- | ---- | ------ |
| displayList | `Pointer` <`Gfx`> | |
| node | [GraphNode](structs.md#GraphNode) | read-only |
| prevRotation | [Vec3s](structs.md#Vec3s) | read-only |
| prevTimestamp | `integer` | |
| rotation | [Vec3s](structs.md#Vec3s) | read-only |
[:arrow_up_small:](#)
@ -1491,13 +1490,24 @@
| ----- | ---- | ------ |
| displayList | `Pointer` <`Gfx`> | |
| node | [GraphNode](structs.md#GraphNode) | read-only |
| prevScale | `number` | |
| scale | `number` | |
[:arrow_up_small:](#)
<br />
## [GraphNodeScaleXYZ](#GraphNodeScaleXYZ)
| Field | Type | Access |
| ----- | ---- | ------ |
| displayList | `Pointer` <`Gfx`> | |
| node | [GraphNode](structs.md#GraphNode) | read-only |
| scale | [Vec3f](structs.md#Vec3f) | read-only |
[:arrow_up_small:](#)
<br />
## [GraphNodeShadow](#GraphNodeShadow)
| Field | Type | Access |

View file

@ -407,6 +407,30 @@ enum SkyBackgroundParams {
CMD_W(scale), \
CMD_PTR(displayList)
/**
* 0x1D: Create scale scene graph node with optional display list
* 0x01: u8 params
* 0b1000_0000: if set, enable displayList field and drawingLayer
* 0b0100_0000: if set, enable scale XYZ
* 0b0000_1111: drawingLayer
* 0x02-0x03: unused
* 0x04: u32 scale X (0x10000 = 1.0)
* 0x08: u32 scale Y (0x10000 = 1.0)
* 0x0C: u32 scale Z (0x10000 = 1.0)
* 0x10: [u32 displayList: if MSbit bit of params is set, display list segment address]
*/
#define GEO_SCALE_XYZ(layer, sx, sy, sz) \
CMD_BBH(0x1D, (layer | 0x40), 0x0000), \
CMD_W(sx), \
CMD_W(sy), \
CMD_W(sz)
#define GEO_SCALE_XYZ_WITH_DL(layer, sx, sy, sz, displayList) \
CMD_BBH(0x1D, (layer | 0xC0), 0x0000), \
CMD_W(sx), \
CMD_W(sy), \
CMD_W(sz), \
CMD_PTR(displayList)
/**
* 0x1E: No operation
*/

View file

@ -535,29 +535,45 @@ void geo_layout_cmd_node_rotation(void) {
0x1D: Create scale scene graph node with optional display list
cmd+0x01: u8 params
(params & 0x80): if set, enable displayList field and drawingLayer
(params & 0x40): if set, enable scale XYZ
(params & 0x0F): drawingLayer
cmd+0x04: u32 scale (0x10000 = 1.0)
[cmd+0x08: void *displayList]
or
cmd+0x04: u32 scale X (0x10000 = 1.0)
cmd+0x08: u32 scale Y (0x10000 = 1.0)
cmd+0x0C: u32 scale Z (0x10000 = 1.0)
[cmd+0x08/0x10: void *displayList]
*/
void geo_layout_cmd_node_scale(void) {
struct GraphNodeScale *graphNode;
s16 drawingLayer = 0;
s16 params = cur_geo_cmd_u8(0x01);
f32 scale = cur_geo_cmd_u32(0x04) / 65536.0f;
Vec3f scale;
void *displayList = NULL;
bool isScaleXYZ = (params & 0x40) != 0;
if (params & 0x80) {
displayList = cur_geo_cmd_ptr(0x08);
drawingLayer = params & 0x0F;
gGeoLayoutCommand += 4 << CMD_SIZE_SHIFT;
if (isScaleXYZ) {
scale[0] = cur_geo_cmd_u32(0x04) / 65536.0f;
scale[1] = cur_geo_cmd_u32(0x08) / 65536.0f;
scale[2] = cur_geo_cmd_u32(0x0C) / 65536.0f;
gGeoLayoutCommand += 0x10 << CMD_SIZE_SHIFT;
} else {
scale[0] = cur_geo_cmd_u32(0x04) / 65536.0f;
gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
}
graphNode = init_graph_node_scale(gGraphNodePool, NULL, drawingLayer, displayList, scale);
if (params & 0x80) {
displayList = cur_geo_cmd_ptr(0x00);
drawingLayer = params & 0x0F;
gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT;
}
register_scene_graph_node(&graphNode->node);
struct GraphNode *graphNode = (
isScaleXYZ ?
(struct GraphNode *) init_graph_node_scale_xyz(gGraphNodePool, NULL, drawingLayer, displayList, scale) :
(struct GraphNode *) init_graph_node_scale(gGraphNodePool, NULL, drawingLayer, displayList, scale[0])
);
gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT;
register_scene_graph_node(graphNode);
}
// 0x1E: No operation

View file

@ -291,7 +291,26 @@ struct GraphNodeScale *init_graph_node_scale(struct DynamicPool *pool,
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_SCALE);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->scale = scale;
graphNode->prevScale = scale;
graphNode->displayList = dynos_gfx_get_writable_display_list(displayList);
}
return graphNode;
}
/**
* Allocates and returns a newly created XYZ scaling node
*/
struct GraphNodeScaleXYZ *init_graph_node_scale_xyz(struct DynamicPool *pool,
struct GraphNodeScaleXYZ *graphNode, s32 drawingLayer,
void *displayList, Vec3f scale) {
if (pool != NULL) {
graphNode = dynamic_pool_alloc(pool, sizeof(struct GraphNodeScaleXYZ));
}
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_SCALE_XYZ);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
vec3f_copy(graphNode->scale, scale);
graphNode->displayList = dynos_gfx_get_writable_display_list(displayList);
}

View file

@ -44,6 +44,7 @@
#define GRAPH_NODE_TYPE_BILLBOARD 0x01A
#define GRAPH_NODE_TYPE_DISPLAY_LIST 0x01B
#define GRAPH_NODE_TYPE_SCALE 0x01C
#define GRAPH_NODE_TYPE_SCALE_XYZ 0x01D
#define GRAPH_NODE_TYPE_SHADOW 0x028
#define GRAPH_NODE_TYPE_OBJECT_PARENT 0x029
#define GRAPH_NODE_TYPE_GENERATED_LIST (0x02A | GRAPH_NODE_TYPE_FUNCTIONAL)
@ -241,8 +242,6 @@ struct GraphNodeRotation
/*0x00*/ struct GraphNode node;
/*0x14*/ Gfx *displayList;
/*0x18*/ Vec3s rotation;
Vec3s prevRotation;
u32 prevTimestamp;
};
/** GraphNode part that transforms itself and its children based on animation
@ -293,7 +292,16 @@ struct GraphNodeScale
/*0x00*/ struct GraphNode node;
/*0x14*/ Gfx *displayList;
/*0x18*/ f32 scale;
/*????*/ f32 prevScale;
};
/** GraphNodeScale but on X, Y and Z independently.
* Must be another graph node type for retro-compatibility.
*/
struct GraphNodeScaleXYZ
{
/*0x00*/ struct GraphNode node;
/*0x14*/ Gfx *displayList;
/*0x18*/ Vec3f scale;
};
/** GraphNode that draws a shadow under an object.
@ -403,6 +411,8 @@ struct GraphNodeRotation *init_graph_node_rotation(struct DynamicPool *pool, str
s32 drawingLayer, void *displayList, Vec3s rotation);
struct GraphNodeScale *init_graph_node_scale(struct DynamicPool *pool, struct GraphNodeScale *graphNode,
s32 drawingLayer, void *displayList, f32 scale);
struct GraphNodeScaleXYZ *init_graph_node_scale_xyz(struct DynamicPool *pool, struct GraphNodeScaleXYZ *graphNode,
s32 drawingLayer, void *displayList, Vec3f scale);
struct GraphNodeObject *init_graph_node_object(struct DynamicPool *pool, struct GraphNodeObject *graphNode,
struct GraphNode *sharedChild, Vec3f pos, Vec3s angle, Vec3f scale);
struct GraphNodeCullingRadius *init_graph_node_culling_radius(struct DynamicPool *pool, struct GraphNodeCullingRadius *graphNode, s16 radius);

View file

@ -471,8 +471,8 @@ void alloc_surface_pools(void) {
clear_static_surfaces();
clear_dynamic_surfaces();
sSurfaceNodePool = growing_array_init(sSurfaceNodePool, 0x1000);
sSurfacePool = growing_array_init(sSurfacePool, 0x400);
sSurfaceNodePool = growing_array_init(sSurfaceNodePool, 0x1000, malloc, free);
sSurfacePool = growing_array_init(sSurfacePool, 0x400, malloc, smlua_free_surface);
gEnvironmentRegions = NULL;
gSurfaceNodesAllocated = 0;

View file

@ -253,6 +253,7 @@ void clear_areas(void) {
}
le_clear();
geo_clear_interp_data();
}
void clear_area_graph_nodes(void) {
@ -311,6 +312,7 @@ void unload_area(void) {
}
le_clear();
geo_clear_interp_data();
}
void load_mario_area(void) {

View file

@ -12,6 +12,7 @@
#include "main.h"
#include "engine/math_util.h"
#include "engine/graph_node.h"
#include "rendering_graph_node.h"
#include "area.h"
#include "save_file.h"
#include "sound_init.h"
@ -1749,6 +1750,7 @@ s32 update_level(void) {
s32 init_level(void) {
sync_objects_clear();
geo_clear_interp_data();
reset_dialog_render_state();
s32 val4 = 0;

View file

@ -460,11 +460,6 @@ Gfx* geo_mario_tilt_torso(s32 callContext, struct GraphNode* node, Mat4* mtx) {
rotNode->rotation[0] = bodyState->torsoAngle[1] * character->torsoRotMult;
rotNode->rotation[1] = bodyState->torsoAngle[2] * character->torsoRotMult;
rotNode->rotation[2] = bodyState->torsoAngle[0] * character->torsoRotMult;
if (plrIdx != 0) {
// only interpolate angles for the local player
vec3s_copy(rotNode->prevRotation, rotNode->rotation);
rotNode->prevTimestamp = gGlobalTimer;
}
// update torso position in bodyState
get_pos_from_transform_mtx(bodyState->torsoPos, *curTransform, *gCurGraphNodeCamera->matrixPtr);
bodyState->updateTorsoTime = gGlobalTimer;
@ -502,12 +497,6 @@ Gfx* geo_mario_head_rotation(s32 callContext, struct GraphNode* node, Mat4* c) {
vec3s_set(rotNode->rotation, 0, 0, 0);
}
if (plrIdx != 0) {
// only interpolate angles for the local player
vec3s_copy(rotNode->prevRotation, rotNode->rotation);
rotNode->prevTimestamp = gGlobalTimer;
}
// update head position in bodyState
get_pos_from_transform_mtx(bodyState->headPos,
*c,

View file

@ -4,7 +4,6 @@
#include "memory.h"
#include "print.h"
#include "pc/debuglog.h"
#include "pc/lua/smlua.h"
#define ALIGN16(val) (((val) + 0xF) & ~0xF)
@ -186,12 +185,14 @@ void growing_pool_free_pool(struct GrowingPool *pool) {
// growing array //
///////////////////
struct GrowingArray *growing_array_init(struct GrowingArray *array, u32 capacity) {
struct GrowingArray *growing_array_init(struct GrowingArray *array, u32 capacity, GrowingArrayAllocFunc alloc, GrowingArrayFreeFunc free) {
growing_array_free(&array);
array = calloc(1, sizeof(struct GrowingArray));
array->buffer = calloc(capacity, sizeof(void *));
array->capacity = capacity;
array->count = 0;
array->alloc = alloc;
array->free = free;
return array;
}
@ -211,7 +212,7 @@ void *growing_array_alloc(struct GrowingArray *array, u32 size) {
// Alloc element if needed
void **elem = &array->buffer[array->count++];
if (!*elem) {
*elem = malloc(size);
*elem = array->alloc(size);
}
memset(*elem, 0, size);
return *elem;
@ -223,7 +224,7 @@ void growing_array_free(struct GrowingArray **array) {
if (*array) {
for (u32 i = 0; i != (*array)->capacity; ++i) {
if ((*array)->buffer[i]) {
smlua_free((*array)->buffer[i]);
(*array)->free((*array)->buffer[i]);
}
}
free((*array)->buffer);

View file

@ -39,11 +39,16 @@ struct GrowingPoolNode
struct GrowingPoolNode* prev;
};
typedef void *(*GrowingArrayAllocFunc)(size_t);
typedef void (*GrowingArrayFreeFunc)(void *);
struct GrowingArray
{
void **buffer;
u32 count;
u32 capacity;
GrowingArrayAllocFunc alloc;
GrowingArrayFreeFunc free;
};
struct MarioAnimation;
@ -70,7 +75,7 @@ struct GrowingPool* growing_pool_init(struct GrowingPool* pool, u32 nodeSize);
void* growing_pool_alloc(struct GrowingPool *pool, u32 size);
void growing_pool_free_pool(struct GrowingPool *pool);
struct GrowingArray *growing_array_init(struct GrowingArray *array, u32 capacity);
struct GrowingArray *growing_array_init(struct GrowingArray *array, u32 capacity, GrowingArrayAllocFunc alloc, GrowingArrayFreeFunc free);
void *growing_array_alloc(struct GrowingArray *array, u32 size);
void growing_array_free(struct GrowingArray **array);
void growing_array_debug_print(struct GrowingArray *array, const char *name, s32 x, s32 y);

View file

@ -19,6 +19,7 @@
#include "obj_behaviors.h"
#include "platform_displacement.h"
#include "profiler.h"
#include "rendering_graph_node.h"
#include "spawn_object.h"
#include "first_person_cam.h"
#include "engine/math_util.h"
@ -624,6 +625,7 @@ void clear_objects(void) {
gObjectLists = gObjectListArray;
clear_dynamic_surfaces();
geo_clear_interp_data();
}
/**

View file

@ -3,6 +3,7 @@
#include "area.h"
#include "engine/math_util.h"
#include "engine/lighting_engine.h"
#include "data/dynos_cmap.cpp.h"
#include "game_init.h"
#include "gfx_dimensions.h"
#include "main.h"
@ -183,28 +184,27 @@ static Gfx* sViewportClipPos = NULL;
static Vp sViewportPrev = { 0 };
static Vp sViewportInterp = { 0 };
static struct GraphNodeBackground* sBackgroundNode = NULL;
Gfx* gBackgroundSkyboxGfx = NULL;
Vtx* gBackgroundSkyboxVerts[SKYBOX_TILES_Y][SKYBOX_TILES_X] = { 0 };
Mtx* gBackgroundSkyboxMtx = NULL;
struct GraphNodeRoot* sBackgroundNodeRoot = NULL;
#define MAX_SHADOW_NODES 128
struct ShadowInterp sShadowInterp[MAX_SHADOW_NODES] = { 0 };
static struct GraphNodeBackground* sBackgroundNode = NULL;
static struct GraphNodeRoot* sBackgroundNodeRoot = NULL;
static struct GraphNodeCamera* sCameraNode = NULL;
static struct GrowingArray* sShadowInterp = NULL;
struct ShadowInterp* gShadowInterpCurrent = NULL;
static u8 sShadowInterpCount = 0;
static struct GraphNodeCamera * sCameraNode = NULL;
struct {
struct MtxInterp {
Gfx *pos;
Mtx *mtx;
Mtx *mtxPrev;
void *displayList;
Mtx interp;
u8 usingCamSpace;
} gMtxTbl[6400];
s32 gMtxTblSize = 0;
};
static struct GrowingArray* sMtxTbl = NULL;
struct Object* gCurGraphNodeProcessingObject = NULL;
struct MarioState* gCurGraphNodeMarioState = NULL;
@ -213,8 +213,36 @@ f32 gOverrideFOV = 0;
f32 gOverrideNear = 0;
f32 gOverrideFar = 0;
static void init_mtx(void) {
// matrices
if (!sMtxTbl) {
sMtxTbl = growing_array_init(NULL, 1024, malloc, free);
if (!sMtxTbl) {
sys_fatal("Cannot allocate matrix buffer for interpolation");
}
}
sMtxTbl->count = 0;
// shadows
if (!sShadowInterp) {
sShadowInterp = growing_array_init(NULL, 32, malloc, free);
if (!sShadowInterp) {
sys_fatal("Cannot allocate shadow buffer for interpolation");
}
}
sShadowInterp->count = 0;
gShadowInterpCurrent = NULL;
}
static void reset_mtx(void) {
growing_array_free(&sMtxTbl);
growing_array_free(&sShadowInterp);
init_mtx();
}
void patch_mtx_before(void) {
gMtxTblSize = 0;
init_mtx();
if (sPerspectiveNode != NULL) {
sPerspectiveNode->prevFov = sPerspectiveNode->fov;
@ -235,8 +263,6 @@ void patch_mtx_before(void) {
sBackgroundNode = NULL;
gBackgroundSkyboxGfx = NULL;
}
sShadowInterpCount = 0;
}
void patch_mtx_interpolated(f32 delta) {
@ -285,8 +311,8 @@ void patch_mtx_interpolated(f32 delta) {
}
struct GraphNodeObject* savedObj = gCurGraphNodeObject;
for (s32 i = 0; i < sShadowInterpCount; i++) {
struct ShadowInterp* interp = &sShadowInterp[i];
for (u32 i = 0; i < sShadowInterp->count; i++) {
struct ShadowInterp* interp = sShadowInterp->buffer[i];
if (!interp->gfx) { continue; }
gShadowInterpCurrent = interp;
Vec3f posInterp;
@ -304,7 +330,7 @@ void patch_mtx_interpolated(f32 delta) {
// technically this is improper use of mtxf functions, but coop doesn't target N64
Mtx camTranfInv, prevCamTranfInv;
Mtx camInterp;
bool translateCamSpace = (gMtxTblSize > 0) && sCameraNode && (sCameraNode->matrixPtr != NULL) && (sCameraNode->matrixPtrPrev != NULL);
bool translateCamSpace = (sMtxTbl->count > 0) && sCameraNode && (sCameraNode->matrixPtr != NULL) && (sCameraNode->matrixPtrPrev != NULL);
if (translateCamSpace) {
// compute inverse camera matrix to transform out of camera space later
mtxf_inverse(camTranfInv.m, *sCameraNode->matrixPtr);
@ -318,12 +344,13 @@ void patch_mtx_interpolated(f32 delta) {
mtxf_to_mtx(&camInterp, camInterp.m);
}
for (s32 i = 0; i < gMtxTblSize; i++) {
Gfx *pos = gMtxTbl[i].pos;
Mtx *srcMtx = gMtxTbl[i].mtx;
Mtx *srcMtxPrev = gMtxTbl[i].mtxPrev;
for (u32 i = 0; i < sMtxTbl->count; i++) {
struct MtxInterp *interp = sMtxTbl->buffer[i];
Gfx *pos = interp->pos;
Mtx *srcMtx = interp->mtx;
Mtx *srcMtxPrev = interp->mtxPrev;
if (gMtxTbl[i].usingCamSpace && translateCamSpace) {
if (interp->usingCamSpace && translateCamSpace) {
// transform out of camera space so the matrix can interp in world space
Mtx bufMtx, bufMtxPrev;
mtxf_copy(bufMtx.m, srcMtx->m);
@ -333,18 +360,96 @@ void patch_mtx_interpolated(f32 delta) {
srcMtx = &bufMtx;
srcMtxPrev = &bufMtxPrev;
}
delta_interpolate_mtx(&gMtxTbl[i].interp, srcMtxPrev, srcMtx, delta);
if (gMtxTbl[i].usingCamSpace) {
delta_interpolate_mtx(&interp->interp, srcMtxPrev, srcMtx, delta);
if (interp->usingCamSpace) {
// transform back to camera space, respecting camera interpolation
mtxf_mul(gMtxTbl[i].interp.m, gMtxTbl[i].interp.m, camInterp.m);
mtxf_mul(interp->interp.m, interp->interp.m, camInterp.m);
}
gSPMatrix(pos++, VIRTUAL_TO_PHYSICAL(&gMtxTbl[i].interp),
gSPMatrix(pos++, VIRTUAL_TO_PHYSICAL(&interp->interp),
G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
}
gCamSkipInterp = 0;
}
/**
* Graph node interpolation
*/
struct GraphNodeInterpData {
Vec3s translation;
Vec3s rotation;
Vec3f scale;
u32 timestamp;
};
static void *sGraphNodeInterpDataMap = NULL;
static struct GraphNodeInterpData *geo_get_interp_data(void *node, struct GraphNodeObject *obj) {
// Map for nodes
if (!sGraphNodeInterpDataMap) {
sGraphNodeInterpDataMap = hmap_create(true);
if (!sGraphNodeInterpDataMap) {
return NULL;
}
}
// Map for objects
void *nodeInterpData = hmap_get(sGraphNodeInterpDataMap, (int64_t) node);
if (!nodeInterpData) {
nodeInterpData = hmap_create(true);
if (!nodeInterpData) {
return NULL;
}
hmap_put(sGraphNodeInterpDataMap, (int64_t) node, nodeInterpData);
}
// Node/object interp data
struct GraphNodeInterpData *interp = hmap_get(nodeInterpData, (int64_t) obj);
if (!interp) {
interp = calloc(1, sizeof(struct GraphNodeInterpData));
if (!interp) {
return NULL;
}
hmap_put(nodeInterpData, (int64_t) obj, interp);
}
return interp;
}
static void geo_init_or_update_interp_data(struct GraphNodeInterpData *interp, Vec3s translation, Vec3s rotation, Vec3f scale, bool update) {
if (interp && (update || interp->timestamp < gGlobalTimer - 1)) {
if (translation) { vec3s_copy(interp->translation, translation); }
if (rotation) { vec3s_copy(interp->rotation, rotation); }
if (scale) { vec3f_copy(interp->scale, scale); }
interp->timestamp = gGlobalTimer;
}
}
static bool geo_should_interpolate(struct GraphNodeInterpData *interp) {
return interp != NULL && interp->timestamp == gGlobalTimer - 1;
}
void geo_clear_interp_data() {
for (void *nodeInterpData = hmap_begin(sGraphNodeInterpDataMap); nodeInterpData; nodeInterpData = hmap_next(sGraphNodeInterpDataMap)) {
for (struct GraphNodeInterpData *interp = hmap_begin(nodeInterpData); interp; interp = hmap_next(nodeInterpData)) {
free(interp);
}
hmap_destroy(nodeInterpData);
}
hmap_destroy(sGraphNodeInterpDataMap);
sGraphNodeInterpDataMap = NULL;
reset_mtx();
}
#define geo_update_interpolation(translation, rotation, scale, ...) { \
struct GraphNodeInterpData *interp = geo_get_interp_data(node, gCurGraphNodeObject); \
geo_init_or_update_interp_data(interp, translation, rotation, scale, false); \
{ __VA_ARGS__; } \
geo_init_or_update_interp_data(interp, translation, rotation, scale, true); \
}
/**
* Increments the matrix stack index and sets the matrixs at the new index.
*/
@ -397,13 +502,13 @@ static void geo_process_master_list_sub(struct GraphNodeMasterList *node) {
gDPSetRenderMode(gDisplayListHead++, modeList->modes[i], mode2List->modes[i]);
while (currList != NULL) {
detect_and_skip_mtx_interpolation(&currList->transform, &currList->transformPrev);
if ((u32) gMtxTblSize < sizeof(gMtxTbl) / sizeof(gMtxTbl[0])) {
gMtxTbl[gMtxTblSize].pos = gDisplayListHead;
gMtxTbl[gMtxTblSize].mtx = currList->transform;
gMtxTbl[gMtxTblSize].mtxPrev = currList->transformPrev;
gMtxTbl[gMtxTblSize].displayList = currList->displayList;
gMtxTbl[gMtxTblSize++].usingCamSpace = currList->usingCamSpace;
}
struct MtxInterp *interp = growing_array_alloc(sMtxTbl, sizeof(struct MtxInterp));
interp->pos = gDisplayListHead;
interp->mtx = currList->transform;
interp->mtxPrev = currList->transformPrev;
interp->displayList = currList->displayList;
interp->usingCamSpace = currList->usingCamSpace;
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transformPrev),
G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
@ -648,10 +753,19 @@ static void geo_process_translation_rotation(struct GraphNodeTranslationRotation
// Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB.
if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; }
// current frame
vec3s_to_vec3f(translation, node->translation);
mtxf_rotate_zxy_and_translate(mtxf, translation, node->rotation);
mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], mtxf, gMatStackPrev[gMatStackIndex]);
// previous frame
geo_update_interpolation(node->translation, node->rotation, NULL,
if (geo_should_interpolate(interp)) {
vec3s_to_vec3f(translation, interp->translation);
mtxf_rotate_zxy_and_translate(mtxf, translation, interp->rotation);
}
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], mtxf, gMatStackPrev[gMatStackIndex]);
);
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
@ -677,10 +791,19 @@ static void geo_process_translation(struct GraphNodeTranslation *node) {
// Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\.
if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; }
// current frame
vec3s_to_vec3f(translation, node->translation);
mtxf_rotate_zxy_and_translate(mtxf, translation, gVec3sZero);
mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], mtxf, gMatStackPrev[gMatStackIndex]);
// previous frame
geo_update_interpolation(node->translation, NULL, NULL,
if (geo_should_interpolate(interp)) {
vec3s_to_vec3f(translation, interp->translation);
mtxf_rotate_zxy_and_translate(mtxf, translation, gVec3sZero);
}
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], mtxf, gMatStackPrev[gMatStackIndex]);
);
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
@ -705,17 +828,17 @@ static void geo_process_rotation(struct GraphNodeRotation *node) {
// Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\.
if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; }
// current frame
mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, node->rotation);
mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
if (gGlobalTimer == node->prevTimestamp + 1) {
mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, node->prevRotation);
} else {
mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, node->rotation);
}
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], mtxf, gMatStackPrev[gMatStackIndex]);
vec3s_copy(node->prevRotation, node->rotation);
node->prevTimestamp = gGlobalTimer;
// previous frame
geo_update_interpolation(NULL, node->rotation, NULL,
if (geo_should_interpolate(interp)) {
mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, interp->rotation);
}
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], mtxf, gMatStackPrev[gMatStackIndex]);
);
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
@ -741,17 +864,53 @@ static void geo_process_scale(struct GraphNodeScale *node) {
// Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\.
if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; }
// current frame
vec3f_set(scaleVec, node->scale, node->scale, node->scale);
mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], scaleVec);
/* TODO: this fails because multiple player models reuse the same scalenode
vec3f_set(prevScaleVec, node->prevScale, node->prevScale, node->prevScale);
mtxf_scale_vec3f(gMatStackPrev[gMatStackIndex + 1], gMatStackPrev[gMatStackIndex], prevScaleVec);
node->prevScale = node->scale;*/
// previous frame
geo_update_interpolation(NULL, NULL, scaleVec,
vec3f_copy(prevScaleVec,
geo_should_interpolate(interp) ?
interp->scale :
scaleVec
);
mtxf_scale_vec3f(gMatStackPrev[gMatStackIndex + 1], gMatStackPrev[gMatStackIndex], prevScaleVec);
);
// just use the current scale for now
vec3f_set(prevScaleVec, node->scale, node->scale, node->scale);
mtxf_scale_vec3f(gMatStackPrev[gMatStackIndex + 1], gMatStackPrev[gMatStackIndex], scaleVec);
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
if (node->displayList != NULL) {
geo_append_display_list(node->displayList, node->node.flags >> 8);
}
if (node->node.children != NULL) {
geo_process_node_and_siblings(node->node.children);
}
gMatStackIndex--;
}
/**
* Process an XYZ scaling node. A transformation matrix based on the node's
* scale is created and pushed on both the float and fixed point matrix stacks.
* For the rest it acts as a normal display list node.
*/
static void geo_process_scale_xyz(struct GraphNodeScaleXYZ *node) {
// Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\.
if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; }
// current frame
mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], node->scale);
// previous frame
geo_update_interpolation(NULL, NULL, node->scale,
mtxf_scale_vec3f(gMatStackPrev[gMatStackIndex + 1], gMatStackPrev[gMatStackIndex],
geo_should_interpolate(interp) ?
interp->scale :
node->scale
);
);
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
@ -779,21 +938,28 @@ static void geo_process_billboard(struct GraphNodeBillboard *node) {
s16 nextMatStackIndex = gMatStackIndex + 1;
// current frame
vec3s_to_vec3f(translation, node->translation);
mtxf_billboard(gMatStack[nextMatStackIndex], gMatStack[gMatStackIndex], translation,
gCurGraphNodeCamera->roll);
mtxf_billboard(gMatStackPrev[nextMatStackIndex], gMatStackPrev[gMatStackIndex], translation,
gCurGraphNodeCamera->roll);
mtxf_billboard(gMatStack[nextMatStackIndex], gMatStack[gMatStackIndex], translation, gCurGraphNodeCamera->roll);
// previous frame
geo_update_interpolation(node->translation, NULL, NULL,
if (geo_should_interpolate(interp)) {
vec3s_to_vec3f(translation, interp->translation);
}
mtxf_billboard(gMatStackPrev[nextMatStackIndex], gMatStackPrev[gMatStackIndex], translation, gCurGraphNodeCamera->roll);
);
if (gCurGraphNodeHeldObject != NULL) {
mtxf_scale_vec3f(gMatStack[nextMatStackIndex], gMatStack[nextMatStackIndex],
gCurGraphNodeHeldObject->objNode->header.gfx.scale);
mtxf_scale_vec3f(gMatStackPrev[nextMatStackIndex], gMatStackPrev[nextMatStackIndex],
gCurGraphNodeHeldObject->objNode->header.gfx.scale);
gCurGraphNodeHeldObject->objNode->header.gfx.prevScale);
} else if (gCurGraphNodeObject != NULL) {
mtxf_scale_vec3f(gMatStack[nextMatStackIndex], gMatStack[nextMatStackIndex],
gCurGraphNodeObject->scale);
mtxf_scale_vec3f(gMatStackPrev[nextMatStackIndex], gMatStackPrev[nextMatStackIndex],
gCurGraphNodeObject->scale);
gCurGraphNodeObject->prevScale);
} else {
//LOG_ERROR("gCurGraphNodeObject and gCurGraphNodeHeldObject are both NULL!");
}
@ -938,8 +1104,6 @@ static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) {
Mat4 matrix;
Vec3s rotation;
Vec3f translation;
Vec3s rotationPrev;
Vec3f translationPrev;
// Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\.
if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; }
@ -947,20 +1111,25 @@ static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) {
u16 *animAttribute = gCurrAnimAttribute;
u8 animType = gCurAnimType;
// current frame
vec3s_copy(rotation, gVec3sZero);
vec3f_set(translation, node->translation[0], node->translation[1], node->translation[2]);
vec3s_copy(rotationPrev, rotation);
vec3f_copy(translationPrev, translation);
anim_process(translationPrev, rotationPrev, &animType, gPrevAnimFrame, &animAttribute);
vec3s_to_vec3f(translation, node->translation);
anim_process(translation, rotation, &gCurAnimType, gCurrAnimFrame, &gCurrAnimAttribute);
mtxf_rotate_xyz_and_translate(matrix, translation, rotation);
mtxf_mul(gMatStack[gMatStackIndex + 1], matrix, gMatStack[gMatStackIndex]);
mtxf_rotate_xyz_and_translate(matrix, translationPrev, rotationPrev);
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], matrix, gMatStackPrev[gMatStackIndex]);
// previous frame
geo_update_interpolation(node->translation, NULL, NULL,
vec3s_to_vec3f(translation,
geo_should_interpolate(interp) ?
interp->translation :
node->translation
);
vec3s_copy(rotation, gVec3sZero);
anim_process(translation, rotation, &animType, gPrevAnimFrame, &animAttribute);
mtxf_rotate_xyz_and_translate(matrix, translation, rotation);
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], matrix, gMatStackPrev[gMatStackIndex]);
);
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
@ -1058,18 +1227,25 @@ static void geo_process_shadow(struct GraphNodeShadow *node) {
shadowScale = node->shadowScale * gCurGraphNodeObject->scale[0];
}
f32 objScale = 1.0f;
Vec3f objScale = { 1, 1, 1 };
if (gCurAnimEnabled) {
if (gCurAnimType == ANIM_TYPE_TRANSLATION
|| gCurAnimType == ANIM_TYPE_LATERAL_TRANSLATION) {
struct GraphNode *geo = node->node.children;
if (geo != NULL && geo->type == GRAPH_NODE_TYPE_SCALE) {
objScale = ((struct GraphNodeScale *) geo)->scale;
if (geo != NULL) {
switch (geo->type) {
case GRAPH_NODE_TYPE_SCALE:
vec3f_mul(objScale, ((struct GraphNodeScale *) geo)->scale);
break;
case GRAPH_NODE_TYPE_SCALE_XYZ:
vec3f_copy(objScale, ((struct GraphNodeScaleXYZ *) geo)->scale);
break;
}
}
animOffset[0] = retrieve_animation_value(gCurAnim, gCurrAnimFrame, &gCurrAnimAttribute) * gCurAnimTranslationMultiplier * objScale;
animOffset[0] = retrieve_animation_value(gCurAnim, gCurrAnimFrame, &gCurrAnimAttribute) * gCurAnimTranslationMultiplier * objScale[0];
animOffset[1] = 0.0f;
gCurrAnimAttribute += 2;
animOffset[2] = retrieve_animation_value(gCurAnim, gCurrAnimFrame, &gCurrAnimAttribute) * gCurAnimTranslationMultiplier * objScale;
animOffset[2] = retrieve_animation_value(gCurAnim, gCurrAnimFrame, &gCurrAnimAttribute) * gCurAnimTranslationMultiplier * objScale[2];
gCurrAnimAttribute -= 6;
// simple matrix rotation so the shadow offset rotates along with the object
@ -1103,18 +1279,14 @@ static void geo_process_shadow(struct GraphNodeShadow *node) {
gCurGraphNodeObject->prevShadowPosTimestamp = gGlobalTimer;
}
if (sShadowInterpCount < MAX_SHADOW_NODES) {
struct ShadowInterp* interp = &sShadowInterp[sShadowInterpCount++];
gShadowInterpCurrent = interp;
interp->gfx = NULL;
interp->node = node;
interp->shadowScale = shadowScale;
interp->obj = gCurGraphNodeObject;
vec3f_copy(interp->shadowPos, gCurGraphNodeObject->shadowPos);
vec3f_copy(interp->shadowPosPrev, shadowPosPrev);
} else {
gShadowInterpCurrent = NULL;
}
struct ShadowInterp* interp = growing_array_alloc(sShadowInterp, sizeof(struct ShadowInterp));
gShadowInterpCurrent = interp;
interp->gfx = NULL;
interp->node = node;
interp->shadowScale = shadowScale;
interp->obj = gCurGraphNodeObject;
vec3f_copy(interp->shadowPos, gCurGraphNodeObject->shadowPos);
vec3f_copy(interp->shadowPosPrev, shadowPosPrev);
Gfx *shadowListPrev = create_shadow_below_xyz(shadowPosPrev[0], shadowPosPrev[1],
shadowPosPrev[2], shadowScale,
@ -1640,6 +1812,9 @@ void geo_process_node_and_siblings(struct GraphNode *firstNode) {
case GRAPH_NODE_TYPE_SCALE:
geo_process_scale((struct GraphNodeScale *) curGraphNode);
break;
case GRAPH_NODE_TYPE_SCALE_XYZ:
geo_process_scale_xyz((struct GraphNodeScaleXYZ *) curGraphNode);
break;
case GRAPH_NODE_TYPE_SHADOW:
geo_process_shadow((struct GraphNodeShadow *) curGraphNode);
break;
@ -1685,11 +1860,11 @@ static void geo_clear_interp_variables(void) {
gBackgroundSkyboxMtx = NULL;
sBackgroundNodeRoot = NULL;
sShadowInterp->count = 0;
gShadowInterpCurrent = NULL;
sShadowInterpCount = 0;
sMtxTbl->count = 0;
sCameraNode = NULL;
gMtxTblSize = 0;
gCurGraphNodeProcessingObject = NULL;
gCurGraphNodeMarioState = NULL;
}

View file

@ -43,6 +43,7 @@ extern f32 gOverrideFar;
void geo_process_node_and_siblings(struct GraphNode *firstNode);
void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor);
void geo_clear_interp_data();
struct ShadowInterp {
Gfx* gfx;

View file

@ -11,7 +11,7 @@
DialogTable *gDialogTable = NULL;
void dialog_table_init(void) {
gDialogTable = growing_array_init(gDialogTable, 256);
gDialogTable = growing_array_init(gDialogTable, 256, malloc, free);
for (u32 i = 0; i < DIALOG_COUNT; i++) {
const struct DialogEntry* dialogOrig = smlua_text_utils_dialog_get_unmodified(i);

View file

@ -1248,23 +1248,27 @@ static struct LuaObjectField sGraphNodeRootFields[LUA_GRAPH_NODE_ROOT_FIELD_COUN
{ "y", LVT_S16, offsetof(struct GraphNodeRoot, y), false, LOT_NONE, 1, sizeof(s16) },
};
#define LUA_GRAPH_NODE_ROTATION_FIELD_COUNT 5
#define LUA_GRAPH_NODE_ROTATION_FIELD_COUNT 3
static struct LuaObjectField sGraphNodeRotationFields[LUA_GRAPH_NODE_ROTATION_FIELD_COUNT] = {
{ "displayList", LVT_COBJECT_P, offsetof(struct GraphNodeRotation, displayList), false, LOT_GFX, 1, sizeof(Gfx*) },
{ "node", LVT_COBJECT, offsetof(struct GraphNodeRotation, node), true, LOT_GRAPHNODE, 1, sizeof(struct GraphNode) },
{ "prevRotation", LVT_COBJECT, offsetof(struct GraphNodeRotation, prevRotation), true, LOT_VEC3S, 1, sizeof(Vec3s) },
{ "prevTimestamp", LVT_U32, offsetof(struct GraphNodeRotation, prevTimestamp), false, LOT_NONE, 1, sizeof(u32) },
{ "rotation", LVT_COBJECT, offsetof(struct GraphNodeRotation, rotation), true, LOT_VEC3S, 1, sizeof(Vec3s) },
{ "displayList", LVT_COBJECT_P, offsetof(struct GraphNodeRotation, displayList), false, LOT_GFX, 1, sizeof(Gfx*) },
{ "node", LVT_COBJECT, offsetof(struct GraphNodeRotation, node), true, LOT_GRAPHNODE, 1, sizeof(struct GraphNode) },
{ "rotation", LVT_COBJECT, offsetof(struct GraphNodeRotation, rotation), true, LOT_VEC3S, 1, sizeof(Vec3s) },
};
#define LUA_GRAPH_NODE_SCALE_FIELD_COUNT 4
#define LUA_GRAPH_NODE_SCALE_FIELD_COUNT 3
static struct LuaObjectField sGraphNodeScaleFields[LUA_GRAPH_NODE_SCALE_FIELD_COUNT] = {
{ "displayList", LVT_COBJECT_P, offsetof(struct GraphNodeScale, displayList), false, LOT_GFX, 1, sizeof(Gfx*) },
{ "node", LVT_COBJECT, offsetof(struct GraphNodeScale, node), true, LOT_GRAPHNODE, 1, sizeof(struct GraphNode) },
{ "prevScale", LVT_F32, offsetof(struct GraphNodeScale, prevScale), false, LOT_NONE, 1, sizeof(f32) },
{ "scale", LVT_F32, offsetof(struct GraphNodeScale, scale), false, LOT_NONE, 1, sizeof(f32) },
};
#define LUA_GRAPH_NODE_SCALE_XYZ_FIELD_COUNT 3
static struct LuaObjectField sGraphNodeScaleXYZFields[LUA_GRAPH_NODE_SCALE_XYZ_FIELD_COUNT] = {
{ "displayList", LVT_COBJECT_P, offsetof(struct GraphNodeScaleXYZ, displayList), false, LOT_GFX, 1, sizeof(Gfx*) },
{ "node", LVT_COBJECT, offsetof(struct GraphNodeScaleXYZ, node), true, LOT_GRAPHNODE, 1, sizeof(struct GraphNode) },
{ "scale", LVT_COBJECT, offsetof(struct GraphNodeScaleXYZ, scale), true, LOT_VEC3F, 1, sizeof(Vec3f) },
};
#define LUA_GRAPH_NODE_SHADOW_FIELD_COUNT 4
static struct LuaObjectField sGraphNodeShadowFields[LUA_GRAPH_NODE_SHADOW_FIELD_COUNT] = {
{ "node", LVT_COBJECT, offsetof(struct GraphNodeShadow, node), true, LOT_GRAPHNODE, 1, sizeof(struct GraphNode) },
@ -2924,6 +2928,7 @@ struct LuaObjectTable sLuaObjectAutogenTable[LOT_AUTOGEN_MAX - LOT_AUTOGEN_MIN]
{ LOT_GRAPHNODEROOT, sGraphNodeRootFields, LUA_GRAPH_NODE_ROOT_FIELD_COUNT },
{ LOT_GRAPHNODEROTATION, sGraphNodeRotationFields, LUA_GRAPH_NODE_ROTATION_FIELD_COUNT },
{ LOT_GRAPHNODESCALE, sGraphNodeScaleFields, LUA_GRAPH_NODE_SCALE_FIELD_COUNT },
{ LOT_GRAPHNODESCALEXYZ, sGraphNodeScaleXYZFields, LUA_GRAPH_NODE_SCALE_XYZ_FIELD_COUNT },
{ LOT_GRAPHNODESHADOW, sGraphNodeShadowFields, LUA_GRAPH_NODE_SHADOW_FIELD_COUNT },
{ LOT_GRAPHNODESTART, sGraphNodeStartFields, LUA_GRAPH_NODE_START_FIELD_COUNT },
{ LOT_GRAPHNODESWITCHCASE, sGraphNodeSwitchCaseFields, LUA_GRAPH_NODE_SWITCH_CASE_FIELD_COUNT },
@ -3052,6 +3057,7 @@ const char *sLuaLotNames[] = {
[LOT_GRAPHNODEROOT] = "GraphNodeRoot",
[LOT_GRAPHNODEROTATION] = "GraphNodeRotation",
[LOT_GRAPHNODESCALE] = "GraphNodeScale",
[LOT_GRAPHNODESCALEXYZ] = "GraphNodeScaleXYZ",
[LOT_GRAPHNODESHADOW] = "GraphNodeShadow",
[LOT_GRAPHNODESTART] = "GraphNodeStart",
[LOT_GRAPHNODESWITCHCASE] = "GraphNodeSwitchCase",

View file

@ -77,6 +77,7 @@ enum LuaObjectAutogenType {
LOT_GRAPHNODEROOT,
LOT_GRAPHNODEROTATION,
LOT_GRAPHNODESCALE,
LOT_GRAPHNODESCALEXYZ,
LOT_GRAPHNODESHADOW,
LOT_GRAPHNODESTART,
LOT_GRAPHNODESWITCHCASE,

View file

@ -1561,6 +1561,7 @@ char gSmluaConstants[] = ""
"GRAPH_NODE_TYPE_BILLBOARD=0x01A\n"
"GRAPH_NODE_TYPE_DISPLAY_LIST=0x01B\n"
"GRAPH_NODE_TYPE_SCALE=0x01C\n"
"GRAPH_NODE_TYPE_SCALE_XYZ=0x01D\n"
"GRAPH_NODE_TYPE_SHADOW=0x028\n"
"GRAPH_NODE_TYPE_OBJECT_PARENT=0x029\n"
"GRAPH_NODE_TYPE_GENERATED_LIST=(0x02A | GRAPH_NODE_TYPE_FUNCTIONAL)\n"

View file

@ -183,6 +183,12 @@ void* smlua_to_cobject(lua_State* L, int index, u16 lot) {
return NULL;
}
if (cobject->freed) {
LOG_LUA_LINE("smlua_to_cobject received freed pointer.");
gSmLuaConvertSuccess = false;
return NULL;
}
gSmLuaConvertSuccess = true;
return cobject->pointer;
}
@ -387,6 +393,10 @@ bool packet_read_lnt(struct Packet* p, struct LSTNetworkType* lnt) {
///////////////////////////////////////////////////////////////////////////////////////////
inline static uintptr_t smlua_get_pointer_key(void *ptr, u16 lt) {
return (lt * 0x9E3779B97F4A7C15) ^ ((uintptr_t) ptr >> 3);
}
CObject *smlua_push_object(lua_State* L, u16 lot, void* p, void *extraInfo) {
if (p == NULL) {
lua_pushnil(L);
@ -394,7 +404,7 @@ CObject *smlua_push_object(lua_State* L, u16 lot, void* p, void *extraInfo) {
}
LUA_STACK_CHECK_BEGIN_NUM(L, 1);
uintptr_t key = (lot * 0x9E3779B97F4A7C15) ^ ((uintptr_t)p >> 3);
uintptr_t key = smlua_get_pointer_key(p, lot);
lua_rawgeti(L, LUA_REGISTRYINDEX, gSmLuaCObjects);
lua_pushinteger(L, key);
lua_gettable(L, -2);
@ -431,7 +441,7 @@ CPointer *smlua_push_pointer(lua_State* L, u16 lvt, void* p, void *extraInfo) {
}
LUA_STACK_CHECK_BEGIN_NUM(L, 1);
uintptr_t key = (lvt * 0x9E3779B97F4A7C15) ^ ((uintptr_t)p >> 3);
uintptr_t key = smlua_get_pointer_key(p, lvt);
lua_rawgeti(L, LUA_REGISTRYINDEX, gSmLuaCPointers);
lua_pushinteger(L, key);
lua_gettable(L, -2);
@ -840,15 +850,11 @@ void smlua_logline(void) {
}
}
// If an object is freed that Lua has a CObject to,
// Lua is able to use-after-free that pointer
// todo figure out a better way to do this
void smlua_free(void *ptr) {
void smlua_free(void *ptr, u16 lot) {
if (ptr && gLuaState) {
lua_State *L = gLuaState;
LUA_STACK_CHECK_BEGIN(L);
u16 lot = LOT_SURFACE; // Assuming this is a surface
uintptr_t key = lot ^ (uintptr_t) ptr;
uintptr_t key = smlua_get_pointer_key(ptr, lot);
lua_rawgeti(L, LUA_REGISTRYINDEX, gSmLuaCObjects);
lua_pushinteger(L, key);
lua_gettable(L, -2);

View file

@ -62,6 +62,11 @@ void smlua_logline(void);
void smlua_dump_stack(void);
void smlua_dump_globals(void);
void smlua_dump_table(int index);
void smlua_free(void *ptr);
void smlua_free(void *ptr, u16 lot);
#define smlua_free_lot(name, lot) \
static inline void smlua_free_##name(void *ptr) { smlua_free(ptr, lot); }
smlua_free_lot(surface, LOT_SURFACE);
#endif