1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
|
#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();
}
}
}
|