summaryrefslogtreecommitdiff
path: root/src/engine
diff options
context:
space:
mode:
authorMegaMech <MegaMech@users.noreply.github.com>2025-01-09 23:46:18 -0700
committerGitHub <noreply@github.com>2025-01-09 23:46:18 -0700
commitaaba052407728b2f13d11900f2e81a85991c1a91 (patch)
tree91d14b8c162a79d7c019454a36374bb3d25a77f4 /src/engine
parent29872e43c7a98550cfa2f896bdc61589f383d705 (diff)
Impl ModelLoader (#150)
* Impl modelloader
Diffstat (limited to 'src/engine')
-rw-r--r--src/engine/ModelLoader.cpp94
-rw-r--r--src/engine/ModelLoader.h63
-rw-r--r--src/engine/actors/BowserStatue.cpp45
-rw-r--r--src/engine/actors/BowserStatue.h34
-rw-r--r--src/engine/courses/TestCourse.cpp6
5 files changed, 240 insertions, 2 deletions
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<Course*> 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<Vtx*>(malloc(vtxSize));
+ if (vtx == NULL) {
+ printf("ModelLoader: Failed to allocate vertex buffer for course: %s\n", course->Props.Name);
+ return;
+ }
+ func_802A86A8(reinterpret_cast<CourseVtx*>(LOAD_ASSET_RAW(course->vtx)), vtx, vtxSize / sizeof(Vtx));
+
+ // Extract packed DLs
+ u8* packed = reinterpret_cast<u8*>(LOAD_ASSET_RAW(course->gfx));
+ Gfx* gfx = reinterpret_cast<Gfx*>(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<uintptr_t*>(gfx), reinterpret_cast<uintptr_t>(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<uintptr_t>(&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 <libultraship.h>
+#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<LoadModelList> _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 <libultra/gbi.h>
+
+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 <libultraship.h>
+#include <libultra/gbi.h>
+#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