mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2025-10-30 08:01:01 +00:00
duplicate every instance of a display list (#720)
Co-authored-by: PeachyPeachSM64 <72323920+PeachyPeachSM64@users.noreply.github.com>
This commit is contained in:
parent
14692e549b
commit
d81512680b
8 changed files with 115 additions and 13 deletions
|
|
@ -79,6 +79,7 @@ struct GraphNode* dynos_model_load_dl(u32* aId, enum ModelPool aModelPool, u8 aL
|
|||
struct GraphNode* dynos_model_store_geo(u32* aId, enum ModelPool aModelPool, void* aAsset, struct GraphNode* aGraphNode);
|
||||
struct GraphNode* dynos_model_get_geo(u32 aId);
|
||||
void dynos_model_overwrite_slot(u32 srcSlot, u32 dstSlot);
|
||||
Gfx *dynos_model_duplicate_displaylist(Gfx* gfx);
|
||||
u32 dynos_model_get_id_from_asset(void* aAsset);
|
||||
u32 dynos_model_get_id_from_graph_node(struct GraphNode* aGraphNode);
|
||||
void dynos_model_clear_pool(enum ModelPool aModelPool);
|
||||
|
|
|
|||
|
|
@ -972,6 +972,7 @@ struct GraphNode* DynOS_Model_GetGeo(u32 aId);
|
|||
u32 DynOS_Model_GetIdFromAsset(void* asset);
|
||||
u32 DynOS_Model_GetIdFromGraphNode(struct GraphNode* aNode);
|
||||
void DynOS_Model_OverwriteSlot(u32 srcSlot, u32 dstSlot);
|
||||
Gfx *DynOS_Model_Duplicate_DisplayList(Gfx* aGfx);
|
||||
void DynOS_Model_ClearPool(enum ModelPool aModelPool);
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -270,6 +270,10 @@ void dynos_model_overwrite_slot(u32 srcSlot, u32 dstSlot) {
|
|||
DynOS_Model_OverwriteSlot(srcSlot, dstSlot);
|
||||
}
|
||||
|
||||
Gfx *dynos_model_duplicate_displaylist(Gfx* gfx) {
|
||||
return DynOS_Model_Duplicate_DisplayList(gfx);
|
||||
}
|
||||
|
||||
// -- other -- //
|
||||
|
||||
void dynos_mod_shutdown(void) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ extern "C" {
|
|||
#include "engine/graph_node.h"
|
||||
#include "model_ids.h"
|
||||
#include "pc/lua/utils/smlua_model_utils.h"
|
||||
#include "engine/display_list.h"
|
||||
}
|
||||
|
||||
enum ModelLoadType {
|
||||
|
|
@ -20,6 +21,7 @@ struct ModelInfo {
|
|||
void* asset;
|
||||
struct GraphNode* graphNode;
|
||||
enum ModelPool modelPool;
|
||||
std::vector<void*> *duplicates;
|
||||
};
|
||||
|
||||
static struct DynamicPool* sModelPools[MODEL_POOL_MAX] = { 0 };
|
||||
|
|
@ -28,6 +30,10 @@ static std::map<void*, struct ModelInfo> sAssetMap[MODEL_POOL_MAX];
|
|||
static std::map<u32, std::vector<struct ModelInfo>> sIdMap;
|
||||
static std::map<u32, u32> sOverwriteMap;
|
||||
|
||||
// An array of display list and/or vertex buffer duplicates for the current model processed in process_geo_layout
|
||||
static std::vector<void*> *sCurrModelDuplicates = nullptr;
|
||||
static std::vector<void*> sScheduledFree[MODEL_POOL_MAX];
|
||||
|
||||
static u32 find_empty_id(bool aIsPermanent) {
|
||||
u32 id = aIsPermanent ? 9999 : VANILLA_ID_END + 1;
|
||||
s8 dir = aIsPermanent ? -1 : 1;
|
||||
|
|
@ -90,6 +96,8 @@ static struct GraphNode* DynOS_Model_LoadCommonInternal(u32* aId, enum ModelPool
|
|||
return found.graphNode;
|
||||
}
|
||||
|
||||
sCurrModelDuplicates = new std::vector<void*>();
|
||||
|
||||
// load geo
|
||||
struct GraphNode* node = NULL;
|
||||
switch (mlt) {
|
||||
|
|
@ -103,7 +111,14 @@ static struct GraphNode* DynOS_Model_LoadCommonInternal(u32* aId, enum ModelPool
|
|||
node = aGraphNode;
|
||||
break;
|
||||
}
|
||||
if (!node) { return NULL; }
|
||||
if (!node) {
|
||||
for (auto &duplicate : *sCurrModelDuplicates) {
|
||||
free(duplicate);
|
||||
}
|
||||
delete sCurrModelDuplicates;
|
||||
sCurrModelDuplicates = nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// figure out id
|
||||
if (!*aId) { *aId = find_empty_id(aModelPool == MODEL_POOL_PERMANENT); }
|
||||
|
|
@ -113,8 +128,10 @@ static struct GraphNode* DynOS_Model_LoadCommonInternal(u32* aId, enum ModelPool
|
|||
.id = *aId,
|
||||
.asset = aAsset,
|
||||
.graphNode = node,
|
||||
.modelPool = aModelPool
|
||||
.modelPool = aModelPool,
|
||||
.duplicates = sCurrModelDuplicates,
|
||||
};
|
||||
sCurrModelDuplicates = nullptr;
|
||||
|
||||
// store in maps
|
||||
sIdMap[*aId].push_back(info);
|
||||
|
|
@ -224,12 +241,53 @@ void DynOS_Model_OverwriteSlot(u32 srcSlot, u32 dstSlot) {
|
|||
sOverwriteMap[srcSlot] = dstSlot;
|
||||
}
|
||||
|
||||
// Display lists need to be duplicated so they can be modified by mods
|
||||
// also to prevent trying to write to read only memory for vanilla display lists
|
||||
Gfx *DynOS_Model_Duplicate_DisplayList(Gfx* aGfx) {
|
||||
if (!aGfx) { return nullptr; }
|
||||
|
||||
u32 size = gfx_get_size(aGfx) * sizeof(Gfx);
|
||||
Gfx *gfxDuplicate = (Gfx *) malloc(size);
|
||||
memcpy(gfxDuplicate, aGfx, size);
|
||||
|
||||
// Look for other display lists or vertices
|
||||
for (u32 i = 0; i < size / sizeof(Gfx); i++) {
|
||||
Gfx *cmd = gfxDuplicate + i;
|
||||
u32 op = cmd->words.w0 >> 24;
|
||||
|
||||
// Duplicate referenced display lists
|
||||
if (op == G_DL) {
|
||||
cmd->words.w1 = (uintptr_t) DynOS_Model_Duplicate_DisplayList((Gfx *) cmd->words.w1);
|
||||
if (C0(cmd, 16, 1) == G_DL_NOPUSH) { break; } // This is a branch (jump), end of display list
|
||||
}
|
||||
|
||||
// Duplicate referenced vertices
|
||||
if (op == G_VTX) {
|
||||
u32 size = C0(cmd, 12, 8) * sizeof(Vtx);
|
||||
Vtx *vtxDuplicate = (Vtx *) malloc(size);
|
||||
memcpy(vtxDuplicate, (Vtx *) cmd->words.w1, size);
|
||||
cmd->words.w1 = (uintptr_t) vtxDuplicate;
|
||||
sCurrModelDuplicates->push_back(vtxDuplicate);
|
||||
}
|
||||
}
|
||||
|
||||
sCurrModelDuplicates->push_back(gfxDuplicate);
|
||||
|
||||
return gfxDuplicate;
|
||||
}
|
||||
|
||||
void DynOS_Model_ClearPool(enum ModelPool aModelPool) {
|
||||
if (!sModelPools[aModelPool]) { return; }
|
||||
|
||||
// schedule pool to be freed
|
||||
dynamic_pool_free_pool(sModelPools[aModelPool]);
|
||||
|
||||
// free scheduled duplicates
|
||||
for (auto &duplicate : sScheduledFree[aModelPool]) {
|
||||
free(duplicate);
|
||||
}
|
||||
sScheduledFree[aModelPool].clear();
|
||||
|
||||
// clear overwrite
|
||||
if (aModelPool == MODEL_POOL_LEVEL) {
|
||||
sOverwriteMap.clear();
|
||||
|
|
@ -257,6 +315,15 @@ void DynOS_Model_ClearPool(enum ModelPool aModelPool) {
|
|||
info2++;
|
||||
}
|
||||
}
|
||||
|
||||
// schedule duplicates to be freed
|
||||
if (info.duplicates) {
|
||||
for (auto &duplicate : *info.duplicates) {
|
||||
sScheduledFree[aModelPool].push_back(duplicate);
|
||||
}
|
||||
delete info.duplicates;
|
||||
info.duplicates = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
assetMap.clear();
|
||||
|
|
|
|||
25
src/engine/display_list.c
Normal file
25
src/engine/display_list.c
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#include "display_list.h"
|
||||
|
||||
// Get the size of a display list by iterating
|
||||
// until gsSPEndDisplayList or gsSPBranchList is found
|
||||
u32 gfx_get_size(const Gfx* gfx) {
|
||||
for (u32 i = 0;;) {
|
||||
u32 op = (gfx + i)->words.w0 >> 24;
|
||||
u32 cmdSize = 1;
|
||||
switch (op) {
|
||||
case G_DL:
|
||||
if (C0(gfx + i, 16, 1) == G_DL_NOPUSH) { return i + 1; } // For displaylists that end with branches (jumps)
|
||||
break;
|
||||
case G_ENDDL:
|
||||
return i + 1;
|
||||
case G_TEXRECT:
|
||||
case G_TEXRECTFLIP:
|
||||
cmdSize = 3;
|
||||
break;
|
||||
case G_FILLRECT:
|
||||
cmdSize = 2;
|
||||
break;
|
||||
}
|
||||
i += cmdSize;
|
||||
}
|
||||
}
|
||||
5
src/engine/display_list.h
Normal file
5
src/engine/display_list.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#include <PR/gbi.h>
|
||||
|
||||
#define C0(cmd, pos, width) (((cmd)->words.w0 >> (pos)) & ((1U << width) - 1))
|
||||
|
||||
u32 gfx_get_size(const Gfx* gfx);
|
||||
|
|
@ -235,7 +235,7 @@ init_graph_node_translation_rotation(struct DynamicPool *pool,
|
|||
vec3s_copy(graphNode->translation, translation);
|
||||
vec3s_copy(graphNode->rotation, rotation);
|
||||
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
|
||||
graphNode->displayList = displayList;
|
||||
graphNode->displayList = dynos_model_duplicate_displaylist(displayList);
|
||||
}
|
||||
|
||||
return graphNode;
|
||||
|
|
@ -257,7 +257,7 @@ struct GraphNodeTranslation *init_graph_node_translation(struct DynamicPool *poo
|
|||
|
||||
vec3s_copy(graphNode->translation, translation);
|
||||
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
|
||||
graphNode->displayList = displayList;
|
||||
graphNode->displayList = dynos_model_duplicate_displaylist(displayList);
|
||||
}
|
||||
|
||||
return graphNode;
|
||||
|
|
@ -278,7 +278,7 @@ struct GraphNodeRotation *init_graph_node_rotation(struct DynamicPool *pool,
|
|||
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ROTATION);
|
||||
vec3s_copy(graphNode->rotation, rotation);
|
||||
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
|
||||
graphNode->displayList = displayList;
|
||||
graphNode->displayList = dynos_model_duplicate_displaylist(displayList);
|
||||
}
|
||||
|
||||
return graphNode;
|
||||
|
|
@ -299,7 +299,7 @@ struct GraphNodeScale *init_graph_node_scale(struct DynamicPool *pool,
|
|||
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
|
||||
graphNode->scale = scale;
|
||||
graphNode->prevScale = scale;
|
||||
graphNode->displayList = displayList;
|
||||
graphNode->displayList = dynos_model_duplicate_displaylist(displayList);
|
||||
}
|
||||
|
||||
return graphNode;
|
||||
|
|
@ -369,7 +369,7 @@ struct GraphNodeAnimatedPart *init_graph_node_animated_part(struct DynamicPool *
|
|||
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ANIMATED_PART);
|
||||
vec3s_copy(graphNode->translation, translation);
|
||||
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
|
||||
graphNode->displayList = displayList;
|
||||
graphNode->displayList = dynos_model_duplicate_displaylist(displayList);
|
||||
}
|
||||
|
||||
return graphNode;
|
||||
|
|
@ -390,7 +390,7 @@ struct GraphNodeBillboard *init_graph_node_billboard(struct DynamicPool *pool,
|
|||
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_BILLBOARD);
|
||||
vec3s_copy(graphNode->translation, translation);
|
||||
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
|
||||
graphNode->displayList = displayList;
|
||||
graphNode->displayList = dynos_model_duplicate_displaylist(displayList);
|
||||
}
|
||||
|
||||
return graphNode;
|
||||
|
|
@ -409,7 +409,7 @@ struct GraphNodeDisplayList *init_graph_node_display_list(struct DynamicPool *po
|
|||
if (graphNode != NULL) {
|
||||
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_DISPLAY_LIST);
|
||||
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
|
||||
graphNode->displayList = displayList;
|
||||
graphNode->displayList = dynos_model_duplicate_displaylist(displayList);
|
||||
}
|
||||
|
||||
return graphNode;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "game/rendering_graph_node.h"
|
||||
#include "game/skybox.h"
|
||||
#include "geo_commands.h"
|
||||
#include "engine/display_list.h"
|
||||
|
||||
void set_override_fov(f32 fov) {
|
||||
gOverrideFOV = fov;
|
||||
|
|
@ -109,8 +110,6 @@ void set_skybox_color(u8 index, u8 value) {
|
|||
|
||||
///
|
||||
|
||||
#define C0(pos, width) ((cmd->words.w0 >> (pos)) & ((1U << width) - 1))
|
||||
|
||||
// Assumes the current microcode is Fast3DEX2 Extended (default for pc port)
|
||||
void gfx_parse(Gfx* cmd, LuaFunction func) {
|
||||
if (!cmd) { return; }
|
||||
|
|
@ -121,7 +120,7 @@ void gfx_parse(Gfx* cmd, LuaFunction func) {
|
|||
u32 op = cmd->words.w0 >> 24;
|
||||
switch (op) {
|
||||
case G_DL:
|
||||
if (C0(16, 1) == 0) {
|
||||
if (C0(cmd, 16, 1) == G_DL_PUSH) {
|
||||
gfx_parse((Gfx *) cmd->words.w1, func);
|
||||
} else {
|
||||
cmd = (Gfx *) cmd->words.w1;
|
||||
|
|
@ -160,7 +159,7 @@ Vtx *gfx_get_vtx(Gfx* cmd, u16 offset) {
|
|||
if (op != G_VTX) { return NULL; }
|
||||
if (cmd->words.w1 == 0) { return NULL; }
|
||||
|
||||
u16 numVertices = C0(12, 8);
|
||||
u16 numVertices = C0(cmd, 12, 8);
|
||||
if (offset >= numVertices) { return NULL; }
|
||||
|
||||
return &((Vtx *) cmd->words.w1)[offset];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue