diff --git a/soh/include/functions.h b/soh/include/functions.h
index 13e3a7055..01901d5d0 100644
--- a/soh/include/functions.h
+++ b/soh/include/functions.h
@@ -656,10 +656,12 @@ Vec3s* SurfaceType_GetCamPosData(CollisionContext* colCtx, CollisionPoly* poly,
u32 SurfaceType_GetSceneExitIndex(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId);
u32 func_80041D4C(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId);
u32 func_80041D70(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId);
+u32 func_80041D94(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId);
s32 func_80041DB8(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId);
s32 func_80041DE4(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId);
s32 func_80041E18(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId);
s32 func_80041E4C(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId);
+s32 func_80041E80(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId);
u32 func_80041EA4(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId);
u32 func_80041EC8(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId);
u32 SurfaceType_IsHorseBlocked(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId);
diff --git a/soh/soh.vcxproj b/soh/soh.vcxproj
index d35dd18b3..2bd4380b9 100644
--- a/soh/soh.vcxproj
+++ b/soh/soh.vcxproj
@@ -175,8 +175,10 @@
+
+
@@ -922,8 +924,10 @@
+
+
diff --git a/soh/soh.vcxproj.filters b/soh/soh.vcxproj.filters
index c31a78b19..a8455a4c8 100644
--- a/soh/soh.vcxproj.filters
+++ b/soh/soh.vcxproj.filters
@@ -2184,6 +2184,12 @@
Source Files\soh
+
+ Source Files\soh\Enhancements\debugger
+
+
+ Source Files\soh\Enhancements\debugger
+
@@ -3734,6 +3740,12 @@
Header Files\soh
+
+ Header Files\soh\Enhancements\debugger
+
+
+ Header Files\soh\Enhancements\debugger
+
diff --git a/soh/soh/Enhancements/debugger/ImGuiHelpers.cpp b/soh/soh/Enhancements/debugger/ImGuiHelpers.cpp
new file mode 100644
index 000000000..6f4f00637
--- /dev/null
+++ b/soh/soh/Enhancements/debugger/ImGuiHelpers.cpp
@@ -0,0 +1,21 @@
+#include "ImGuiHelpers.h"
+
+// Adds a text tooltip for the previous ImGui item
+void SetLastItemHoverText(const std::string& text) {
+ if (ImGui::IsItemHovered()) {
+ ImGui::BeginTooltip();
+ ImGui::Text(text.c_str());
+ ImGui::EndTooltip();
+ }
+}
+
+// Adds a "?" next to the previous ImGui item with a custom tooltip
+void InsertHelpHoverText(const std::string& text) {
+ ImGui::SameLine();
+ ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "?");
+ if (ImGui::IsItemHovered()) {
+ ImGui::BeginTooltip();
+ ImGui::Text(text.c_str());
+ ImGui::EndTooltip();
+ }
+}
diff --git a/soh/soh/Enhancements/debugger/ImGuiHelpers.h b/soh/soh/Enhancements/debugger/ImGuiHelpers.h
new file mode 100644
index 000000000..7f01e4580
--- /dev/null
+++ b/soh/soh/Enhancements/debugger/ImGuiHelpers.h
@@ -0,0 +1,8 @@
+#pragma once
+#include "../libultraship/Lib/ImGui/imgui.h"
+
+#include
+
+void SetLastItemHoverText(const std::string& text);
+
+void InsertHelpHoverText(const std::string& text);
diff --git a/soh/soh/Enhancements/debugger/colViewer.cpp b/soh/soh/Enhancements/debugger/colViewer.cpp
new file mode 100644
index 000000000..ad9d341d4
--- /dev/null
+++ b/soh/soh/Enhancements/debugger/colViewer.cpp
@@ -0,0 +1,719 @@
+#include "colViewer.h"
+#include "../libultraship/SohImGuiImpl.h"
+#include "ImGuiHelpers.h"
+
+#include
+#include
+
+extern "C" {
+#include
+#include "variables.h"
+#include "functions.h"
+#include "macros.h"
+extern GlobalContext* gGlobalCtx;
+}
+
+enum class ColRenderSetting {
+ Disabled,
+ Solid,
+ Transparent,
+ NumSettings
+};
+
+std::string ColRenderSettingNames[] = {
+ "Disabled",
+ "Solid",
+ "Transparent",
+};
+
+static ColRenderSetting showSceneColSetting = ColRenderSetting::Disabled;
+static ColRenderSetting showBgActorSetting = ColRenderSetting::Disabled;
+static ColRenderSetting showColCheckSetting = ColRenderSetting::Disabled;
+static ColRenderSetting showWaterboxSetting = ColRenderSetting::Disabled;
+
+static uint32_t sceneColor = 0xFFFFFFFF;
+static uint32_t hookshotColor = 0x8080FFFF;
+static uint32_t entranceColor = 0x00FF00FF;
+static uint32_t specialSurfaceColor = 0xC0FFC0FF;
+static uint32_t interactableColor = 0xC000C0FF;
+static uint32_t slopeColor = 0xFFFF80FF;
+static uint32_t voidColor = 0xFF0000FF;
+
+static uint32_t ocColor = 0xFFFFFFFF;
+static uint32_t acColor = 0x0000FFFF;
+static uint32_t atColor = 0xFF0000FF;
+
+static uint32_t waterboxColor = 0x0000FFFF;
+
+static bool applyAsDecal = false;
+static bool isShaded = false;
+
+static std::vector opaDl;
+static std::vector xluDl;
+static std::vector vtxDl;
+static std::vector mtxDl;
+
+// These DLs contain a cylinder/sphere model scaled to 128x (to have less error)
+// The idea is to push a model view matrix, then draw the DL, to draw the shape somewhere with a certain size
+static std::vector cylinderGfx;
+static std::vector cylinderVtx;
+static std::vector sphereGfx;
+static std::vector sphereVtx;
+
+// Create a dropdown menu to set a ColRenderSetting
+void DrawColRenderSetting(const std::string& name, ColRenderSetting& setting) {
+ if (ImGui::BeginCombo(name.c_str(), ColRenderSettingNames[static_cast(setting)].c_str())) {
+ for (int32_t settingIndex = 0; settingIndex < static_cast(ColRenderSetting::NumSettings); settingIndex++) {
+ if (ImGui::Selectable(ColRenderSettingNames[settingIndex].c_str())) {
+ setting = static_cast(settingIndex);
+ }
+ }
+ ImGui::EndCombo();
+ }
+}
+
+// Draw a color picker box
+void DrawColorPicker(const std::string& name, uint32_t& color) {
+ float colorAsFloat[4];
+ colorAsFloat[0] = ((color >> 24) & 0xFF) / 255.0f;
+ colorAsFloat[1] = ((color >> 16) & 0xFF) / 255.0f;
+ colorAsFloat[2] = ((color >> 8) & 0xFF) / 255.0f;
+ colorAsFloat[3] = (color & 0xFF) / 255.0f;
+ if (ImGui::ColorEdit4(name.c_str(), colorAsFloat, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) {
+ color = static_cast(colorAsFloat[3] * 255) |
+ static_cast(colorAsFloat[2] * 255) << 8 |
+ static_cast(colorAsFloat[1] * 255) << 16 |
+ static_cast(colorAsFloat[0] * 255) << 24;
+ }
+ ImGui::SameLine();
+ ImGui::Text(name.c_str());
+}
+
+// Draws the ImGui window for the collision viewer
+void DrawColViewerWindow(bool& open) {
+ if (!open) {
+ return;
+ }
+
+ ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
+ if (!ImGui::Begin("Collision Viewer", &open)) {
+ ImGui::End();
+ return;
+ }
+
+ DrawColRenderSetting("Scene", showSceneColSetting);
+ DrawColRenderSetting("Bg Actors", showBgActorSetting);
+ DrawColRenderSetting("Col Check", showColCheckSetting);
+ DrawColRenderSetting("Waterbox", showWaterboxSetting);
+
+ ImGui::Checkbox("Apply as decal", &applyAsDecal);
+ InsertHelpHoverText("Applies the collision as a decal display. This can be useful if there is z-fighting occuring "
+ "with the scene geometry, but can cause other artifacts.");
+ ImGui::Checkbox("Shaded", &isShaded);
+ InsertHelpHoverText("Applies the scene's shading to the collision display.");
+
+ // This has to be duplicated in both code paths due to the nature of ImGui::IsItemHovered()
+ const std::string colorHelpText = "View and change the colors used for collision display.";
+ if (ImGui::TreeNode("Colors")) {
+ InsertHelpHoverText(colorHelpText);
+
+ DrawColorPicker("Normal", sceneColor);
+ DrawColorPicker("Hookshot", hookshotColor);
+ DrawColorPicker("Entrance", entranceColor);
+ DrawColorPicker("Special Surface (Grass/Sand/Etc)", specialSurfaceColor);
+ DrawColorPicker("Interactable (Vines/Crawlspace/Etc)", interactableColor);
+ DrawColorPicker("Slope", slopeColor);
+ DrawColorPicker("Void", voidColor);
+ DrawColorPicker("OC", ocColor);
+ DrawColorPicker("AC", acColor);
+ DrawColorPicker("AT", atColor);
+ DrawColorPicker("Waterbox", waterboxColor);
+
+ ImGui::TreePop();
+ } else {
+ InsertHelpHoverText(colorHelpText);
+ }
+
+ ImGui::End();
+}
+
+// Calculates the normal for a triangle at the 3 specified points
+void CalcTriNorm(const Vec3f& v1, const Vec3f& v2, const Vec3f& v3, Vec3f& norm) {
+ norm.x = (v2.y - v1.y) * (v3.z - v1.z) - (v2.z - v1.z) * (v3.y - v1.y);
+ norm.y = (v2.z - v1.z) * (v3.x - v1.x) - (v2.x - v1.x) * (v3.z - v1.z);
+ norm.z = (v2.x - v1.x) * (v3.y - v1.y) - (v2.y - v1.y) * (v3.x - v1.x);
+ float norm_d = sqrtf(norm.x * norm.x + norm.y * norm.y + norm.z * norm.z);
+ if (norm_d != 0.f) {
+ norm.x *= 127.f / norm_d;
+ norm.y *= 127.f / norm_d;
+ norm.z *= 127.f / norm_d;
+ }
+}
+
+// Various macros used for creating verticies and rendering that aren't in gbi.h
+#define G_CC_MODULATERGB_PRIM_ENVA PRIMITIVE, 0, SHADE, 0, 0, 0, 0, ENVIRONMENT
+#define G_CC_PRIMITIVE_ENVA 0, 0, 0, PRIMITIVE, 0, 0, 0, ENVIRONMENT
+#define qs105(n) ((int16_t)((n)*0x0020))
+#define gdSPDefVtxN(x, y, z, s, t, nx, ny, nz, ca) \
+ { \
+ .n = {.ob = { x, y, z }, .tc = { qs105(s), qs105(t) }, .n = { nx, ny, nz }, .a = ca } \
+ }
+
+
+void CreateCylinderData() {
+ constexpr int32_t CYL_DIVS = 12;
+ cylinderGfx.reserve(5 + CYL_DIVS * 2);
+ cylinderVtx.reserve(2 + CYL_DIVS * 2);
+
+ cylinderVtx.push_back(gdSPDefVtxN(0, 0, 0, 0, 0, 0, -127, 0, 0xFF)); // Bottom center vertex
+ cylinderVtx.push_back(gdSPDefVtxN(0, 128, 0, 0, 0, 0, 127, 0, 0xFF)); // Top center vertex
+ // Create two rings of vertices
+ for (int i = 0; i < CYL_DIVS; ++i) {
+ short vtx_x = floorf(0.5f + cosf(2.f * M_PI * i / CYL_DIVS) * 128.f);
+ short vtx_z = floorf(0.5f - sinf(2.f * M_PI * i / CYL_DIVS) * 128.f);
+ signed char norm_x = cosf(2.f * M_PI * i / CYL_DIVS) * 127.f;
+ signed char norm_z = -sinf(2.f * M_PI * i / CYL_DIVS) * 127.f;
+ cylinderVtx.push_back(gdSPDefVtxN(vtx_x, 0, vtx_z, 0, 0, norm_x, 0, norm_z, 0xFF));
+ cylinderVtx.push_back(gdSPDefVtxN(vtx_x, 128, vtx_z, 0, 0, norm_x, 0, norm_z, 0xFF));
+ }
+
+ // Draw edges
+ cylinderGfx.push_back(gsSPSetGeometryMode(G_CULL_BACK | G_SHADING_SMOOTH));
+ cylinderGfx.push_back(gsSPVertex((uintptr_t)cylinderVtx.data(), 2 + CYL_DIVS * 2, 0));
+ for (int i = 0; i < CYL_DIVS; ++i) {
+ int p = (i + CYL_DIVS - 1) % CYL_DIVS;
+ int v[4] = {
+ 2 + p * 2 + 0,
+ 2 + i * 2 + 0,
+ 2 + i * 2 + 1,
+ 2 + p * 2 + 1,
+ };
+ cylinderGfx.push_back(gsSP2Triangles(v[0], v[1], v[2], 0, v[0], v[2], v[3], 0));
+ }
+
+ // Draw top & bottom
+ cylinderGfx.push_back(gsSPClearGeometryMode(G_SHADING_SMOOTH));
+ for (int i = 0; i < CYL_DIVS; ++i) {
+ int p = (i + CYL_DIVS - 1) % CYL_DIVS;
+ int v[4] = {
+ 2 + p * 2 + 0,
+ 2 + i * 2 + 0,
+ 2 + i * 2 + 1,
+ 2 + p * 2 + 1,
+ };
+ cylinderGfx.push_back(gsSP2Triangles(0, v[1], v[0], 0, 1, v[3], v[2], 0));
+ }
+
+ cylinderGfx.push_back(gsSPClearGeometryMode(G_CULL_BACK));
+ cylinderGfx.push_back(gsSPEndDisplayList());
+}
+
+// This subdivides a face into four tris by placing new verticies at the midpoints of the sides (Like a triforce!), then blowing up the
+// verticies so they are on the unit sphere
+void CreateSphereFace(std::vector>& faces, int32_t v0Index, int32_t v1Index, int32_t v2Index) {
+ size_t nextIndex = sphereVtx.size();
+
+ size_t v01Index = nextIndex;
+ size_t v12Index = nextIndex + 1;
+ size_t v20Index = nextIndex + 2;
+
+ faces.emplace_back(v0Index, v01Index, v20Index);
+ faces.emplace_back(v1Index, v12Index, v01Index);
+ faces.emplace_back(v2Index, v20Index, v12Index);
+ faces.emplace_back(v01Index, v12Index, v20Index);
+
+ const Vtx& v0 = sphereVtx[v0Index];
+ const Vtx& v1 = sphereVtx[v1Index];
+ const Vtx& v2 = sphereVtx[v2Index];
+
+ // Create 3 new verticies at the midpoints
+ Vec3f vs[3] = {
+ Vec3f((v0.n.ob[0] + v1.n.ob[0]) / 2.0f, (v0.n.ob[1] + v1.n.ob[1]) / 2.0f, (v0.n.ob[2] + v1.n.ob[2]) / 2.0f),
+ Vec3f((v1.n.ob[0] + v2.n.ob[0]) / 2.0f, (v1.n.ob[1] + v2.n.ob[1]) / 2.0f, (v1.n.ob[2] + v2.n.ob[2]) / 2.0f),
+ Vec3f((v2.n.ob[0] + v0.n.ob[0]) / 2.0f, (v2.n.ob[1] + v0.n.ob[1]) / 2.0f, (v2.n.ob[2] + v0.n.ob[2]) / 2.0f)
+ };
+
+ // Normalize vertex positions so they are on the sphere
+ for (int32_t vAddIndex = 0; vAddIndex < 3; vAddIndex++) {
+ Vec3f& v = vs[vAddIndex];
+ float mag = sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);
+ v.x /= mag;
+ v.y /= mag;
+ v.z /= mag;
+ sphereVtx.push_back(gdSPDefVtxN((short)(v.x * 127), (short)(v.y * 127), (short)(v.z * 127), 0, 0,
+ (signed char)(v.x * 127), (signed char)(v.y * 127), (signed char)(v.z * 127),
+ 0xFF));
+ }
+}
+
+// Creates a sphere following the idea in here: http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html
+// Spcifically, create a icosahedron by realizing that the points can be placed on 3 rectangles that are on each unit plane.
+// Then, subdividing each face.
+void CreateSphereData() {
+ std::vector base;
+
+ float d = (1.0f + sqrtf(5.0f)) / 2.0f;
+
+ // Create the 12 starting verticies, 4 on each rectangle
+ base.emplace_back(-1, d, 0);
+ base.emplace_back(1, d, 0);
+ base.emplace_back(-1, -d, 0);
+ base.emplace_back(1, -d, 0);
+
+ base.emplace_back(0, -1, d);
+ base.emplace_back(0, 1, d);
+ base.emplace_back(0, -1, -d);
+ base.emplace_back(0, 1, -d);
+
+ base.emplace_back(d, 0, -1);
+ base.emplace_back(d, 0, 1);
+ base.emplace_back(-d, 0, -1);
+ base.emplace_back(-d, 0, 1);
+
+ // Normalize verticies so they are on the unit sphere
+ for (Vec3f& v : base) {
+ float mag = sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);
+ v.x /= mag;
+ v.y /= mag;
+ v.z /= mag;
+ sphereVtx.push_back(gdSPDefVtxN((short)(v.x * 128), (short)(v.y * 128), (short)(v.z * 128), 0, 0,
+ (signed char)(v.x * 127), (signed char)(v.y * 127), (signed char)(v.z * 127),
+ 0xFF));
+ }
+
+ std::vector> faces;
+
+ // Subdivide faces
+ CreateSphereFace(faces, 0, 11, 5);
+ CreateSphereFace(faces, 0, 5, 1);
+ CreateSphereFace(faces, 0, 1, 7);
+ CreateSphereFace(faces, 0, 7, 10);
+ CreateSphereFace(faces, 0, 10, 11);
+
+ CreateSphereFace(faces, 1, 5, 9);
+ CreateSphereFace(faces, 5, 11, 4);
+ CreateSphereFace(faces, 11, 10, 2);
+ CreateSphereFace(faces, 10, 7, 6);
+ CreateSphereFace(faces, 7, 1, 8);
+
+ CreateSphereFace(faces, 3, 9, 4);
+ CreateSphereFace(faces, 3, 4, 2);
+ CreateSphereFace(faces, 3, 2, 6);
+ CreateSphereFace(faces, 3, 6, 8);
+ CreateSphereFace(faces, 3, 8, 9);
+
+ CreateSphereFace(faces, 4, 9, 5);
+ CreateSphereFace(faces, 2, 4, 11);
+ CreateSphereFace(faces, 6, 2, 10);
+ CreateSphereFace(faces, 8, 6, 7);
+ CreateSphereFace(faces, 9, 8, 1);
+
+ size_t vtxStartIndex = sphereVtx.size();
+ sphereVtx.reserve(sphereVtx.size() + faces.size() * 3);
+ for (int32_t faceIndex = 0; faceIndex < faces.size(); faceIndex++) {
+ sphereVtx.push_back(sphereVtx[std::get<0>(faces[faceIndex])]);
+ sphereVtx.push_back(sphereVtx[std::get<1>(faces[faceIndex])]);
+ sphereVtx.push_back(sphereVtx[std::get<2>(faces[faceIndex])]);
+ sphereGfx.push_back(gsSPVertex((uintptr_t)(sphereVtx.data() + vtxStartIndex + faceIndex * 3), 3, 0));
+ sphereGfx.push_back(gsSP1Triangle(0, 1, 2, 0));
+ }
+
+ sphereGfx.push_back(gsSPEndDisplayList());
+}
+
+void InitColViewer() {
+ SohImGui::AddWindow("Debug", "Collision Viewer", DrawColViewerWindow);
+
+ CreateCylinderData();
+ CreateSphereData();
+}
+
+// Initializes the display list for a ColRenderSetting
+void InitGfx(std::vector& gfx, ColRenderSetting setting) {
+ uint32_t rm;
+ uint32_t blc1;
+ uint32_t blc2;
+ uint8_t alpha;
+ uint64_t cm;
+ uint32_t gm;
+
+ if (setting == ColRenderSetting::Transparent) {
+ rm = Z_CMP | IM_RD | CVG_DST_FULL | FORCE_BL;
+ blc1 = GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA);
+ blc2 = GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA);
+ alpha = 0x80;
+ } else {
+ rm = Z_CMP | Z_UPD | CVG_DST_CLAMP | FORCE_BL;
+ blc1 = GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1);
+ blc2 = GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1);
+ alpha = 0xFF;
+ }
+
+ if (applyAsDecal) {
+ rm |= ZMODE_DEC;
+ } else if (setting == ColRenderSetting::Transparent) {
+ rm |= ZMODE_XLU;
+ } else {
+ rm |= ZMODE_OPA;
+ }
+
+ gfx.push_back(gsSPTexture(0, 0, 0, G_TX_RENDERTILE, G_OFF));
+ gfx.push_back(gsDPSetCycleType(G_CYC_1CYCLE));
+ gfx.push_back(gsDPSetRenderMode(rm | blc1, rm | blc2));
+
+ if (isShaded) {
+ gfx.push_back(gsDPSetCombineMode(G_CC_MODULATERGB_PRIM_ENVA, G_CC_MODULATERGB_PRIM_ENVA));
+ gfx.push_back(gsSPLoadGeometryMode(G_CULL_BACK | G_ZBUFFER | G_LIGHTING));
+ } else {
+ gfx.push_back(gsDPSetCombineMode(G_CC_PRIMITIVE_ENVA, G_CC_PRIMITIVE_ENVA));
+ gfx.push_back(gsSPLoadGeometryMode(G_ZBUFFER));
+ }
+
+ gfx.push_back(gsDPSetEnvColor(0xFF, 0xFF, 0xFF, alpha));
+}
+
+// Draws a dynapoly structure (scenes or Bg Actors)
+void DrawDynapoly(std::vector& dl, CollisionHeader* col, int32_t bgId) {
+ uint32_t color = sceneColor;
+ uint32_t lastColor = color;
+ dl.push_back(gsDPSetPrimColor(0, 0, (color >> 24) & 0xFF, (color >> 16) & 0xFF, (color >> 8) & 0xFF,
+ (color >> 0) & 0xFF));
+
+ // This keeps track of if we have processed a poly, but not drawn it yet so we can batch them.
+ // This saves several hundred commands in larger scenes
+ bool previousPoly = false;
+
+ for (int i = 0; i < col->numPolygons; i++) {
+ CollisionPoly* poly = &col->polyList[i];
+
+ if (SurfaceType_IsHookshotSurface(&gGlobalCtx->colCtx, poly, bgId)) {
+ color = hookshotColor;
+ } else if (func_80041D94(&gGlobalCtx->colCtx, poly, bgId) > 0x01) {
+ color = interactableColor;
+ } else if (func_80041E80(&gGlobalCtx->colCtx, poly, bgId) == 0x0C) {
+ color = voidColor;
+ } else if (SurfaceType_GetSceneExitIndex(&gGlobalCtx->colCtx, poly, bgId) ||
+ func_80041E80(&gGlobalCtx->colCtx, poly, bgId) == 0x05) {
+ color = entranceColor;
+ } else if (func_80041D4C(&gGlobalCtx->colCtx, poly, bgId) != 0 ||
+ SurfaceType_IsWallDamage(&gGlobalCtx->colCtx, poly, bgId)) {
+ color = specialSurfaceColor;
+ } else if (SurfaceType_GetSlope(&gGlobalCtx->colCtx, poly, bgId) == 0x01) {
+ color = slopeColor;
+ } else {
+ color = sceneColor;
+ }
+
+ if (lastColor != color) {
+ // Color changed, flush previous poly
+ if (previousPoly) {
+ dl.push_back(gsSPVertex((uintptr_t)&vtxDl.at(vtxDl.size() - 3), 3, 0));
+ dl.push_back(gsSP1Triangle(0, 1, 2, 0));
+ previousPoly = false;
+ }
+ dl.push_back(gsDPSetPrimColor(0, 0, (color >> 24) & 0xFF, (color >> 16) & 0xFF, (color >> 8) & 0xFF,
+ (color >> 0) & 0xFF));
+ }
+ lastColor = color;
+
+ Vec3s* va = &col->vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)];
+ Vec3s* vb = &col->vtxList[COLPOLY_VTX_INDEX(poly->flags_vIB)];
+ Vec3s* vc = &col->vtxList[COLPOLY_VTX_INDEX(poly->vIC)];
+ vtxDl.push_back(gdSPDefVtxN(va->x, va->y, va->z, 0, 0, (signed char)(poly->normal.x / 0x100),
+ (signed char)(poly->normal.y / 0x100), (signed char)(poly->normal.z / 0x100),
+ 0xFF));
+ vtxDl.push_back(gdSPDefVtxN(vb->x, vb->y, vb->z, 0, 0, (signed char)(poly->normal.x / 0x100),
+ (signed char)(poly->normal.y / 0x100), (signed char)(poly->normal.z / 0x100),
+ 0xFF));
+ vtxDl.push_back(gdSPDefVtxN(vc->x, vc->y, vc->z, 0, 0, (signed char)(poly->normal.x / 0x100),
+ (signed char)(poly->normal.y / 0x100), (signed char)(poly->normal.z / 0x100),
+ 0xFF));
+
+ if (previousPoly) {
+ dl.push_back(gsSPVertex((uintptr_t)&vtxDl.at(vtxDl.size() - 6), 6, 0));
+ dl.push_back(gsSP2Triangles(0, 1, 2, 0, 3, 4, 5, 0));
+ previousPoly = false;
+ } else {
+ previousPoly = true;
+ }
+ }
+
+ // Flush previous poly if this is the end and there's no more coming
+ if (previousPoly) {
+ dl.push_back(gsSPVertex((uintptr_t)&vtxDl.at(vtxDl.size() - 3), 3, 0));
+ dl.push_back(gsSP1Triangle(0, 1, 2, 0));
+ previousPoly = false;
+ }
+}
+
+// Draws the scene
+void DrawSceneCollision() {
+ if (showSceneColSetting == ColRenderSetting::Disabled) {
+ return;
+ }
+
+ std::vector& dl = (showSceneColSetting == ColRenderSetting::Transparent) ? xluDl : opaDl;
+ InitGfx(dl, showSceneColSetting);
+ dl.push_back(gsSPMatrix(&gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH));
+
+ DrawDynapoly(dl, gGlobalCtx->colCtx.colHeader, BGCHECK_SCENE);
+}
+
+// Draws all Bg Actors
+void DrawBgActorCollision() {
+ if (showBgActorSetting == ColRenderSetting::Disabled) {
+ return;
+ }
+
+ std::vector& dl = (showBgActorSetting == ColRenderSetting::Transparent) ? xluDl : opaDl;
+ InitGfx(dl, showBgActorSetting);
+ dl.push_back(gsSPMatrix(&gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH));
+
+ for (int32_t bgIndex = 0; bgIndex < BG_ACTOR_MAX; bgIndex++) {
+ if (gGlobalCtx->colCtx.dyna.bgActorFlags[bgIndex] & 1) {
+ BgActor& bg = gGlobalCtx->colCtx.dyna.bgActors[bgIndex];
+ Mtx m;
+ MtxF mf;
+ SkinMatrix_SetTranslateRotateYXZScale(&mf, bg.curTransform.scale.x, bg.curTransform.scale.y,
+ bg.curTransform.scale.z, bg.curTransform.rot.x, bg.curTransform.rot.y,
+ bg.curTransform.rot.z, bg.curTransform.pos.x, bg.curTransform.pos.y,
+ bg.curTransform.pos.z);
+ guMtxF2L(&mf, &m);
+ mtxDl.push_back(m);
+ dl.push_back(gsSPMatrix(&mtxDl.back(), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_PUSH));
+
+ DrawDynapoly(dl, bg.colHeader, bgIndex);
+
+ dl.push_back(gsSPPopMatrix(G_MTX_MODELVIEW));
+ }
+ }
+
+}
+
+// Draws a quad
+void DrawQuad(std::vector& dl, Vec3f& v0, Vec3f& v1, Vec3f& v2, Vec3f& v3) {
+ Vec3f norm;
+ CalcTriNorm(v0, v1, v2, norm);
+
+ vtxDl.push_back(gdSPDefVtxN((short)v0.x, (short)v0.y, (short)v0.z, 0, 0, (signed char)norm.x, (signed char)norm.y,
+ (signed char)norm.z, 0xFF));
+ vtxDl.push_back(gdSPDefVtxN((short)v1.x, (short)v1.y, (short)v1.z, 0, 0, (signed char)norm.x, (signed char)norm.y,
+ (signed char)norm.z, 0xFF));
+ vtxDl.push_back(gdSPDefVtxN((short)v2.x, (short)v2.y, (short)v2.z, 0, 0, (signed char)norm.x, (signed char)norm.y,
+ (signed char)norm.z, 0xFF));
+ vtxDl.push_back(gdSPDefVtxN((short)v3.x, (short)v3.y, (short)v3.z, 0, 0, (signed char)norm.x, (signed char)norm.y,
+ (signed char)norm.z, 0xFF));
+ dl.push_back(gsSPVertex((uintptr_t)&vtxDl.at(vtxDl.size() - 4), 4, 0));
+ dl.push_back(gsSP2Triangles(0, 1, 2, 0, 0, 2, 3, 0));
+}
+
+// Draws a list of Col Check objects
+void DrawColCheckList(std::vector& dl, Collider** objects, int32_t count) {
+ for (int32_t colIndex = 0; colIndex < count; colIndex++) {
+ Collider* col = objects[colIndex];
+ switch (col->shape) {
+ case COLSHAPE_JNTSPH: {
+ ColliderJntSph* jntSph = (ColliderJntSph*)col;
+
+ for (int32_t sphereIndex = 0; sphereIndex < jntSph->count; sphereIndex++) {
+ ColliderJntSphElement* sph = &jntSph->elements[sphereIndex];
+
+ Mtx m;
+ MtxF mf;
+ SkinMatrix_SetTranslate(&mf, sph->dim.worldSphere.center.x, sph->dim.worldSphere.center.y,
+ sph->dim.worldSphere.center.z);
+ MtxF ms;
+ int32_t radius = sph->dim.worldSphere.radius == 0 ? 1 : sph->dim.worldSphere.radius;
+ SkinMatrix_SetScale(&ms, radius / 128.0f, radius / 128.0f, radius / 128.0f);
+ MtxF dest;
+ SkinMatrix_MtxFMtxFMult(&mf, &ms, &dest);
+ guMtxF2L(&dest, &m);
+ mtxDl.push_back(m);
+
+ dl.push_back(gsSPMatrix(&mtxDl.back(), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_PUSH));
+ dl.push_back(gsSPDisplayList(sphereGfx.data()));
+ dl.push_back(gsSPPopMatrix(G_MTX_MODELVIEW));
+ }
+ } break;
+ case COLSHAPE_CYLINDER: {
+ ColliderCylinder* cyl = (ColliderCylinder*)col;
+
+ Mtx m;
+ MtxF mt;
+ SkinMatrix_SetTranslate(&mt, cyl->dim.pos.x, cyl->dim.pos.y, cyl->dim.pos.z);
+ MtxF ms;
+ int32_t radius = cyl->dim.radius == 0 ? 1 : cyl->dim.radius;
+ SkinMatrix_SetScale(&ms, radius / 128.0f, cyl->dim.height / 128.0f, radius / 128.0f);
+ MtxF dest;
+ SkinMatrix_MtxFMtxFMult(&mt, &ms, &dest);
+ guMtxF2L(&dest, &m);
+ mtxDl.push_back(m);
+
+ dl.push_back(gsSPMatrix(&mtxDl.back(), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_PUSH));
+ dl.push_back(gsSPDisplayList(cylinderGfx.data()));
+ dl.push_back(gsSPPopMatrix(G_MTX_MODELVIEW));
+ } break;
+ case COLSHAPE_TRIS: {
+ ColliderTris* tris = (ColliderTris*)col;
+ for (int32_t triIndex = 0; triIndex < tris->count; triIndex++) {
+ ColliderTrisElement* tri = &tris->elements[triIndex];
+
+ vtxDl.push_back(gdSPDefVtxN((short)tri->dim.vtx[0].x, (short)tri->dim.vtx[0].y,
+ (short)tri->dim.vtx[0].z, 0, 0, (signed char)tri->dim.plane.normal.x,
+ (signed char)tri->dim.plane.normal.y,
+ (signed char)tri->dim.plane.normal.z, 0xFF));
+ vtxDl.push_back(gdSPDefVtxN((short)tri->dim.vtx[1].x, (short)tri->dim.vtx[1].y,
+ (short)tri->dim.vtx[1].z, 0, 0, (signed char)tri->dim.plane.normal.x,
+ (signed char)tri->dim.plane.normal.y,
+ (signed char)tri->dim.plane.normal.z, 0xFF));
+ vtxDl.push_back(gdSPDefVtxN((short)tri->dim.vtx[2].x, (short)tri->dim.vtx[2].y,
+ (short)tri->dim.vtx[2].z, 0, 0, (signed char)tri->dim.plane.normal.x,
+ (signed char)tri->dim.plane.normal.y,
+ (signed char)tri->dim.plane.normal.z, 0xFF));
+ dl.push_back(gsSPVertex((uintptr_t)&vtxDl.at(vtxDl.size() - 3), 3, 0));
+ dl.push_back(gsSP1Triangle(0, 1, 2, 0));
+ }
+ } break;
+ case COLSHAPE_QUAD: {
+ ColliderQuad* quad = (ColliderQuad*)col;
+ DrawQuad(dl, quad->dim.quad[0], quad->dim.quad[2], quad->dim.quad[3], quad->dim.quad[1]);
+ } break;
+ default:
+ break;
+ }
+ }
+}
+
+// Draws all Col Check objects
+void DrawColCheckCollision() {
+ if (showColCheckSetting == ColRenderSetting::Disabled) {
+ return;
+ }
+
+ std::vector& dl = (showColCheckSetting == ColRenderSetting::Transparent) ? xluDl : opaDl;
+ InitGfx(dl, showColCheckSetting);
+ dl.push_back(gsSPMatrix(&gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH));
+
+ CollisionCheckContext& col = gGlobalCtx->colChkCtx;
+
+ dl.push_back(gsDPSetPrimColor(0, 0, (ocColor >> 24) & 0xFF, (ocColor >> 16) & 0xFF, (ocColor >> 8) & 0xFF,
+ (ocColor >> 0) & 0xFF));
+ DrawColCheckList(dl, col.colOC, col.colOCCount);
+
+ dl.push_back(gsDPSetPrimColor(0, 0, (acColor >> 24) & 0xFF, (acColor >> 16) & 0xFF, (acColor >> 8) & 0xFF,
+ (acColor >> 0) & 0xFF));
+ DrawColCheckList(dl, col.colAC, col.colACCount);
+
+ dl.push_back(gsDPSetPrimColor(0, 0, (atColor >> 24) & 0xFF, (atColor >> 16) & 0xFF, (atColor >> 8) & 0xFF,
+ (atColor >> 0) & 0xFF));
+ DrawColCheckList(dl, col.colAT, col.colATCount);
+}
+
+// Draws a waterbox
+void DrawWaterbox(std::vector& dl, WaterBox* water, float water_max_depth = -4000.0f) {
+ // Skip waterboxes that would be disabled in current room
+ int32_t room = ((water->properties >> 13) & 0x3F);
+ if ((room != gGlobalCtx->roomCtx.curRoom.num) && (room != 0x3F)) {
+ return;
+ }
+
+ Vec3f vtx[] = {
+ { water->xMin, water->ySurface, water->zMin + water->zLength },
+ { water->xMin + water->xLength, water->ySurface, water->zMin + water->zLength },
+ { water->xMin + water->xLength, water->ySurface, water->zMin },
+ { water->xMin, water->ySurface, water->zMin },
+ { water->xMin, water_max_depth, water->zMin + water->zLength },
+ { water->xMin + water->xLength, water_max_depth, water->zMin + water->zLength },
+ { water->xMin + water->xLength, water_max_depth, water->zMin },
+ { water->xMin, water_max_depth, water->zMin },
+ };
+ DrawQuad(dl, vtx[0], vtx[1], vtx[2], vtx[3]);
+ DrawQuad(dl, vtx[0], vtx[3], vtx[7], vtx[4]);
+ DrawQuad(dl, vtx[1], vtx[0], vtx[4], vtx[5]);
+ DrawQuad(dl, vtx[2], vtx[1], vtx[5], vtx[6]);
+ DrawQuad(dl, vtx[3], vtx[2], vtx[6], vtx[7]);
+}
+
+extern "C" WaterBox zdWaterBox;
+extern "C" f32 zdWaterBoxMinY;
+
+// Draws all waterboxes
+void DrawWaterboxList() {
+ if (showWaterboxSetting == ColRenderSetting::Disabled) {
+ return;
+ }
+
+ std::vector& dl = (showWaterboxSetting == ColRenderSetting::Transparent) ? xluDl : opaDl;
+ InitGfx(dl, showWaterboxSetting);
+ dl.push_back(gsSPMatrix(&gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH));
+ dl.push_back(gsDPSetPrimColor(0, 0, (waterboxColor >> 24) & 0xFF, (waterboxColor >> 16) & 0xFF,
+ (waterboxColor >> 8) & 0xFF, (waterboxColor >> 0) & 0xFF));
+
+ CollisionHeader* col = gGlobalCtx->colCtx.colHeader;
+ for (int32_t waterboxIndex = 0; waterboxIndex < col->numWaterBoxes; waterboxIndex++) {
+ WaterBox* water = &col->waterBoxes[waterboxIndex];
+ DrawWaterbox(dl, water);
+ }
+
+ // Zora's Domain has a special, hard-coded waterbox with a bottom so you can go under the waterfall
+ if (gGlobalCtx->sceneNum == SCENE_SPOT07) {
+ DrawWaterbox(dl, &zdWaterBox, zdWaterBoxMinY);
+ }
+}
+
+// Resets a vector for the next frame and returns the capacity
+template
+size_t ResetVector(T& vec) {
+ size_t oldSize = vec.size();
+ vec.clear();
+ // Reserve slightly more space than last frame to account for variance (such as different amounts of bg actors)
+ vec.reserve(oldSize * 1.2);
+ return vec.capacity();
+}
+
+void DrawColViewer() {
+ if (gGlobalCtx == nullptr) {
+ return;
+ }
+
+ ResetVector(opaDl);
+ ResetVector(xluDl);
+ size_t vtxDlCapacity = ResetVector(vtxDl);
+ size_t mtxDlCapacity = ResetVector(mtxDl);
+
+ DrawSceneCollision();
+ DrawBgActorCollision();
+ DrawColCheckCollision();
+ DrawWaterboxList();
+
+ // Check if we used up more space than we reserved. If so, redo the drawing with our new sizes.
+ // This is because we resized the vectors while drawing, invalidating pointers to them.
+ // This only matters for the Vtx and Mtx vectors.
+ if ((vtxDl.size() > vtxDlCapacity) || (mtxDl.size() > mtxDlCapacity)) {
+ ResetVector(opaDl);
+ ResetVector(xluDl);
+ vtxDlCapacity = ResetVector(vtxDl);
+ mtxDlCapacity = ResetVector(mtxDl);
+
+ DrawSceneCollision();
+ DrawBgActorCollision();
+ DrawColCheckCollision();
+ DrawWaterboxList();
+ }
+
+ if ((vtxDl.size() > vtxDlCapacity) || (mtxDl.size() > mtxDlCapacity)) {
+ // If the sizes somehow changed between the two draws, we can't continue because we may be using invalid data
+ printf("Error drawing collision, vertex/matrix sizes didn't settle.\n");
+ return;
+ }
+
+ OPEN_DISPS(gGlobalCtx->state.gfxCtx, "", 0);
+
+ opaDl.push_back(gsSPEndDisplayList());
+ gSPDisplayList(POLY_OPA_DISP++, opaDl.data());
+
+ xluDl.push_back(gsSPEndDisplayList());
+ gSPDisplayList(POLY_XLU_DISP++, xluDl.data());
+
+ CLOSE_DISPS(gGlobalCtx->state.gfxCtx, "", 0);
+}
diff --git a/soh/soh/Enhancements/debugger/colViewer.h b/soh/soh/Enhancements/debugger/colViewer.h
new file mode 100644
index 000000000..e38da7951
--- /dev/null
+++ b/soh/soh/Enhancements/debugger/colViewer.h
@@ -0,0 +1,4 @@
+#pragma once
+
+void InitColViewer();
+void DrawColViewer();
\ No newline at end of file
diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp
index 2aaac5a1b..e2ed02705 100644
--- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp
+++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp
@@ -1,6 +1,7 @@
#include "debugSaveEditor.h"
#include "../../util.h"
#include "../libultraship/SohImGuiImpl.h"
+#include "ImGuiHelpers.h"
#include
#include
@@ -224,26 +225,6 @@ std::array songMapping = { {
SONG_MAP_ENTRY(QUEST_SONG_PRELUDE, 255, 240, 100),
} };
-// Adds a text tooltip for the previous ImGui item
-void SetLastItemHoverText(const std::string& text) {
- if (ImGui::IsItemHovered()) {
- ImGui::BeginTooltip();
- ImGui::Text(text.c_str());
- ImGui::EndTooltip();
- }
-}
-
-// Adds a "?" next to the previous ImGui item with a custom tooltip
-void InsertHelpHoverText(const std::string& text) {
- ImGui::SameLine();
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "?");
- if (ImGui::IsItemHovered()) {
- ImGui::BeginTooltip();
- ImGui::Text(text.c_str());
- ImGui::EndTooltip();
- }
-}
-
// Encapsulates what is drawn by the passed-in function within a border
template
void DrawGroupWithBorder(T&& drawFunc) {
diff --git a/soh/soh/Enhancements/debugger/debugger.cpp b/soh/soh/Enhancements/debugger/debugger.cpp
index e493375cd..83e6a94a4 100644
--- a/soh/soh/Enhancements/debugger/debugger.cpp
+++ b/soh/soh/Enhancements/debugger/debugger.cpp
@@ -1,6 +1,16 @@
#include "debugger.h"
#include "debugSaveEditor.h"
+#include "colViewer.h"
+
+extern "C" {
void Debug_Init(void) {
InitSaveEditor();
+ InitColViewer();
+}
+
+void Debug_Draw(void) {
+ DrawColViewer();
+}
+
}
diff --git a/soh/soh/Enhancements/debugger/debugger.h b/soh/soh/Enhancements/debugger/debugger.h
index 4bc0f985b..bbefd2e21 100644
--- a/soh/soh/Enhancements/debugger/debugger.h
+++ b/soh/soh/Enhancements/debugger/debugger.h
@@ -1,3 +1,12 @@
#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void Debug_Init(void);
+void Debug_Draw(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/soh/src/code/graph.c b/soh/src/code/graph.c
index 5d0495961..9f744ec02 100644
--- a/soh/src/code/graph.c
+++ b/soh/src/code/graph.c
@@ -5,6 +5,7 @@
#include
#include "soh/Enhancements/gameconsole.h"
+#include "soh/Enhancements/debugger/debugger.h"
#define GFXPOOL_HEAD_MAGIC 0x1234
#define GFXPOOL_TAIL_MAGIC 0x5678
@@ -282,6 +283,7 @@ void Graph_Update(GraphicsContext* gfxCtx, GameState* gameState) {
GameState_ReqPadData(gameState);
GameState_Update(gameState);
+ Debug_Draw();
OPEN_DISPS(gfxCtx, "../graph.c", 987);
diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c
index 6cb04bec3..6218b7d76 100644
--- a/soh/src/code/z_play.c
+++ b/soh/src/code/z_play.c
@@ -1222,64 +1222,8 @@ void Gameplay_Draw(GlobalContext* globalCtx) {
sp80 = HREG(84);
}
Scene_Draw(globalCtx);
- //Room_Draw(globalCtx, &globalCtx->roomCtx.curRoom, sp80 & 3);
- //Room_Draw(globalCtx, &globalCtx->roomCtx.prevRoom, sp80 & 3);
-
- Vec3f asd = { 0.0f, 0.0f, 0.0f };
- func_800342EC(&asd, globalCtx);
- //func_80093C80(globalCtx);
- {
- uint32_t rm;
- uint32_t blc1;
- uint32_t blc2;
-
-
- rm = Z_CMP | Z_UPD | CVG_DST_CLAMP | FORCE_BL;
- blc1 = GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1);
- blc2 = GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1);
- rm |= ZMODE_OPA;
-
- gSPLoadGeometryMode(POLY_OPA_DISP++, G_ZBUFFER | G_SHADE | G_LIGHTING);
- gSPTexture(POLY_OPA_DISP++, 0, 0, 0, G_TX_RENDERTILE, G_OFF);
-
- //gDPPipeSync(POLY_OPA_DISP++);
- gDPSetCycleType(POLY_OPA_DISP++, G_CYC_1CYCLE);
- gDPSetRenderMode(POLY_OPA_DISP++, rm | blc1, rm | blc2);
- gDPSetCombineMode(POLY_OPA_DISP++, G_CC_HILITERGB, G_CC_HILITERGB);
- gDPSetEnvColor(POLY_OPA_DISP++, 0xFF, 0xFF, 0xFF, 0xFF);
- }
- gSPMatrix(POLY_OPA_DISP++, &gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
- gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0xFF, 0, 0, 0xFF);
-
- CollisionHeader* col = globalCtx->colCtx.colHeader;
- //for (int i = 0; i < col->numPolygons; i++) {
- for (int i = 0; i < col->numPolygons / 2; i++) {
- CollisionPoly* poly = &col->polyList[i];
- u16 a = poly->flags_vIA & 0x1FFF;
- u16 b = poly->flags_vIB & 0x1FFF;
- u16 c = poly->vIC & 0x1FFF;
-
- Vec3s* va = &col->vtxList[a];
- Vec3s* vb = &col->vtxList[b];
- Vec3s* vc = &col->vtxList[c];
-#define qs105(n) ((int16_t)((n)*0x0020))
-#define gdSPDefVtxN(x, y, z, s, t, nx, ny, nz, ca) \
- ((Vtx){ .n = { .ob = { x, y, z }, .tc = { qs105(s), qs105(t) }, .n = { nx, ny, nz }, .a = ca } })
- Vtx v[3] = {
- gdSPDefVtxN(va->x, va->y, va->z, 0, 0, poly->normal.x / 0x100, poly->normal.y / 0x100,
- poly->normal.z / 0x100, 0xFF),
- gdSPDefVtxN(vb->x, vb->y, vb->z, 0, 0, poly->normal.x / 0x100, poly->normal.y / 0x100,
- poly->normal.z / 0x100, 0xFF),
- gdSPDefVtxN(vc->x, vc->y, vc->z, 0, 0, poly->normal.x / 0x100, poly->normal.y / 0x100,
- poly->normal.z / 0x100, 0xFF),
- };
-
- void* vtxBuf = Graph_Alloc(gfxCtx, sizeof(Vtx) * 3);
- memcpy(vtxBuf, v, sizeof(Vtx) * 3);
- gSPVertex(POLY_OPA_DISP++, vtxBuf, 3, 0);
- gSP1Triangle(POLY_OPA_DISP++, 0, 1, 2, 0);
-
- }
+ Room_Draw(globalCtx, &globalCtx->roomCtx.curRoom, sp80 & 3);
+ Room_Draw(globalCtx, &globalCtx->roomCtx.prevRoom, sp80 & 3);
}
}