From 78a2e17d7ce5e217c0bc124c00b447dd1438d3fd Mon Sep 17 00:00:00 2001 From: MysterD Date: Tue, 5 Apr 2022 22:56:03 -0700 Subject: [PATCH] Added support for custom skyboxes in DynOS level gen --- data/dynos.c.h | 2 +- data/dynos.cpp.h | 7 +++ data/dynos_bin_actor.cpp | 7 +++ data/dynos_bin_geo.cpp | 35 +++++++++++++- data/dynos_bin_lvl.cpp | 6 +++ data/dynos_bin_pointer.cpp | 14 ++++++ data/dynos_bin_read.cpp | 15 +++++- data/dynos_bin_texlist.cpp | 93 ++++++++++++++++++++++++++++++++++++++ data/dynos_bin_utils.cpp | 4 ++ data/dynos_c.cpp | 4 ++ data/dynos_misc.cpp | 38 +++++++++++++++- include/geo_commands.h | 10 ++++ src/engine/geo_layout.c | 23 ++++++++++ src/engine/geo_layout.h | 1 + src/game/skybox.c | 10 +++- src/game/skybox.h | 2 + 16 files changed, 264 insertions(+), 7 deletions(-) create mode 100644 data/dynos_bin_texlist.cpp diff --git a/data/dynos.c.h b/data/dynos.c.h index 3f48ace5..232ef212 100644 --- a/data/dynos.c.h +++ b/data/dynos.c.h @@ -34,9 +34,9 @@ void dynos_add_collision(const char *modPath, const char* collisionName); Collision* dynos_collision_get(const char* collisionName); // -- levels -- // - void dynos_add_level(s32 modIndex, const char *modPath, const char* levelName); LevelScript* dynos_level_get(const char* levelName); +void dynos_level_load_background(void *ptr); #endif #endif diff --git a/data/dynos.cpp.h b/data/dynos.cpp.h index 49cfff09..94d3b9bd 100644 --- a/data/dynos.cpp.h +++ b/data/dynos.cpp.h @@ -37,6 +37,7 @@ enum { DATA_TYPE_ROOMS, DATA_TYPE_LIGHT_T, DATA_TYPE_AMBIENT_T, + DATA_TYPE_TEXTURE_LIST, DATA_TYPE_UNUSED, }; @@ -431,6 +432,7 @@ struct GfxData : NoCopy { DataNodes mLightTs; DataNodes mAmbientTs; DataNodes mTextures; + DataNodes mTextureLists; DataNodes mVertices; DataNodes mDisplayLists; DataNodes mGeoLayouts; @@ -722,6 +724,7 @@ void DynOS_Lvl_Add(s32 modIndex, const SysPath &aPackFolder, const char *aLevelN LevelScript* DynOS_Lvl_Get(const char* levelName); s32 DynOS_Lvl_GetModIndex(void* levelScript); DataNode *DynOS_Lvl_Texture_Get(void *aPtr); +void DynOS_Lvl_Load_Background(void *aPtr); // // Warps @@ -812,6 +815,10 @@ void DynOS_Tex_Write(FILE* aFile, GfxData* aGfxData, DataNode *aNode); void DynOS_Tex_Load(FILE *aFile, GfxData *aGfxData); void DynOS_Tex_ConvertTextureDataToPng(GfxData *aGfxData, TexData* aTexture); +DataNode* DynOS_TexList_Parse(GfxData* aGfxData, DataNode* aNode); +void DynOS_TexList_Write(FILE* aFile, GfxData* aGfxData, DataNode *aNode); +DataNode* DynOS_TexList_Load(FILE *aFile, GfxData *aGfxData); + DataNode* DynOS_Vtx_Parse(GfxData* aGfxData, DataNode* aNode); void DynOS_Vtx_Write(FILE* aFile, GfxData* aGfxData, DataNode *aNode); void DynOS_Vtx_Load(FILE *aFile, GfxData *aGfxData); diff --git a/data/dynos_bin_actor.cpp b/data/dynos_bin_actor.cpp index ea6784c5..eea09cca 100644 --- a/data/dynos_bin_actor.cpp +++ b/data/dynos_bin_actor.cpp @@ -41,6 +41,11 @@ static bool DynOS_Actor_WriteBinary(const SysPath &aOutputFilename, GfxData *aGf DynOS_Tex_Write(_File, aGfxData, _Node); } } + for (auto &_Node : aGfxData->mTextureLists) { + if (_Node->mLoadIndex == i) { + DynOS_TexList_Write(_File, aGfxData, _Node); + } + } for (auto &_Node : aGfxData->mVertices) { if (_Node->mLoadIndex == i) { DynOS_Vtx_Write(_File, aGfxData, _Node); @@ -101,6 +106,7 @@ GfxData *DynOS_Actor_LoadFromBinary(const SysPath &aPackFolder, const char *aAct case DATA_TYPE_LIGHT_T: DynOS_LightT_Load (_File, _GfxData); break; case DATA_TYPE_AMBIENT_T: DynOS_AmbientT_Load (_File, _GfxData); break; case DATA_TYPE_TEXTURE: DynOS_Tex_Load (_File, _GfxData); break; + case DATA_TYPE_TEXTURE_LIST: DynOS_TexList_Load (_File, _GfxData); break; case DATA_TYPE_VERTEX: DynOS_Vtx_Load (_File, _GfxData); break; case DATA_TYPE_DISPLAY_LIST: DynOS_Gfx_Load (_File, _GfxData); break; case DATA_TYPE_GEO_LAYOUT: DynOS_Geo_Load (_File, _GfxData); break; @@ -202,6 +208,7 @@ static void DynOS_Actor_Generate(const SysPath &aPackFolder, ArraymLightTs); ClearGfxDataNodes(_GfxData->mAmbientTs); ClearGfxDataNodes(_GfxData->mTextures); + ClearGfxDataNodes(_GfxData->mTextureLists); ClearGfxDataNodes(_GfxData->mVertices); ClearGfxDataNodes(_GfxData->mDisplayLists); ClearGfxDataNodes(_GfxData->mGeoLayouts); diff --git a/data/dynos_bin_geo.cpp b/data/dynos_bin_geo.cpp index 5442b58e..b5d1b9a7 100644 --- a/data/dynos_bin_geo.cpp +++ b/data/dynos_bin_geo.cpp @@ -282,7 +282,6 @@ static void ParseGeoSymbol(GfxData* aGfxData, DataNode* aNode, GeoLay geo_symbol_3(GEO_SHADOW, 0); geo_symbol_0(GEO_RENDER_OBJ); geo_symbol_2(GEO_ASM, 1); - geo_symbol_2(GEO_BACKGROUND, 1); geo_symbol_1(GEO_BACKGROUND_COLOR, 0); geo_symbol_0(GEO_NOP_1A); geo_symbol_5(GEO_HELD_OBJECT, 2); @@ -345,6 +344,40 @@ static void ParseGeoSymbol(GfxData* aGfxData, DataNode* aNode, GeoLay return; } + // Background + if (_Symbol == "GEO_BACKGROUND") { + // check if this is a custom background + const String& backgroundName = aNode->mTokens[aTokenIndex]; + DataNode* node = NULL; + for (auto& _Node : aGfxData->mTextureLists) { + if (backgroundName == _Node->mName) { + node = _Node; + break; + } + } + + if (node) { + // custom background cmd + node = DynOS_TexList_Parse(aGfxData, node); + aTokenIndex++; // skip background name + s64 func = ParseGeoSymbolArg(aGfxData, aNode, aTokenIndex); + aGfxData->mPointerList.Add(aHead + 1); + aGfxData->mPointerList.Add(aHead + 2); + GeoLayout _Gl[] = { GEO_BACKGROUND_EXT(node, func) }; + memcpy(aHead, _Gl, sizeof(_Gl)); + aHead += (sizeof(_Gl) / sizeof(_Gl[0])); + } else { + // regular background cmd + s64 background = ParseGeoSymbolArg(aGfxData, aNode, aTokenIndex); + s64 func = ParseGeoSymbolArg(aGfxData, aNode, aTokenIndex); + aGfxData->mPointerList.Add(aHead + 1); + GeoLayout _Gl[] = { GEO_BACKGROUND(background, func) }; + memcpy(aHead, _Gl, sizeof(_Gl)); + aHead += (sizeof(_Gl) / sizeof(_Gl[0])); + } + return; + } + // Unknown PrintError(" ERROR: Unknown geo symbol: %s", _Symbol.begin()); } diff --git a/data/dynos_bin_lvl.cpp b/data/dynos_bin_lvl.cpp index 78e8df4a..a37f7be1 100644 --- a/data/dynos_bin_lvl.cpp +++ b/data/dynos_bin_lvl.cpp @@ -1848,6 +1848,11 @@ static bool DynOS_Lvl_WriteBinary(const SysPath &aOutputFilename, GfxData *aGfxD DynOS_Tex_Write(_File, aGfxData, _Node); } } + for (auto &_Node : aGfxData->mTextureLists) { + if (_Node->mLoadIndex == i) { + DynOS_TexList_Write(_File, aGfxData, _Node); + } + } for (auto &_Node : aGfxData->mVertices) { if (_Node->mLoadIndex == i) { DynOS_Vtx_Write(_File, aGfxData, _Node); @@ -1950,6 +1955,7 @@ GfxData *DynOS_Lvl_LoadFromBinary(const SysPath &aPackFolder, const char *aLevel case DATA_TYPE_LIGHT_T: DynOS_LightT_Load (_File, _GfxData); break; case DATA_TYPE_AMBIENT_T: DynOS_AmbientT_Load (_File, _GfxData); break; case DATA_TYPE_TEXTURE: DynOS_Tex_Load (_File, _GfxData); break; + case DATA_TYPE_TEXTURE_LIST: DynOS_TexList_Load (_File, _GfxData); break; case DATA_TYPE_VERTEX: DynOS_Vtx_Load (_File, _GfxData); break; case DATA_TYPE_DISPLAY_LIST: DynOS_Gfx_Load (_File, _GfxData); break; case DATA_TYPE_GEO_LAYOUT: DynOS_Geo_Load (_File, _GfxData); break; diff --git a/data/dynos_bin_pointer.cpp b/data/dynos_bin_pointer.cpp index bc9ac46c..834cf600 100644 --- a/data/dynos_bin_pointer.cpp +++ b/data/dynos_bin_pointer.cpp @@ -71,6 +71,13 @@ static PointerData GetDataFromPointer(const void* aPtr, GfxData* aGfxData) { } } + // Texture Lists + for (auto& _Node : aGfxData->mTextureLists) { + if (_Node == aPtr) { + return { _Node->mName, 0 }; + } + } + // Display lists for (auto& _Node : aGfxData->mDisplayLists) { if (_Node == aPtr) { @@ -284,6 +291,13 @@ static void *GetPointerFromData(GfxData *aGfxData, const String &aPtrName, u32 a } } + // Texture Lists + for (auto& _Node : aGfxData->mTextureLists) { + if (_Node->mName == aPtrName) { + return (void *) _Node; + } + } + // Display lists for (auto &_Node : aGfxData->mDisplayLists) { if (_Node->mName == aPtrName) { diff --git a/data/dynos_bin_read.cpp b/data/dynos_bin_read.cpp index c0b65ee9..5dba78b6 100644 --- a/data/dynos_bin_read.cpp +++ b/data/dynos_bin_read.cpp @@ -137,7 +137,7 @@ void DynOS_Read_Source(GfxData *aGfxData, const SysPath &aFilename) { // Scanning data type if (_DataType == DATA_TYPE_NONE) { - // skip includes and externs + // skip entire line of includes and externs if (!strncmp(c, "#include", 8) || !strncmp(c, "extern ", 7)) { while (*c != '\n' && *c != '\0') { c++; @@ -184,7 +184,12 @@ void DynOS_Read_Source(GfxData *aGfxData, const SysPath &aFilename) { ? DATA_TYPE_ROOMS : DATA_TYPE_TEXTURE; } else if (_Buffer == "Texture") { - _DataType = DATA_TYPE_TEXTURE; + // check if this is a 'Texture*' or a 'Texture' + char* peek = c; + while(*peek == ' ') { peek++; } + _DataType = (*peek == '*') + ? DATA_TYPE_TEXTURE_LIST + : DATA_TYPE_TEXTURE; } else if (_Buffer == "Vtx") { _DataType = DATA_TYPE_VERTEX; } else if (_Buffer == "Gfx") { @@ -218,6 +223,11 @@ void DynOS_Read_Source(GfxData *aGfxData, const SysPath &aFilename) { _Buffer.Add(*c); } + // Skip const + else if (_Buffer == "const") { + _Buffer.Clear(); + } + // Adding new data node else if (_Buffer.Length() != 0) { switch (_DataType) { @@ -225,6 +235,7 @@ void DynOS_Read_Source(GfxData *aGfxData, const SysPath &aFilename) { case DATA_TYPE_LIGHT_T: AppendNewNode(aGfxData, aGfxData->mLightTs, _Buffer, pDataName, pDataTokens); break; case DATA_TYPE_AMBIENT_T: AppendNewNode(aGfxData, aGfxData->mAmbientTs, _Buffer, pDataName, pDataTokens); break; case DATA_TYPE_TEXTURE: AppendNewNode(aGfxData, aGfxData->mTextures, _Buffer, pDataName, pDataTokens); break; + case DATA_TYPE_TEXTURE_LIST: AppendNewNode(aGfxData, aGfxData->mTextureLists, _Buffer, pDataName, pDataTokens); break; case DATA_TYPE_VERTEX: AppendNewNode(aGfxData, aGfxData->mVertices, _Buffer, pDataName, pDataTokens); break; case DATA_TYPE_DISPLAY_LIST: AppendNewNode(aGfxData, aGfxData->mDisplayLists, _Buffer, pDataName, pDataTokens); break; case DATA_TYPE_GEO_LAYOUT: AppendNewNode(aGfxData, aGfxData->mGeoLayouts, _Buffer, pDataName, pDataTokens); break; diff --git a/data/dynos_bin_texlist.cpp b/data/dynos_bin_texlist.cpp new file mode 100644 index 00000000..9d166e9b --- /dev/null +++ b/data/dynos_bin_texlist.cpp @@ -0,0 +1,93 @@ +#include "dynos.cpp.h" + + ///////////// + // Parsing // +///////////// + +static TexData* ParseTexListSymbol(GfxData* aGfxData, DataNode* aNode, String& aToken) { + // Textures + for (auto& _Node : aGfxData->mTextures) { + if (aToken == _Node->mName) { + return DynOS_Tex_Parse(aGfxData, _Node)->mData; + } + } + + // Unknown + PrintError(" ERROR: Unknown texlist arg: %s", aToken.begin()); + return NULL; +} + +DataNode* DynOS_TexList_Parse(GfxData* aGfxData, DataNode* aNode) { + if (aNode->mData) return aNode; + + // TexList data + aNode->mSize = (u32) (aNode->mTokens.Count()); + + aNode->mData = New(aNode->mSize); + for (u32 i = 0; i != aNode->mSize; ++i) { + aNode->mData[i] = ParseTexListSymbol(aGfxData, aNode, aNode->mTokens[i]); + aGfxData->mPointerList.Add(&aNode->mData[i]); + } + aNode->mLoadIndex = aGfxData->mLoadIndex++; + return aNode; +} + + ///////////// + // Writing // +///////////// + +void DynOS_TexList_Write(FILE* aFile, GfxData* aGfxData, DataNode *aNode) { + if (!aNode->mData) return; + + // Name + WriteBytes(aFile, DATA_TYPE_TEXTURE_LIST); + aNode->mName.Write(aFile); + + // Data + WriteBytes(aFile, aNode->mSize); + for (u32 i = 0; i != aNode->mSize; ++i) { + // find node + bool found = false; + for (auto& _Node : aGfxData->mTextures) { + if (_Node->mData == aNode->mData[i]) { + DynOS_Pointer_Write(aFile, (const void *) (_Node), aGfxData); + found = true; + break; + } + } + if (!found) { + PrintError("Could not write texture in texlist"); + } + } +} + + ///////////// + // Reading // +///////////// + +DataNode* DynOS_TexList_Load(FILE *aFile, GfxData *aGfxData) { + DataNode *_Node = New>(); + + // Name + _Node->mName.Read(aFile); + + // Data + _Node->mSize = ReadBytes(aFile); + _Node->mData = New(_Node->mSize); + for (u32 i = 0; i != _Node->mSize; ++i) { + u32 _Value = ReadBytes(aFile); + void *_Ptr = DynOS_Pointer_Load(aFile, aGfxData, _Value, false); + if (_Ptr == NULL) { + PrintError("Could not read texture in texlist"); + } else { + _Node->mData[i] = ((DataNode*)_Ptr)->mData; + } + } + + // Add it + if (aGfxData != NULL) { + aGfxData->mTextureLists.Add(_Node); + } + + return _Node; +} diff --git a/data/dynos_bin_utils.cpp b/data/dynos_bin_utils.cpp index f27086ad..a15ac011 100644 --- a/data/dynos_bin_utils.cpp +++ b/data/dynos_bin_utils.cpp @@ -22,6 +22,10 @@ void DynOS_Gfx_Free(GfxData* aGfxData) { Delete(_Node->mData); Delete(_Node); } + for (auto& _Node : aGfxData->mTextureLists) { + Delete(_Node->mData); + Delete(_Node); + } for (auto& _Node : aGfxData->mVertices) { Delete(_Node->mData); Delete(_Node); diff --git a/data/dynos_c.cpp b/data/dynos_c.cpp index b23ec32e..73e8c173 100644 --- a/data/dynos_c.cpp +++ b/data/dynos_c.cpp @@ -107,4 +107,8 @@ LevelScript* dynos_level_get(const char* levelName) { return DynOS_Lvl_Get(levelName); } +void dynos_level_load_background(void *ptr) { + DynOS_Lvl_Load_Background(ptr); +} + } diff --git a/data/dynos_misc.cpp b/data/dynos_misc.cpp index 2456fa70..7d21e34a 100644 --- a/data/dynos_misc.cpp +++ b/data/dynos_misc.cpp @@ -14,6 +14,8 @@ extern "C" { #include "game/object_list_processor.h" #include "game/behavior_actions.h" #include "game/rendering_graph_node.h" +#include "game/skybox.h" + #include "actors/common0.h" #include "actors/common1.h" #include "actors/group0.h" @@ -36,6 +38,7 @@ extern "C" { #include "actors/group17.h" #include "actors/custom0.h" #include "actors/zcustom0.h" + #include "levels/bbh/header.h" #include "levels/bitdw/header.h" #include "levels/bitfs/header.h" @@ -1168,4 +1171,37 @@ DataNode *DynOS_Lvl_Texture_Get(void *aPtr) { } } return NULL; -} \ No newline at end of file +} + +void DynOS_Lvl_Load_Background(void *aPtr) { + // ensure this texture list exists + GfxData* foundGfxData = NULL; + DataNode* foundList = NULL; + for (auto& script : sDynosCustomLevelScripts) { + auto &textureLists = script.second->mTextureLists; + for (auto& textureList : textureLists) { + if (textureList == aPtr) { + foundGfxData = script.second; + foundList = textureList; + goto double_break; + } + } + } +double_break: + + if (foundList == NULL) { + Print("Could not find custom background"); + return; + } + + // Load up custom background + for (s32 i = 0; i < 80; i++) { + // find texture + for (auto& tex : foundGfxData->mTextures) { + if (tex->mData == foundList->mData[i]) { + gCustomSkyboxPtrList[i] = (Texture*)tex; + break; + } + } + } +} diff --git a/include/geo_commands.h b/include/geo_commands.h index 432ec1fd..2c058f87 100644 --- a/include/geo_commands.h +++ b/include/geo_commands.h @@ -21,6 +21,7 @@ #define BACKGROUND_GREEN_SKY 7 #define BACKGROUND_ABOVE_CLOUDS 8 #define BACKGROUND_PURPLE_SKY 9 +#define BACKGROUND_CUSTOM 10 // geo layout macros @@ -429,4 +430,13 @@ #define GEO_CULLING_RADIUS(cullingRadius) \ CMD_BBH(0x20, 0x00, cullingRadius) + +/** + * 0x21: Custom backgrounds (skyboxes) + */ +#define GEO_BACKGROUND_EXT(background, function) \ + CMD_BBH(0x21, 0x00, 0x00), \ + CMD_PTR(background), \ + CMD_PTR(function) + #endif // GEO_COMMANDS_H diff --git a/src/engine/geo_layout.c b/src/engine/geo_layout.c index 04b8b324..fba334b5 100644 --- a/src/engine/geo_layout.c +++ b/src/engine/geo_layout.c @@ -5,6 +5,7 @@ #include "math_util.h" #include "game/memory.h" #include "graph_node.h" +#include "geo_commands.h" typedef void (*GeoLayoutCommandProc)(void); @@ -42,6 +43,8 @@ GeoLayoutCommandProc GeoLayoutJumpTable[] = { geo_layout_cmd_nop2, geo_layout_cmd_nop3, geo_layout_cmd_node_culling_radius, + // coop + geo_layout_cmd_node_background_ext, }; struct GraphNode gObjParentGraphNode; @@ -767,6 +770,26 @@ void geo_layout_cmd_node_culling_radius(void) { gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT; } +/* + 0x21: Create custom background scene graph node +*/ +void geo_layout_cmd_node_background_ext(void) { + struct GraphNodeBackground *graphNode; + + void* bgPtr = cur_geo_cmd_ptr(0x04); + dynos_level_load_background(bgPtr); + + graphNode = init_graph_node_background( + gGraphNodePool, NULL, + BACKGROUND_CUSTOM, // background ID, or RGBA5551 color if asm function is null + (GraphNodeFunc) cur_geo_cmd_ptr(0x08), // asm function + 0); + + register_scene_graph_node(&graphNode->fnNode.node); + + gGeoLayoutCommand += 0x0C << CMD_SIZE_SHIFT; +} + struct GraphNode *process_geo_layout(struct AllocOnlyPool *pool, void *segptr) { // set by register_scene_graph_node when gCurGraphNodeIndex is 0 // and gCurRootGraphNode is NULL diff --git a/src/engine/geo_layout.h b/src/engine/geo_layout.h index 6211e141..f7d408b1 100644 --- a/src/engine/geo_layout.h +++ b/src/engine/geo_layout.h @@ -81,6 +81,7 @@ void geo_layout_cmd_nop(void); void geo_layout_cmd_copy_view(void); void geo_layout_cmd_node_held_obj(void); void geo_layout_cmd_node_culling_radius(void); +void geo_layout_cmd_node_background_ext(void); struct GraphNode *process_geo_layout(struct AllocOnlyPool *a0, void *segptr); diff --git a/src/game/skybox.c b/src/game/skybox.c index 6c037935..750d6f0b 100644 --- a/src/game/skybox.c +++ b/src/game/skybox.c @@ -72,6 +72,7 @@ extern SkyboxTexture clouds_skybox_ptrlist; extern SkyboxTexture ssl_skybox_ptrlist; extern SkyboxTexture water_skybox_ptrlist; extern SkyboxTexture wdw_skybox_ptrlist; +Texture* gCustomSkyboxPtrList[80] = { NULL }; SkyboxTexture *sSkyboxTextures[10] = { &water_skybox_ptrlist, @@ -223,8 +224,13 @@ void draw_skybox_tile_grid(Gfx **dlist, s8 background, s8 player, s8 colorIndex) s32 tileIndex = sSkyBoxInfo[player].upperLeftTile + row * SKYBOX_COLS + col; // UGLY HACK: if the camera moves weird after a level transition this can go too high if (tileIndex > 79) { tileIndex = 79; } - const u8 *const texture = - (*(SkyboxTexture *) segmented_to_virtual(sSkyboxTextures[background]))[tileIndex]; + Texture* texture = NULL; + if (background >= 10) { + texture = gCustomSkyboxPtrList[tileIndex]; + } else { + texture = (Texture*)(*(SkyboxTexture *) segmented_to_virtual(sSkyboxTextures[background]))[tileIndex]; + } + Vtx *vertices = make_skybox_rect(tileIndex, colorIndex); gLoadBlockTexture((*dlist)++, 32, 32, G_IM_FMT_RGBA, texture); diff --git a/src/game/skybox.h b/src/game/skybox.h index 39220447..e70b8d96 100644 --- a/src/game/skybox.h +++ b/src/game/skybox.h @@ -4,6 +4,8 @@ #include #include +extern Texture* gCustomSkyboxPtrList[]; + Gfx *create_skybox_facing_camera(s8 player, s8 background, f32 fov, f32 posX, f32 posY, f32 posZ, f32 focX, f32 focY, f32 focZ);