diff --git a/src/engine/ModelLoader.cpp b/src/engine/ModelLoader.cpp new file mode 100644 index 000000000..1c41a8ca9 --- /dev/null +++ b/src/engine/ModelLoader.cpp @@ -0,0 +1,94 @@ +#include "ModelLoader.h" +#include "port/Engine.h" +#include "engine/courses/Course.h" + +extern "C" { +#include "memory.h" +} + +void ModelLoader::Add(LoadModelList list) { + _deferredList.push_back(list); +} + +void ModelLoader::Load() { + if (_hasRan) { // Ensure this does not get ran multiple times + printf("ModelLoader.cpp has already been ran once.\n"); + return; + } + _hasRan = true; + + // Set to track processed courses + std::unordered_set processedCourses; + + for (auto& list : _deferredList) { + // Check if the course has already been processed + if (processedCourses.find(list.course) != processedCourses.end()) { + continue; // Skip if already processed + } + + // Process the course + ModelLoader::Extract(list.course); + + // Mark this course as processed + processedCourses.insert(list.course); + } +} + +void ModelLoader::Extract(Course* course) { + Course* saveCourse = gWorldInstance.CurrentCourse; + gWorldInstance.CurrentCourse = course; // Quick hack so that `get_texture` will find the right textures. + + size_t vtxSize = (ResourceGetSizeByName(course->vtx) / sizeof(CourseVtx)) * sizeof(Vtx); + size_t texSegSize; + + // Convert course vtx to vtx + Vtx* vtx = reinterpret_cast(malloc(vtxSize)); + if (vtx == NULL) { + printf("ModelLoader: Failed to allocate vertex buffer for course: %s\n", course->Props.Name); + return; + } + func_802A86A8(reinterpret_cast(LOAD_ASSET_RAW(course->vtx)), vtx, vtxSize / sizeof(Vtx)); + + // Extract packed DLs + u8* packed = reinterpret_cast(LOAD_ASSET_RAW(course->gfx)); + Gfx* gfx = reinterpret_cast(malloc(sizeof(Gfx) * course->gfxSize)); // Size of unpacked DLs + if (gfx == NULL) { + printf("ModelLoader: Failed to allocate course displaylist memory for course: %s\n", course->Props.Name); + return; + } + + displaylist_unpack(reinterpret_cast(gfx), reinterpret_cast(packed), 0); + + // Now that the data has been retrieved, extract the model data and save to provided buffers. + for (auto& list : _deferredList) { + // Ensure the list matches the course being extracted. + if (list.course != course) { + return; + } + + memcpy(list.vtxBuffer, &vtx[list.vtxStart], list.vtxBufferSize * sizeof(Vtx)); + memcpy(list.gfxBuffer, &gfx[list.gfxStart], list.gfxBufferSize * sizeof(Gfx)); + + UpdateVtx(list); + + } + + free(vtx); + free(gfx); + + _deferredList.clear(); + gWorldInstance.CurrentCourse = saveCourse; +} + +void ModelLoader::UpdateVtx(LoadModelList list) { + for (size_t i = 0; i < list.gfxStart + list.gfxBufferSize; i++) { + Gfx* gfx = &list.gfxBuffer[i]; + + // Check if the current Gfx command is G_VTX + if (GFX_GET_OPCODE(gfx->words.w0) == (G_VTX << 24)) { + // Re-write the vtx to point at the provided vtx buffer. + size_t vtxIndex = (gfx->words.w1 / sizeof(Vtx)) - list.vtxStart; + gfx->words.w1 = reinterpret_cast(&list.vtxBuffer[vtxIndex]); + } + } +} diff --git a/src/engine/ModelLoader.h b/src/engine/ModelLoader.h new file mode 100644 index 000000000..e89c7c457 --- /dev/null +++ b/src/engine/ModelLoader.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include "engine/courses/Course.h" + +extern "C" { +#include "common_structs.h" +} + +class Course; + +/** + * + * Lists are deferred until load time so that models that use the same course may all use the same extraction + * + * Note ensure that the buffers passed to LoadModelList are big enough for the requested data. + * + * This class should only be ran once. + * + * + * Usage: + * + * ModelLoader::LoadModelList bowserStatueList = { + .course = gBowsersCastle, + .gfxBuffer = &gBowserStatueGfx[0], + .gfxBufferSize = 162, + .gfxStart = (0x2BB8 / 8), // This is 0x2BB8 / sizeof(N64Gfx) not sizeof(Gfx) + .vtxBuffer = &gBowserStatueVtx[0], + .vtxBufferSize = 717, + .vtxStart = 1942, + }; + * + * gModelLoader.Add(bowserStatueList); + */ + +class ModelLoader { + +public: + struct LoadModelList { + Course* course; + + Gfx* gfxBuffer; // buffer for output gfx + size_t gfxBufferSize; + size_t gfxStart; // The starting point to extract data in NumGfx + + Vtx* vtxBuffer; // buffer for output vtx + size_t vtxBufferSize; + size_t vtxStart; // The starting point to extract data in NumVtx + }; + + void Add(LoadModelList list); + void Load(); +private: + struct CourseMap { + + }; + + void Extract(Course* course); + void UpdateVtx(LoadModelList list); + + std::vector _deferredList; + bool _hasRan = false; +}; diff --git a/src/engine/actors/BowserStatue.cpp b/src/engine/actors/BowserStatue.cpp new file mode 100644 index 000000000..de448de5e --- /dev/null +++ b/src/engine/actors/BowserStatue.cpp @@ -0,0 +1,45 @@ +#include "BowserStatue.h" + +#include + +extern "C" { +#include "common_structs.h" +#include "math_util.h" +#include "main.h" +#include "assets/bowsers_castle_data.h" +} + +Vtx gBowserStatueVtx[717]; +Gfx gBowserStatueGfx[162]; + +ABowserStatue::ABowserStatue(FVector pos, ABowserStatue::Behaviour behaviour) { + Pos = pos; + ABowserStatue::Behaviour _behaviour = behaviour; +} + +void ABowserStatue::Tick() { + switch(_behaviour) { + case DEFAULT: + break; + case CRUSH: + break; + } +} + +void ABowserStatue::Draw(Camera *camera) { + Mat4 mtx; + Vec3f pos; + pos[0] = Pos.x + 76; + pos[1] = Pos.y; + pos[2] = Pos.z + 1846; + gSPSetGeometryMode(gDisplayListHead++, G_SHADING_SMOOTH); + gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING); + + mtxf_pos_rotation_xyz(mtx, pos, Rot); + if (render_set_position(mtx, 0) != 0) { + + gSPDisplayList(gDisplayListHead++, gBowserStatueGfx); + } +} + +bool ABowserStatue::IsMod() { return true; } diff --git a/src/engine/actors/BowserStatue.h b/src/engine/actors/BowserStatue.h new file mode 100644 index 000000000..869f6910f --- /dev/null +++ b/src/engine/actors/BowserStatue.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include "engine/Actor.h" +#include "CoreMath.h" + +extern "C" { +#include "common_structs.h" +#include "assets/other_textures.h" +} + +extern Vtx gBowserStatueVtx[717]; +extern Gfx gBowserStatueGfx[162]; + +class ABowserStatue : public AActor { +public: + enum Behaviour { + DEFAULT, + CRUSH + }; + + virtual ~ABowserStatue() = default; + explicit ABowserStatue(FVector pos, ABowserStatue::Behaviour behaviour); + + virtual void Tick() override; + virtual void Draw(Camera*) override; + virtual bool IsMod() override; + + FVector Pos; +private: + ABowserStatue::Behaviour _behaviour; + f32 scale; +}; diff --git a/src/engine/courses/TestCourse.cpp b/src/engine/courses/TestCourse.cpp index 2a094c6a1..7e25d3357 100644 --- a/src/engine/courses/TestCourse.cpp +++ b/src/engine/courses/TestCourse.cpp @@ -7,6 +7,7 @@ #include "TestCourse.h" #include "World.h" #include "engine/actors/AFinishline.h" +#include "engine/actors/BowserStatue.h" #include "engine/objects/Object.h" #include "engine/objects/BombKart.h" #include "assets/mario_raceway_data.h" @@ -207,14 +208,15 @@ void TestCourse::SpawnActors() { // gWorldInstance.AddObject(new OCheepCheep(FVector(0, 40, 0), OCheepCheep::CheepType::RACE, IPathSpan(0, 10))); // gWorldInstance.AddObject(new OTrophy(FVector(0,0,0), OTrophy::TrophyType::GOLD, OTrophy::Behaviour::GO_FISH)); //gWorldInstance.AddObject(new OSnowman(FVector(0, 0, 0))); - //gWorldInstance.AddObject(new OTrashBin(FVector(0.0f, 0.0f, 0.0f), FRotation(0, 90, 0), 1.0f)); + //gWorldInstance.AddObject(new OTrashBin(FVector(0.0f, 0.0f, 0.0f), FRotation(0, 90, 0), 1.0f, OTrashBin::Behaviour::MUNCHING)); //gWorldInstance.AddEmitter(new StarEmitter(FVector(0,50,0))); //gWorldInstance.AddObject(new OHedgehog(FVector(0, 0, 0), FVector2D(0, -200), 9)); //gWorldInstance.AddObject(new OFlagpole(FVector(0, 0, -200), 0x400)); // gWorldInstance.AddObject(new OHotAirBalloon(FVector(0.0, 20.0f, -200.0f))); - gWorldInstance.AddObject(new OCrab(FVector2D(0, 0), FVector2D(0, -200))); + //gWorldInstance.AddObject(new OCrab(FVector2D(0, 0), FVector2D(0, -200))); + gWorldInstance.AddActor(new ABowserStatue(FVector(-200, 0, 0), ABowserStatue::Behaviour::CRUSH)); } // Likely sets minimap boundaries diff --git a/src/enhancements/freecam/freecam.cpp b/src/enhancements/freecam/freecam.cpp index 5e0930419..709cc6f1d 100644 --- a/src/enhancements/freecam/freecam.cpp +++ b/src/enhancements/freecam/freecam.cpp @@ -35,7 +35,7 @@ typedef struct { freecamSaveState fState; u32 fRankIndex = 0; -u32 fTargetPlayer = true; +u32 fTargetPlayer = false; u32 fMode; // freecam mode should probably be an enum u32 fModeInit = false; diff --git a/src/port/Game.cpp b/src/port/Game.cpp index 70634713a..dd91b902f 100644 --- a/src/port/Game.cpp +++ b/src/port/Game.cpp @@ -31,6 +31,9 @@ #include "engine/courses/PodiumCeremony.h" +#include "engine/ModelLoader.h" +#include "engine/actors/BowserStatue.h" + #include "engine/GarbageCollector.h" #include "engine/TrainCrossing.h" @@ -86,6 +89,8 @@ Cup* gStarCup; Cup* gSpecialCup; Cup* gBattleCup; +ModelLoader gModelLoader; + void CustomEngineInit() { gMarioRaceway = new MarioRaceway(); @@ -152,6 +157,20 @@ void CustomEngineInit() { gWorldInstance.CurrentCup = gMushroomCup; gWorldInstance.CurrentCup->CursorPosition = 3; gWorldInstance.CupIndex = 0; + + ModelLoader::LoadModelList bowserStatueList = { + .course = gBowsersCastle, + .gfxBuffer = &gBowserStatueGfx[0], + .gfxBufferSize = 162, + .gfxStart = (0x2BB8 / 8), // 0x2BB8 / sizeof(OldGfx) + .vtxBuffer = &gBowserStatueVtx[0], + .vtxBufferSize = 717, + .vtxStart = 1942, + }; + + gModelLoader.Add(bowserStatueList); + + gModelLoader.Load(); } extern "C" {