mirror of
https://github.com/HarbourMasters/SpaghettiKart
synced 2026-09-06 19:31:06 -04:00
8629bec320
* menu_items: fix the real-bug-tier compiler warnings (#691 batch 1) Six fixes, all in menu_items.c, all verified by menu playtest on macOS: - BSWAP16(*color0++) advanced the pointer twice per pixel on little-endian builds (the macro evaluates its argument twice) and mixed bytes from two different pixels. Read first, increment separately. Note: this function (func_8009A9FC) currently has no callers, so the corruption was latent, not live. - func_8009B9D0 fell off the end on a lookup miss, returning garbage (the existing code comment already suspected this). The miss path now returns the display list head unchanged, i.e. draws nothing. - update_ok_menu_item read an uninitialized stack slot for unknown menu item types; now selects an explicit no-animation value, matching what the garbage read did in practice. - pause_menu_item_box_cursor: removed the three empty 'if (x2);' matching artifacts. The x/y/z spin state itself is untouched. - func_800A1FB0: initialized var_s4/var_s5 against the guarded-but-warned switch default. - func_800A54EC: initialized the pause cursor position pointer against its unreachable mode-switch default (would have been a null-deref class bug if mode values ever grew). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * collision: define the G_ENDDL opcode shifts, return 0 when no tyre surface found Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * replays: cast staff-ghost pointer comparisons, return 0 for empty ghost buffer Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * update_objects: return 0 from conditional step helpers, fix TLUT pointer comparison, init train draw distances Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * render_player: remove the impossible lamp range (vanilla bug, never glowed on N64 either) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * math_util_2: return the vector, not the address of the parameter slot Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Crab: remove inner declaration shadowing the initialized objectIndex Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * editor: honor InverseMatrix failure (bool was compared against 2, always true) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Track.h: drop dead null checks on array members Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * shells: drop always-true angle and surface gates (behavior unchanged, s16 made them tautological) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * particles: make the no-return particle setters void Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * effects/stubs/skybox/main: align signatures with functions that return nothing Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * menus/save: type the pak status variable as s32, return BAD_READ on the fall-off path Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * audio: remove uninitialized-read matching artifacts, init isSound Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * code_80005FD0/code_80086E70: init dead bomb kart pointer and the no_init variable Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * port/engine UI: fix printf-style format types and non-literal format string Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * render_objects: feed the unused texture param to the uninitialized img walker Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Format the changed lines per .clang-format Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Address review: strip explanatory comments, drop vec3f pointer returns Comments moved to the PR record; the one flagged worth keeping stays. vec3f_set_xyz/normalize/cross_product return void now since the out argument is the interface and nothing used the pointer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Update actors_extended.c * Add checkbox for 'Shells Shoot Straight' option * Update render_player.c * Update PortMenu.cpp * Update render_player.c * Update actors_extended.c * Update render_player.c --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: MegaMech <MegaMech@users.noreply.github.com>
550 lines
22 KiB
C++
550 lines
22 KiB
C++
#include "TrackProperties.h"
|
|
#include "port/ui/PortMenu.h"
|
|
#include "UIWidgets.h"
|
|
#include "ship/Context.h"
|
|
|
|
#include <imgui.h>
|
|
#include <map>
|
|
#include <libultraship/libultraship.h>
|
|
#include <spdlog/fmt/fmt.h>
|
|
#include "spdlog/formatter.h"
|
|
#include <common_structs.h>
|
|
#include <defines.h>
|
|
|
|
#include "port/Game.h"
|
|
|
|
#include "engine/cameras/TourCamera.h"
|
|
#include "engine/TrackBrowser.h"
|
|
#include "engine/editor/SceneManager.h"
|
|
#include "engine/registry/Registry.h"
|
|
|
|
extern "C" {
|
|
#include "code_800029B0.h"
|
|
#include "sounds.h"
|
|
#include "audio/external.h"
|
|
#include "racing/render_courses.h"
|
|
#include "render_objects.h"
|
|
}
|
|
|
|
namespace TrackEditor {
|
|
|
|
TrackPropertiesWindow::~TrackPropertiesWindow() {
|
|
SPDLOG_TRACE("destruct track properties window");
|
|
}
|
|
|
|
void TrackPropertiesWindow::DrawElement() {
|
|
if (nullptr == GetWorld()->GetTrack()) {
|
|
return;
|
|
}
|
|
|
|
if (ImGui::Button("Edit TrackInfo")) {
|
|
ImGui::OpenPopup("Edit TrackInfo");
|
|
}
|
|
|
|
DrawResourceNameEdit();
|
|
|
|
ImGui::InputFloat("Water Level", &GetWorld()->GetTrack()->Props.WaterLevel);
|
|
|
|
if (ImGui::CollapsingHeader("Camera")) {
|
|
ImGui::InputFloat("Near Perspective", &GetWorld()->GetTrack()->Props.NearPersp);
|
|
ImGui::InputFloat("Far Perspective", &GetWorld()->GetTrack()->Props.FarPersp);
|
|
if (ImGui::IsItemHovered()) {
|
|
ImGui::BeginTooltip();
|
|
ImGui::Text("Controls the far clipping distance for perspective rendering.");
|
|
ImGui::Text("Disable culling enhancement needs to be off to see a difference.");
|
|
ImGui::EndTooltip();
|
|
}
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Environment")) {
|
|
TrackPropertiesWindow::DrawFog();
|
|
TrackPropertiesWindow::DrawLight();
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("AI")) {
|
|
|
|
ImGui::InputFloat("AI Max Separation", &GetWorld()->GetTrack()->Props.AIMaximumSeparation);
|
|
ImGui::InputFloat("AI Min Separation", &GetWorld()->GetTrack()->Props.AIMinimumSeparation);
|
|
ImGui::InputInt("AI Steering Sensitivity", (int*)&GetWorld()->GetTrack()->Props.AISteeringSensitivity);
|
|
|
|
ImGui::Separator();
|
|
|
|
for (size_t i = 0; i < 32; i++) {
|
|
ImGui::InputScalar(("Element " + std::to_string(i)).c_str(), ImGuiDataType_S16, &GetWorld()->GetTrack()->Props.AIDistance[i]);
|
|
}
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Random Junk")) {
|
|
for (size_t i = 0; i < 4; i++) {
|
|
ImGui::InputFloat(fmt::format("CurveTargetSpeed[{}]", i).c_str(), &GetWorld()->GetTrack()->Props.CurveTargetSpeed[i]);
|
|
}
|
|
|
|
ImGui::Separator();
|
|
|
|
|
|
for (size_t i = 0; i < 4; i++) {
|
|
ImGui::InputFloat(fmt::format("NormalTargetSpeed[{}]", i).c_str(), &GetWorld()->GetTrack()->Props.NormalTargetSpeed[i]);
|
|
}
|
|
|
|
ImGui::Separator();
|
|
|
|
|
|
for (size_t i = 0; i < 4; i++) {
|
|
ImGui::InputFloat(fmt::format("D_0D0096B8[{}]", i).c_str(), &GetWorld()->GetTrack()->Props.D_0D0096B8[i]);
|
|
}
|
|
|
|
ImGui::Separator();
|
|
|
|
for (size_t i = 0; i < 4; i++) {
|
|
ImGui::InputFloat(fmt::format("OffTrackTargetSpeed[{}]", i).c_str(), &GetWorld()->GetTrack()->Props.OffTrackTargetSpeed[i]);
|
|
}
|
|
}
|
|
|
|
float minimapColour[3];
|
|
RGB8ToFloat((u8*)&GetWorld()->GetTrack()->Props.Minimap.Colour, minimapColour);
|
|
|
|
if (ImGui::CollapsingHeader("Minimap")) {
|
|
ImGui::Text("Position");
|
|
ImGui::SameLine();
|
|
|
|
|
|
if (ImGui::DragInt2("##MinimapPosition", &GetWorld()->GetTrack()->Props.Minimap.Pos[0].X, 1.0f)) {
|
|
}
|
|
ImGui::Text("P2 Position");
|
|
ImGui::SameLine();
|
|
if (ImGui::DragInt2("##MinimapPosition2p", &GetWorld()->GetTrack()->Props.Minimap.Pos[1].X, 1.0f)) {
|
|
}
|
|
|
|
ImGui::Text("Player Markers");
|
|
ImGui::SameLine();
|
|
if (ImGui::DragInt2("##MinimapPlayers", &GetWorld()->GetTrack()->Props.Minimap.PlayerX, 1.0f)) {
|
|
}
|
|
|
|
ImGui::Text("Player Scale Factor");
|
|
ImGui::SameLine();
|
|
if (ImGui::DragFloat("##MinimapScaleFactor", &GetWorld()->GetTrack()->Props.Minimap.PlayerScaleFactor, 0.0001f)) {
|
|
}
|
|
|
|
ImGui::Text("Finishline");
|
|
ImGui::SameLine();
|
|
ImGui::DragFloat2("##MinimapFinishlineX", &GetWorld()->GetTrack()->Props.Minimap.FinishlineX, 1.0f);
|
|
|
|
ImGui::Text("Colour");
|
|
ImGui::SameLine();
|
|
ImGui::ColorEdit3("##MinimapColour", minimapColour, 1.0f);
|
|
}
|
|
|
|
FloatToRGB8(minimapColour, (u8*)&GetWorld()->GetTrack()->Props.Minimap.Colour);
|
|
|
|
// Convert and pass to ImGui ColorEdit3
|
|
float topRight[3], bottomRight[3], bottomLeft[3], topLeft[3];
|
|
float floorTopRight[3], floorBottomRight[3], floorBottomLeft[3], floorTopLeft[3];
|
|
|
|
// Convert RGB8 (0-255) to float (0.0f to 1.0f)
|
|
RGB8ToFloat((u8*)&GetWorld()->GetTrack()->Props.Skybox.TopRight, topRight);
|
|
RGB8ToFloat((u8*)&GetWorld()->GetTrack()->Props.Skybox.BottomRight, bottomRight);
|
|
RGB8ToFloat((u8*)&GetWorld()->GetTrack()->Props.Skybox.BottomLeft, bottomLeft);
|
|
RGB8ToFloat((u8*)&GetWorld()->GetTrack()->Props.Skybox.TopLeft, topLeft);
|
|
RGB8ToFloat((u8*)&GetWorld()->GetTrack()->Props.Skybox.FloorTopRight, floorTopRight);
|
|
RGB8ToFloat((u8*)&GetWorld()->GetTrack()->Props.Skybox.FloorBottomRight, floorBottomRight);
|
|
RGB8ToFloat((u8*)&GetWorld()->GetTrack()->Props.Skybox.FloorBottomLeft, floorBottomLeft);
|
|
RGB8ToFloat((u8*)&GetWorld()->GetTrack()->Props.Skybox.FloorTopLeft, floorTopLeft);
|
|
|
|
if (ImGui::CollapsingHeader("Skybox")) {
|
|
ImGui::ColorEdit3("Skybox Top Right", topRight);
|
|
ImGui::ColorEdit3("Skybox Bottom Right", bottomRight);
|
|
ImGui::ColorEdit3("Skybox Bottom Left", bottomLeft);
|
|
ImGui::ColorEdit3("Skybox Top Left", topLeft);
|
|
ImGui::ColorEdit3("Skybox Floor Top Right", floorTopRight);
|
|
ImGui::ColorEdit3("Skybox Floor Bottom Right", floorBottomRight);
|
|
ImGui::ColorEdit3("Skybox Floor Bottom Left", floorBottomLeft);
|
|
ImGui::ColorEdit3("Skybox Floor Top Left", floorTopLeft);
|
|
}
|
|
|
|
// Convert the modified float values back to RGB8 (0-255)
|
|
FloatToRGB8(topRight, (u8*)&GetWorld()->GetTrack()->Props.Skybox.TopRight);
|
|
FloatToRGB8(bottomRight, (u8*)&GetWorld()->GetTrack()->Props.Skybox.BottomRight);
|
|
FloatToRGB8(bottomLeft, (u8*)&GetWorld()->GetTrack()->Props.Skybox.BottomLeft);
|
|
FloatToRGB8(topLeft, (u8*)&GetWorld()->GetTrack()->Props.Skybox.TopLeft);
|
|
FloatToRGB8(floorTopRight, (u8*)&GetWorld()->GetTrack()->Props.Skybox.FloorTopRight);
|
|
FloatToRGB8(floorBottomRight, (u8*)&GetWorld()->GetTrack()->Props.Skybox.FloorBottomRight);
|
|
FloatToRGB8(floorBottomLeft, (u8*)&GetWorld()->GetTrack()->Props.Skybox.FloorBottomLeft);
|
|
FloatToRGB8(floorTopLeft, (u8*)&GetWorld()->GetTrack()->Props.Skybox.FloorTopLeft);
|
|
|
|
TrackPropertiesWindow::DrawMusic();
|
|
TrackPropertiesWindow::DrawTourCamera();
|
|
}
|
|
|
|
void TrackPropertiesWindow::DrawResourceNameEdit() {
|
|
Track* track = GetWorld()->GetTrack();
|
|
if (!track) {
|
|
return;
|
|
}
|
|
|
|
static char resourceNameBuffer[128] = {};
|
|
static char nameBuffer[128] = "blank_track";
|
|
static char debugNameBuffer[128] = "blanktrack";
|
|
static char lengthBuffer[128] = "100m";
|
|
static bool initialized = false;
|
|
static std::string oldResourceName = track->ResourceName;
|
|
|
|
// Auto-sizing fills the height of the screen for a single frame.
|
|
// Because there's no content in the window in the first frame.
|
|
// This forces the window size to prevent that
|
|
ImGui::SetNextWindowSize(ImVec2(400, 275), ImGuiCond_Always);
|
|
if (ImGui::BeginPopupModal("Edit TrackInfo", nullptr,
|
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
|
|
|
// Initialize once per popup open
|
|
if (!initialized) {
|
|
strncpy(resourceNameBuffer, track->ResourceName.c_str(), sizeof(resourceNameBuffer));
|
|
resourceNameBuffer[sizeof(resourceNameBuffer) - 1] = '\0';
|
|
oldResourceName = track->ResourceName;
|
|
initialized = true;
|
|
}
|
|
|
|
ImGui::TextWrapped(
|
|
"Changing these fields will:\n"
|
|
"- Save the current track\n"
|
|
"- Reload the track\n"
|
|
"- Update the registry\n\n"
|
|
);
|
|
|
|
gEditor.Pause();
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
ImGui::InputText("ResourceName", resourceNameBuffer, IM_ARRAYSIZE(resourceNameBuffer));
|
|
ImGui::InputText("Name", GetWorld()->GetTrack()->Props.Name, IM_ARRAYSIZE(nameBuffer));
|
|
ImGui::InputText("Debug Name", GetWorld()->GetTrack()->Props.DebugName, IM_ARRAYSIZE(debugNameBuffer));
|
|
ImGui::InputText("Track Length", GetWorld()->GetTrack()->Props.TrackLength, IM_ARRAYSIZE(lengthBuffer));
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
bool cancel = ImGui::Button("Cancel", ImVec2(120, 0));
|
|
|
|
ImGui::SameLine();
|
|
|
|
bool confirm = ImGui::Button("Confirm", ImVec2(120, 0));
|
|
|
|
if (confirm) {
|
|
if (oldResourceName != resourceNameBuffer) {
|
|
track->ResourceName = resourceNameBuffer;
|
|
const TrackInfo* oldInfo = gTrackRegistry.GetInfo(oldResourceName);
|
|
TrackInfo info;
|
|
info.ResourceName = track->ResourceName;
|
|
info.Name = track->Props.Name;
|
|
info.DebugName = track->Props.DebugName;
|
|
info.Path = oldInfo->Path;
|
|
|
|
TrackEditor::SaveLevel(track, static_cast<const TrackInfo*>(&info));
|
|
auto archive = track->Archive;
|
|
gTrackRegistry.Remove(oldResourceName);
|
|
gTrackRegistry.Add(info, [info, archive]() {
|
|
auto track = std::make_unique<Track>();
|
|
track->Archive = archive;
|
|
track->ResourceName = info.ResourceName;
|
|
GetWorld()->SetCurrentTrack(std::move(track));
|
|
});
|
|
TrackBrowser::Instance->Refresh(gTrackRegistry);
|
|
gGotoMode = RACING;
|
|
|
|
}
|
|
initialized = false;
|
|
|
|
ImGui::CloseCurrentPopup();
|
|
}
|
|
|
|
if (cancel) {
|
|
initialized = false;
|
|
ImGui::CloseCurrentPopup();
|
|
}
|
|
|
|
ImGui::EndPopup();
|
|
}
|
|
}
|
|
|
|
void TrackPropertiesWindow::DrawMusic() {
|
|
const char* items[] = {
|
|
"None", "Title Screen", "Main Menu", "Wario Stadium", "Moo Moo Farm",
|
|
"Choco Mountain", "Koopa Troopa Beach", "Banshee Boardwalk", "Frappe Snowland", "Bowser's Castle",
|
|
"Kalimari Desert", "Start Grid GP/VS", "Final Lap Fanfare", "Finish 1st Place", "Finish 2nd-4th Place",
|
|
"Finish 5th-8th Place", "16", "Star Jingle", "Rainbow Road", "DK Jungle", "Game Over", "Toad's Turnpike",
|
|
"Start Grid Time Attack", "VS Battle Results", "Losing Results", "Battle Arenas", "Award Ceremony Buildup",
|
|
"Award Ceremony 1st-3rd", "Staff Roll", "Award Ceremony 4th-8th", "Luigi Raceway", "Mario Raceway",
|
|
"Royal Raceway", "Yoshi Valley", "Block Fort", "Double Deck"
|
|
};
|
|
|
|
const char* currentItem = MusicSeqToString(GetWorld()->GetTrack()->Props.Sequence); // Get the current selected value's string
|
|
|
|
if (ImGui::BeginCombo("Music Sequence", currentItem)) {
|
|
for (size_t i = 0; i < IM_ARRAYSIZE(items); ++i) {
|
|
bool isSelected = (currentItem == items[i]);
|
|
if (ImGui::Selectable(items[i], isSelected)) {
|
|
// Update the sequence when an option is selected
|
|
GetWorld()->GetTrack()->Props.Sequence = static_cast<MusicSeq>(i);
|
|
play_sequence(GetWorld()->GetTrack()->Props.Sequence); // Call play_sequence with the updated sequence
|
|
|
|
// Update currentItem after selection is made
|
|
currentItem = items[i];
|
|
}
|
|
if (isSelected) {
|
|
ImGui::SetItemDefaultFocus();
|
|
}
|
|
}
|
|
ImGui::EndCombo();
|
|
}
|
|
}
|
|
|
|
// Enum to string conversion
|
|
const char* TrackPropertiesWindow::MusicSeqToString(MusicSeq seq) {
|
|
switch (seq) {
|
|
case MUSIC_SEQ_SOUND_PLAYER: return "Sound FX Player (Don't use)";
|
|
case MUSIC_SEQ_TITLE_SCREEN: return "Title Screen";
|
|
case MUSIC_SEQ_MAIN_MENU: return "Main Menu";
|
|
case MUSIC_SEQ_WARIO_STADIUM: return "Wario Stadium";
|
|
case MUSIC_SEQ_MOO_MOO_FARM: return "Moo Moo Farm";
|
|
case MUSIC_SEQ_CHOCO_MOUNTAIN: return "Choco Mountain";
|
|
case MUSIC_SEQ_KOOPA_TROOPA_BEACH: return "Koopa Troopa Beach";
|
|
case MUSIC_SEQ_BANSHEE_BOARDWALK: return "Banshee Boardwalk";
|
|
case MUSIC_SEQ_FRAPPE_SNOWLAND: return "Frappe Snowland";
|
|
case MUSIC_SEQ_BOWSERS_CASTLE: return "Bowser's Castle";
|
|
case MUSIC_SEQ_KALIMARI_DESERT: return "Kalimari Desert";
|
|
case MUSIC_SEQ_START_GRID_GP_VS: return "Start Grid GP/VS";
|
|
case MUSIC_SEQ_FINAL_LAP_FANFARE: return "Final Lap Fanfare";
|
|
case MUSIC_SEQ_FINISH_1ST_PLACE: return "Finish 1st Place";
|
|
case MUSIC_SEQ_FINISH_2ND_4TH_PLACE: return "Finish 2nd-4th Place";
|
|
case MUSIC_SEQ_FINISH_5TH_8TH_PLACE: return "Finish 5th-8th Place";
|
|
case MUSIC_SEQ_WINNING_RESULTS: return "Winning Results";
|
|
case MUSIC_SEQ_STAR_JINGLE: return "Star Jingle";
|
|
case MUSIC_SEQ_RAINBOW_ROAD: return "Rainbow Road";
|
|
case MUSIC_SEQ_DK_JUNGLE: return "DK Jungle";
|
|
case MUSIC_SEQ_GAME_OVER: return "Game Over";
|
|
case MUSIC_SEQ_TOADS_TURNPIKE: return "Toad's Turnpike";
|
|
case MUSIC_SEQ_START_GRID_TIME_ATTACK: return "Start Grid Time Attack";
|
|
case MUSIC_SEQ_VS_BATTLE_RESULTS: return "VS Battle Results";
|
|
case MUSIC_SEQ_LOSING_RESULTS: return "Losing Results";
|
|
case MUSIC_SEQ_BATTLE_ARENAS: return "Battle Arenas";
|
|
case MUSIC_SEQ_AWARD_CEREMONY_BUILDUP: return "Award Ceremony Buildup";
|
|
case MUSIC_SEQ_AWARD_CEREMONY_1ST_3RD: return "Award Ceremony 1st-3rd";
|
|
case MUSIC_SEQ_STAFF_ROLL: return "Staff Roll";
|
|
case MUSIC_SEQ_AWARD_CEREMONY_4TH_8TH: return "Award Ceremony 4th-8th";
|
|
case MUSIC_SEQ_LUIGI_RACEWAY: return "Luigi Raceway";
|
|
case MUSIC_SEQ_MARIO_RACEWAY: return "Mario Raceway";
|
|
case MUSIC_SEQ_ROYAL_RACEWAY: return "Royal Raceway";
|
|
case MUSIC_SEQ_YOSHI_VALLEY: return "Yoshi Valley";
|
|
case MUSIC_SEQ_BLOCK_FORT: return "Block Fort";
|
|
case MUSIC_SEQ_DOUBLE_DECK: return "Double Deck";
|
|
default: return "None";
|
|
}
|
|
}
|
|
|
|
void TrackPropertiesWindow::DrawFog() {
|
|
if (ImGui::CollapsingHeader("Fog")) {
|
|
ImGui::Checkbox("Enable Fog", &bFog);
|
|
float colours[4];
|
|
|
|
// Convert rgba to floats
|
|
RGB8ToFloat((u8*)&gFogColour, colours);
|
|
colours[3] = gFogColour.a / 255.0f;
|
|
// Edit the ambient RGB colour
|
|
ImGui::ColorEdit4("Fog Colour", colours);
|
|
|
|
// Convert floats to rgba
|
|
FloatToRGB8(colours, (u8*)&gFogColour);
|
|
gFogColour.a = static_cast<u8>(colours[3] * 255.0f);
|
|
|
|
// Fog near and far planes
|
|
int val[2] = {static_cast<int>(gFogMin), static_cast<int>(gFogMax)};
|
|
|
|
ImGui::DragInt2("##MinimapPosition", &val[0], 1.0f, 0, 1000);
|
|
|
|
if (val[0] >= val[1]) {
|
|
val[0] = val[1] - 1;
|
|
}
|
|
|
|
// Clamp to allowed range just in case
|
|
val[0] = std::clamp(val[0], 0, 999);
|
|
val[1] = std::clamp(val[1], 1, 1000);
|
|
|
|
gFogMin = static_cast<int16_t>(val[0]);
|
|
gFogMax = static_cast<int16_t>(val[1]);
|
|
}
|
|
|
|
}
|
|
|
|
void TrackPropertiesWindow::DrawLight() {
|
|
// Convert and pass to ImGui ColorEdit3
|
|
|
|
|
|
for (size_t i = 0; i < 2; ++i) {
|
|
float ambient[3], diffuse[3], direction[3];
|
|
|
|
RGB8ToFloat((u8*)&D_800DC610[i].a.l.col, ambient);
|
|
RGB8ToFloat((u8*)&D_800DC610[i].l->l.col, diffuse);
|
|
RGB8ToFloat((u8*)&D_800DC610[i].l->l.dir, direction);
|
|
|
|
// Edit the ambient RGB colour
|
|
ImGui::Text("Light %zu - Ambient Colour", i + 1);
|
|
ImGui::ColorEdit3(("Ambient Colour " + std::to_string(i)).c_str(), ambient); // Modify ambient colour
|
|
|
|
// Edit the diffuse RGB colour
|
|
ImGui::Text("Light %zu - Diffuse Colour", i + 1);
|
|
ImGui::ColorEdit3(("Diffuse Colour " + std::to_string(i)).c_str(), diffuse); // Modify diffuse colour
|
|
|
|
// Edit the direction RGB colour (this could be represented as a direction vector)
|
|
ImGui::Text("Light %zu - Direction", i + 1);
|
|
ImGui::ColorEdit3(("Direction Colour " + std::to_string(i)).c_str(), direction); // Modify direction vector colour
|
|
|
|
FloatToRGB8(ambient, (u8*)&D_800DC610[i].a.l.col);
|
|
FloatToRGB8(ambient, (u8*)&D_800DC610[i].a.l.colc);
|
|
FloatToRGB8(diffuse, (u8*)&D_800DC610[i].l->l.col);
|
|
FloatToRGB8(diffuse, (u8*)&D_800DC610[i].l->l.colc);
|
|
FloatToRGB8(direction, (u8*)&D_800DC610[i].l->l.dir);
|
|
|
|
}
|
|
}
|
|
|
|
// Convert s16 colour values to float (normalized to [0, 1] range)
|
|
void TrackPropertiesWindow::RGB8ToFloat(const u8* src, float* dst) {
|
|
for (size_t i = 0; i < 3; ++i) {
|
|
dst[i] = src[i] / 255.0f; // Normalize to the range [0.0f, 1.0f]
|
|
}
|
|
}
|
|
|
|
void TrackPropertiesWindow::FloatToRGB8(const float* src, u8* dst) {
|
|
for (size_t i = 0; i < 3; ++i) {
|
|
dst[i] = static_cast<u8>(src[i] * 255.0f); // Scale to [0, 255] range
|
|
}
|
|
}
|
|
|
|
int32_t TrackPropertiesWindow::SelectedShot = -1;
|
|
int32_t TrackPropertiesWindow::SelectedKeyframe = -1;
|
|
|
|
void TrackPropertiesWindow::DrawTourCamera() {
|
|
|
|
Track* track = GetWorld()->GetTrack();
|
|
if (nullptr == track) {
|
|
return;
|
|
}
|
|
|
|
Camera* camera = gScreenOneCtx->camera;
|
|
if (nullptr == camera) {
|
|
return;
|
|
}
|
|
|
|
|
|
FVector camPos = FVector(camera->pos[0], camera->pos[1], camera->pos[2]);
|
|
FVector camLookAt = FVector(camera->lookAt[0], camera->lookAt[1], camera->lookAt[2]);
|
|
|
|
// Enable / disable
|
|
ImGui::Checkbox("Enable Tour Camera", &track->bTourEnabled);
|
|
|
|
if (!track->bTourEnabled) {
|
|
return;
|
|
}
|
|
|
|
ImGui::SeparatorText("Tour Camera Controls");
|
|
|
|
// Button to add a new empty shot
|
|
if (ImGui::Button("Add CameraShot")) {
|
|
TourCamera::CameraShot shot;
|
|
shot.Pos = camPos; // start where the camera currently is
|
|
shot.LookAt = camLookAt; // or however your camera exposes these
|
|
track->TourShots.push_back(shot);
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
|
|
// ============================
|
|
// Display Camera Shots
|
|
// ============================
|
|
for (int32_t i = 0; i < track->TourShots.size(); i++)
|
|
{
|
|
TourCamera::CameraShot& shot = track->TourShots[i];
|
|
ImGui::PushID(i);
|
|
|
|
bool open = ImGui::CollapsingHeader(
|
|
("CameraShot " + std::to_string(i)).c_str(),
|
|
ImGuiTreeNodeFlags_DefaultOpen
|
|
);
|
|
|
|
// Select the shot
|
|
if (ImGui::Selectable("Select Shot", SelectedShot == i)) {
|
|
SelectedShot = i;
|
|
}
|
|
|
|
// Delete shot
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Delete Shot")) {
|
|
track->TourShots.erase(track->TourShots.begin() + i);
|
|
if (SelectedShot == i) SelectedShot = -1;
|
|
ImGui::PopID();
|
|
break;
|
|
}
|
|
|
|
if (open) {
|
|
ImGui::Indent();
|
|
|
|
// Start pos / lookAt editing
|
|
ImGui::InputFloat3("Start Pos", &shot.Pos.x);
|
|
ImGui::InputFloat3("Start LookAt", &shot.LookAt.x);
|
|
|
|
ImGui::Spacing();
|
|
|
|
// Record keyframe from current camera
|
|
if (ImGui::Button("Record KeyFrame From Camera")) {
|
|
TourCamera::KeyFrame kf;
|
|
kf.Pos = camPos;
|
|
kf.LookAt = camLookAt;
|
|
kf.Duration = 60.0f;
|
|
shot.Frames.push_back(kf);
|
|
}
|
|
|
|
ImGui::SeparatorText("KeyFrames");
|
|
|
|
// ============================
|
|
// KeyFrame List
|
|
// ============================
|
|
for (int32_t k = 0; k < shot.Frames.size(); k++)
|
|
{
|
|
TourCamera::KeyFrame& kf = shot.Frames[k];
|
|
ImGui::PushID(k);
|
|
|
|
bool kOpen = ImGui::TreeNode(("KeyFrame " + std::to_string(k)).c_str());
|
|
|
|
// Select keyframe
|
|
if (ImGui::Selectable("Select KeyFrame", SelectedKeyframe == k))
|
|
SelectedKeyframe = k;
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Delete")) {
|
|
shot.Frames.erase(shot.Frames.begin() + k);
|
|
if (SelectedKeyframe == k) SelectedKeyframe = -1;
|
|
ImGui::PopID();
|
|
break;
|
|
}
|
|
|
|
if (kOpen) {
|
|
ImGui::InputFloat3("Position", &kf.Pos.x);
|
|
ImGui::InputFloat3("LookAt", &kf.LookAt.x);
|
|
ImGui::DragFloat("Duration", &kf.Duration, 1.0f, 1.0f, 5000.0f);
|
|
ImGui::TreePop();
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
ImGui::PopID();
|
|
}
|
|
|
|
}
|
|
|
|
}
|