diff --git a/src/code_800029B0.c b/src/code_800029B0.c index beba2e490..30263e4f9 100644 --- a/src/code_800029B0.c +++ b/src/code_800029B0.c @@ -68,7 +68,8 @@ s32 gIsMirrorMode = 0; f32 vtxStretchY = 1.0f; Lights1 D_800DC610[] = { gdSPDefLights1(175, 175, 175, 255, 255, 255, 0, 0, 120), - gdSPDefLights1(115, 115, 115, 255, 255, 255, 0, 0, 120), + //! @todo impl lighting in custom track origin value 115 instead of 209. Hack fix for lighting for now + gdSPDefLights1(209, 209, 209, 255, 255, 255, 0, 0, 120), }; UNUSED s32 pad_800029B0 = 0x80000000; s16 gCreditsCourseId = COURSE_LUIGI_RACEWAY; diff --git a/src/engine/HM_Intro.cpp b/src/engine/HM_Intro.cpp index e27d5db0c..320ea9e45 100644 --- a/src/engine/HM_Intro.cpp +++ b/src/engine/HM_Intro.cpp @@ -3,6 +3,7 @@ #include "HM_Intro.h" #include "port/Game.h" +#include "port/interpolation/FrameInterpolation.h" extern "C" { #include "main.h" @@ -64,7 +65,7 @@ void HarbourMastersIntro::HM_InitIntro() { 0x60, 20, 10, 0x49, 0x49, 0x49 ); - gEditor.AddObject("lus", &lusPos, &lusRot, &lusScale, nullptr, 1, Editor::GameObject::CollisionType::BOUNDING_BOX, 10, &DespawnValue, -1); + //gEditor.AddObject("lus", &lusPos, &lusRot, &lusScale, nullptr, 1, Editor::GameObject::CollisionType::BOUNDING_BOX, 10, &DespawnValue, -1); } void HarbourMastersIntro::HM_TickIntro() { @@ -115,6 +116,7 @@ void HarbourMastersIntro::SpagBob(FVector& pos, IRotator& rot, f32 bobAmp, f32 b } void HarbourMastersIntro::HM_DrawIntro() { + FrameInterpolation_ShouldInterpolateFrame(false); HarbourMastersIntro::Setup(); gSPMatrix(gDisplayListHead++, &gGfxPool->mtxScreen, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); @@ -122,21 +124,28 @@ void HarbourMastersIntro::HM_DrawIntro() { gDPSetRenderMode(gDisplayListHead++, G_RM_AA_ZB_OPA_SURF, G_RM_AA_ZB_OPA_SURF2); gDPSetEnvColor(gDisplayListHead++, 0x00, 0x00, 0x00, 0x00); + FrameInterpolation_RecordOpenChild("spag_ship", 0); Mat4 mtx_spaghettiShip; ApplyMatrixTransformations(mtx_spaghettiShip, _pos, _rot, _scale); render_set_position(mtx_spaghettiShip, 0); gSPDisplayList(gDisplayListHead++, ship1_spag1_mesh); + FrameInterpolation_RecordCloseChild(); + FrameInterpolation_RecordOpenChild("ship2", 0); Mat4 mtx_ship2; ApplyMatrixTransformations(mtx_ship2, _ship2Pos, _ship2Rot, _scale); render_set_position(mtx_ship2, 0); gSPDisplayList(gDisplayListHead++, ship2_SoH_mesh); + FrameInterpolation_RecordCloseChild(); + FrameInterpolation_RecordOpenChild("ship3", 0); Mat4 mtx_ship3; ApplyMatrixTransformations(mtx_ship3, _shipPos, _shipRot, _scale); render_set_position(mtx_ship3, 0); gSPDisplayList(gDisplayListHead++, ship3_2Ship_mesh); + FrameInterpolation_RecordCloseChild(); + FrameInterpolation_RecordOpenChild("hm_geo", 0); Mat4 mtx_geo; ApplyMatrixTransformations(mtx_geo, _hPos, _hRot, _hScale); render_set_position(mtx_geo, 0); @@ -146,13 +155,17 @@ void HarbourMastersIntro::HM_DrawIntro() { gSPDisplayList(gDisplayListHead++, castle_map_002_mesh); gSPDisplayList(gDisplayListHead++, road_map_001_mesh); gSPDisplayList(gDisplayListHead++, water_water1_mesh); + FrameInterpolation_RecordCloseChild(); + FrameInterpolation_RecordOpenChild("hm_lus", 0); Mat4 lusMtx; ApplyMatrixTransformations(lusMtx, lusPos, lusRot, lusScale); render_set_position(lusMtx, 0); gSPDisplayList(gDisplayListHead++, (Gfx*)"__OTR__hmintro/poweredbylus"); + FrameInterpolation_RecordCloseChild(); HarbourMastersIntro::Sync(); + FrameInterpolation_ShouldInterpolateFrame(true); } void HarbourMastersIntro::Setup() { diff --git a/src/engine/Matrix.cpp b/src/engine/Matrix.cpp index b55a9b89f..4c87d7eb7 100644 --- a/src/engine/Matrix.cpp +++ b/src/engine/Matrix.cpp @@ -74,6 +74,7 @@ void ApplyMatrixTransformations(Mat4 mtx, FVector pos, IRotator rot, FVector sca f32 sine1, cosine1; f32 sine2, cosine2; f32 sine3, cosine3; + FrameInterpolation_ApplyMatrixTransformations((Mat4*)mtx, pos, rot, scale); // Compute the sine and cosine of the orientation (Euler angles) sine1 = sins(rot.pitch); diff --git a/src/engine/courses/Course.cpp b/src/engine/courses/Course.cpp index bbc9455fe..94d634373 100644 --- a/src/engine/courses/Course.cpp +++ b/src/engine/courses/Course.cpp @@ -23,12 +23,13 @@ extern "C" { #include "render_courses.h" #include "collision.h" #include "actors.h" +#include "math_util.h" extern StaffGhost* d_mario_raceway_staff_ghost; } Course::Course() { - // Props.Name = "Course Name"; - // Props.DebugName = "CName"; + Props.SetText(Props.Name, "Blank Track", sizeof(Props.Name)); + Props.SetText(Props.DebugName, "blnktrck", sizeof(Props.DebugName)); Props.SetText(Props.CourseLength, "100m", sizeof(Props.CourseLength)); // Props.Cup = FLOWER_CUP; // Props.CupIndex = 3; @@ -134,7 +135,7 @@ void Course::LoadO2R(std::string trackPath) { } } -// Load stock track +// Load stock and o2r tracks void Course::Load() { // Load from O2R @@ -150,6 +151,8 @@ void Course::Load() { ParseCourseSections(sections, size); func_80295C6C(); Props.WaterLevel = gCourseMinY - 10.0f; + } else { + printf("Course.cpp: Custom track sections are invalid\n"); } return; } @@ -337,7 +340,7 @@ void Course::Render(struct UnkStruct_800DC5EC* arg0) { if (!TrackSectionsPtr.empty()) { gSPSetGeometryMode(gDisplayListHead++, G_SHADING_SMOOTH); gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING); - // set_track_light_direction(D_800DC610, D_802B87D4, 0, 1); + set_track_light_direction(D_800DC610, D_802B87D4, 0, 1); gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); gSPSetGeometryMode(gDisplayListHead++, G_SHADING_SMOOTH); diff --git a/src/engine/courses/Course.h b/src/engine/courses/Course.h index 0a0eff823..a1a7d2f4b 100644 --- a/src/engine/courses/Course.h +++ b/src/engine/courses/Course.h @@ -96,7 +96,7 @@ typedef struct Properties { j["Name"] = Name ? Name : ""; j["DebugName"] = DebugName ? DebugName : ""; j["CourseLength"] = CourseLength ? CourseLength : ""; - j["AIBehaviour"] = AIBehaviour ? AIBehaviour : ""; + //j["AIBehaviour"] = AIBehaviour ? AIBehaviour : ""; j["LakituTowType"] = LakituTowType; j["AIMaximumSeparation"] = AIMaximumSeparation; j["AIMinimumSeparation"] = AIMinimumSeparation; @@ -133,10 +133,22 @@ typedef struct Properties { j["MinimapFinishlineY"] = Minimap.FinishlineY; j["MinimapColour"] = {static_cast(Minimap.Colour.r), static_cast(Minimap.Colour.g), static_cast(Minimap.Colour.b)}; // SkyboxColors - assuming SkyboxColors can be serialized similarly - // j["Skybox"] = Skybox; // Implement your serialization logic here + + #define TO_INT(value) static_cast(value) + j["Skybox"] = { + TO_INT(Skybox.TopRight.r), TO_INT(Skybox.TopRight.g), TO_INT(Skybox.TopRight.b), + TO_INT(Skybox.BottomRight.r), TO_INT(Skybox.BottomRight.g), TO_INT(Skybox.BottomRight.b), + TO_INT(Skybox.BottomLeft.r), TO_INT(Skybox.BottomLeft.g), TO_INT(Skybox.BottomLeft.b), + TO_INT(Skybox.TopLeft.r), TO_INT(Skybox.TopLeft.g), TO_INT(Skybox.TopLeft.b), + TO_INT(Skybox.FloorTopRight.r), TO_INT(Skybox.FloorTopRight.g), TO_INT(Skybox.FloorTopRight.b), + TO_INT(Skybox.FloorBottomRight.r), TO_INT(Skybox.FloorBottomRight.g), TO_INT(Skybox.FloorBottomRight.b), + TO_INT(Skybox.FloorBottomLeft.r), TO_INT(Skybox.FloorBottomLeft.g), TO_INT(Skybox.FloorBottomLeft.b), + TO_INT(Skybox.FloorTopLeft.r), TO_INT(Skybox.FloorTopLeft.g), TO_INT(Skybox.FloorTopLeft.b) + }; j["Sequence"] = static_cast(Sequence); j["WaterLevel"] = static_cast(WaterLevel); + #undef CAST_TO_INT return j; } @@ -156,7 +168,7 @@ typedef struct Properties { strncpy(CourseLength, j.at("CourseLength").get().c_str(), sizeof(CourseLength) - 1); CourseLength[sizeof(CourseLength) - 1] = '\0'; // Ensure null termination - AIBehaviour = j.at("AIBehaviour").get().c_str(); + //AIBehaviour = j.at("AIBehaviour").get().c_str(); LakituTowType = j.at("LakituTowType").get(); AIMaximumSeparation = j.at("AIMaximumSeparation").get(); @@ -215,6 +227,39 @@ typedef struct Properties { Minimap.Colour.g = j.at("MinimapColour")[1].get(); Minimap.Colour.b = j.at("MinimapColour")[2].get(); //textures = nullptr; // Deserialize textures if present + + Skybox.TopRight.r = j.at("Skybox")[0].get(); + Skybox.TopRight.g = j.at("Skybox")[1].get(); + Skybox.TopRight.b = j.at("Skybox")[2].get(); + + Skybox.BottomRight.r = j.at("Skybox")[3].get(); + Skybox.BottomRight.g = j.at("Skybox")[4].get(); + Skybox.BottomRight.b = j.at("Skybox")[5].get(); + + Skybox.BottomLeft.r = j.at("Skybox")[6].get(); + Skybox.BottomLeft.g = j.at("Skybox")[7].get(); + Skybox.BottomLeft.b = j.at("Skybox")[8].get(); + + Skybox.TopLeft.r = j.at("Skybox")[9].get(); + Skybox.TopLeft.g = j.at("Skybox")[10].get(); + Skybox.TopLeft.b = j.at("Skybox")[11].get(); + + Skybox.FloorTopRight.r = j.at("Skybox")[12].get(); + Skybox.FloorTopRight.g = j.at("Skybox")[13].get(); + Skybox.FloorTopRight.b = j.at("Skybox")[14].get(); + + Skybox.FloorBottomRight.r = j.at("Skybox")[15].get(); + Skybox.FloorBottomRight.g = j.at("Skybox")[16].get(); + Skybox.FloorBottomRight.b = j.at("Skybox")[17].get(); + + Skybox.FloorBottomLeft.r = j.at("Skybox")[18].get(); + Skybox.FloorBottomLeft.g = j.at("Skybox")[19].get(); + Skybox.FloorBottomLeft.b = j.at("Skybox")[20].get(); + + Skybox.FloorTopLeft.r = j.at("Skybox")[21].get(); + Skybox.FloorTopLeft.g = j.at("Skybox")[22].get(); + Skybox.FloorTopLeft.b = j.at("Skybox")[23].get(); + Sequence = static_cast(j.at("Sequence").get()); WaterLevel = j.at("WaterLevel").get(); } diff --git a/src/engine/editor/SceneManager.cpp b/src/engine/editor/SceneManager.cpp index a2de6656c..c825431de 100644 --- a/src/engine/editor/SceneManager.cpp +++ b/src/engine/editor/SceneManager.cpp @@ -69,7 +69,7 @@ namespace Editor { void LoadLevel(std::shared_ptr archive, Course* course, std::string sceneFile) { SceneFile = sceneFile; - if (archive) { + if (archive && (course != nullptr)) { auto initData = std::make_shared(); initData->Parent = archive; initData->Format = RESOURCE_FORMAT_BINARY; diff --git a/src/port/Game.cpp b/src/port/Game.cpp index 4cea9c5e2..63faab45c 100644 --- a/src/port/Game.cpp +++ b/src/port/Game.cpp @@ -141,7 +141,8 @@ void CustomEngineInit() { gWorldInstance.AddCup(gBattleCup); /* Set default course; mario raceway */ - SelectMarioRaceway(); + //SelectMarioRaceway(); // This results in a nullptr + gWorldInstance.CurrentCourse = mario; gWorldInstance.CurrentCup = gMushroomCup; gWorldInstance.CurrentCup->CursorPosition = 3; gWorldInstance.CupIndex = 0; @@ -601,10 +602,6 @@ void* GetCourse(void) { return gWorldInstance.CurrentCourse; } -void SetCourseByClass(void* course) { - gWorldInstance.CurrentCourse = (Course*) course; -} - struct Actor* CM_GetActor(size_t index) { if (index < gWorldInstance.Actors.size()) { AActor* actor = gWorldInstance.Actors[index]; diff --git a/src/port/Game.h b/src/port/Game.h index 14cc0edf6..3cc63afc0 100644 --- a/src/port/Game.h +++ b/src/port/Game.h @@ -143,8 +143,6 @@ void* GetCourse(void); void SetCourseById(s32 course); -void SetCourseByClass(void* course); - struct Actor* CM_GetActor(size_t index); void CM_DeleteActor(size_t index); struct Actor* CM_AddBaseActor(); diff --git a/src/port/interpolation/FrameInterpolation.cpp b/src/port/interpolation/FrameInterpolation.cpp index 438c10c0a..b11eb0391 100644 --- a/src/port/interpolation/FrameInterpolation.cpp +++ b/src/port/interpolation/FrameInterpolation.cpp @@ -8,6 +8,7 @@ #include "FrameInterpolation.h" #include "matrix.h" #include "engine/Matrix.h" +#include "src/engine/CoreMath.h" extern "C" { #include "math_util.h" @@ -85,6 +86,7 @@ enum class Op { SetTranslateRotate, SetTextMatrix, SetMatrixPosRotScaleXY, + SetApplyMatrixTransformations, }; typedef pair label; @@ -108,6 +110,13 @@ union Data { u8 mode; } matrix_mult; + struct { + Mat4* matrix; + FVector pos; + IRotator rot; + FVector scale; + } matrix_applytransformations; + struct { Mat4* matrix; Vec3fInterp b; @@ -578,6 +587,13 @@ struct InterpolateCtx { mtxf_translation_x_y_rotate_z_scale_x_y(*gInterpolationMatrix, tmp32[0], tmp32[1], tmp_vec3s[0], tmp_vec3f[0]); break; } + case Op::SetApplyMatrixTransformations: { + lerp_vec3f(&tmp_vec3f, (Vec3f*)&old_op.matrix_applytransformations.pos, (Vec3f*)&new_op.matrix_applytransformations.pos); + lerp_vec3s(&tmp_vec3s, (int16_t*)&old_op.matrix_applytransformations.rot, (int16_t*)&new_op.matrix_applytransformations.rot); + lerp_vec3f(&tmp_vec3f2, (Vec3f*)&old_op.matrix_applytransformations.scale, (Vec3f*)&new_op.matrix_applytransformations.scale); + + ApplyMatrixTransformations(*gInterpolationMatrix, *(FVector*)&tmp_vec3f, *(IRotator*)&tmp_vec3s, *(FVector*)&tmp_vec3f2); + } } } } @@ -694,6 +710,12 @@ void FrameInterpolation_RecordMatrixMult(Mat4* matrix, MtxF* mf, u8 mode) { append(Op::MatrixMult).matrix_mult = { matrix, *mf, mode }; } +void FrameInterpolation_ApplyMatrixTransformations(Mat4* matrix, FVector pos, IRotator rot, FVector scale) { + if (!is_recording) + return; + append(Op::SetApplyMatrixTransformations).matrix_applytransformations = { matrix, pos, rot, scale }; +} + void FrameInterpolation_RecordMatrixTranslate(Mat4* matrix, Vec3f b) { if (!is_recording) return; diff --git a/src/port/interpolation/FrameInterpolation.h b/src/port/interpolation/FrameInterpolation.h index 30187c808..8acb0a83a 100644 --- a/src/port/interpolation/FrameInterpolation.h +++ b/src/port/interpolation/FrameInterpolation.h @@ -7,11 +7,13 @@ #ifdef __cplusplus +#include "src/engine/CoreMath.h" #include std::unordered_map FrameInterpolation_Interpolate(float step); +void FrameInterpolation_ApplyMatrixTransformations(Mat4* matrix, FVector pos, IRotator rot, FVector scale); extern "C" { #endif diff --git a/src/port/resource/importers/TrackSectionsFactory.cpp b/src/port/resource/importers/TrackSectionsFactory.cpp index 2abce35d4..64a3b0a2f 100644 --- a/src/port/resource/importers/TrackSectionsFactory.cpp +++ b/src/port/resource/importers/TrackSectionsFactory.cpp @@ -44,9 +44,10 @@ ResourceFactoryXMLTrackSectionsV0::ReadResource(std::shared_ptr file if (!FileHasValidFormatAndReader(file, initData)) { return nullptr; } - + auto section = std::make_shared(initData); - auto child = std::get>(file->Reader)->FirstChildElement("TrackSections"); + auto child = + std::get>(file->Reader)->FirstChildElement()->FirstChildElement(); while (child != nullptr) { std::string childName = std::string(child->Name()); diff --git a/src/port/ui/ContentBrowser.cpp b/src/port/ui/ContentBrowser.cpp index 5390d8a9d..ee109d290 100644 --- a/src/port/ui/ContentBrowser.cpp +++ b/src/port/ui/ContentBrowser.cpp @@ -127,7 +127,7 @@ namespace Editor { if (!track.SceneFile.empty()) { // has scene file std::string label = fmt::format("{}##{}", track.Name, i_track); if (ImGui::Button(label.c_str())) { - SetCourseByClass(track.course); + gWorldInstance.CurrentCourse = track.course; gGamestateNext = RACING; SetSceneFile(track.Archive, track.SceneFile); break; @@ -136,7 +136,7 @@ namespace Editor { std::string label = fmt::format("{} {}", ICON_FA_EXCLAMATION_TRIANGLE, track.Name); if (ImGui::Button(label.c_str())) { track.SceneFile = track.Dir + "/scene.json"; - SetCourseByClass(track.course); + gWorldInstance.CurrentCourse = track.invalidTrack.get(); SetSceneFile(track.Archive, track.SceneFile); SaveLevel(); Refresh = true; @@ -240,10 +240,10 @@ namespace Editor { auto course = std::make_unique(); course->LoadO2R(dir); - gWorldInstance.Courses.push_back(std::move(course)); LoadLevel(archive, course.get(), sceneFile); LoadMinimap(archive, course.get(), minimapFile); - Tracks.push_back({course.get(), sceneFile, name, dir, archive}); + Tracks.push_back({nullptr, course.get(), sceneFile, name, dir, archive}); + gWorldInstance.Courses.push_back(std::move(course)); } else { // The track does not have a valid scene file const std::string file = dir + "/data_track_sections"; @@ -256,9 +256,8 @@ namespace Editor { course->Id = (std::string("mods:") + name).c_str(); course->Props.SetText(course->Props.Name, name.c_str(), sizeof(course->Props.Name)); course->Props.SetText(course->Props.DebugName, name.c_str(), sizeof(course->Props.Name)); - auto archive = manager->GetArchiveFromFile(file); - Tracks.push_back({course.get(), "", name, dir, archive}); + Tracks.push_back({std::move(course), nullptr, "", name, dir, archive}); } else { printf("ContentBrowser.cpp: Track '%s' missing required track files. Cannot add to game\n Missing %s/data_track_sections file\n", name.c_str(), dir.c_str()); } diff --git a/src/port/ui/ContentBrowser.h b/src/port/ui/ContentBrowser.h index f077f7283..001d1a3b3 100644 --- a/src/port/ui/ContentBrowser.h +++ b/src/port/ui/ContentBrowser.h @@ -10,7 +10,8 @@ public: ~ContentBrowserWindow(); struct Tracks { - Course* course; + std::unique_ptr invalidTrack; // If not nullptr, user needs to create a scene file for this track. + Course* course; // A valid custom track. Used to reset the Courses array on a file system refresh. std::string SceneFile; std::string Name; std::string Dir; // Directory