Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
SolutionExplorerWindow.cpp
Go to the documentation of this file.
1
2
5
7
12
17
19
21
32
40
41#include <IconsForkAwesome.h>
42#include <imgui_internal.h>
43
44namespace Divide {
45 namespace {
46 bool s_onlyVisibleNodes = false;
47 constexpr U8 g_maxEntryCount = 32;
48 eastl::deque<F32> g_framerateBuffer;
52 std::shared_ptr<ParticleData> g_particleEmitterData = nullptr;
53 std::shared_ptr<ParticleSource> g_particleSource = nullptr;
55 vec3<F32> g_particleAcceleration = {0.f, -20.f, 0.f};
58 }
59
61 : DockedWindow(parent, descriptor),
63 {
64 g_framerateBufferCont.reserve(g_maxEntryCount);
65 }
66
67 void SolutionExplorerWindow::printCameraNode(ProjectManager* projectManager, Camera* const camera) const {
68 if (camera == nullptr) {
69 return;
70 }
71
72 constexpr ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_SpanAvailWidth;
73 if (_filter.PassFilter(camera->resourceName().c_str())) {
74 if (ImGui::TreeNodeEx((void*)(intptr_t)camera->getGUID(), node_flags, "%s %s", ICON_FK_CAMERA, camera->resourceName().c_str())) {
75 if (ImGui::IsItemClicked()) {
76 if (projectManager->resetSelection(0, false)) {
79 } else {
81 }
82 }
83 }
84
85 ImGui::TreePop();
86 }
87 }
88 }
89
91 if (ImGui::BeginPopupContextItem("Context menu")) {
92 const SceneNode& node = sgn->getNode();
93 const bool isSubMesh = node.type() == SceneNodeType::TYPE_SUBMESH;
94 const bool isRoot = sgn->parent() == nullptr;
95
96 ImGui::Text(Util::StringFormat("{} [{}]", getIconForNode(sgn), sgn->name()).c_str());
97 ImGui::Separator();
98 if (isSubMesh) {
100 }
101 if (ImGui::Selectable(ICON_FK_USERS" Change Parent")) {
102 _childNode = sgn;
104 }
105 if (isSubMesh) {
106 PopReadOnly();
107 if (ImGui::IsItemHovered()) {
108 ImGui::SetTooltip("Can't re-parent sub-meshes!");
109 }
110 }
111 if (ImGui::Selectable(ICON_FK_CHILD" Add Child")) {
112 g_particleEmitterData.reset();
113 g_particleSource.reset();
114
115 g_nodeDescriptor = {};
116 Util::StringFormat( g_nodeDescriptor._name, "New_Child_Node_{}", sgn->getGUID());
118 g_currentNodeType = SceneNodeType::TYPE_TRANSFORM;
119 _parentNode = sgn;
120 }
121 ImGui::Separator();
122
123 if (ImGui::Selectable(ICON_FK_LOCATION_ARROW" Go To")) {
124 goToNode(sgn);
125 }
126 ImGui::Separator();
127
128 if (ImGui::Selectable(ICON_FK_FLOPPY_O" Save Changes")) {
129 saveNode(sgn);
130 }
131 if (ImGui::Selectable(ICON_FK_FILE" Load from file")) {
132 loadNode(sgn);
133 }
134 ImGui::NewLine();
135 ImGui::Separator();
136 if (isRoot) {
137 PushReadOnly();
138 }
139 if (ImGui::Selectable(ICON_FK_TRASH" Remove")) {
140 _nodeToRemove = sgn->getGUID();
141 }
142 if (isRoot) {
143 PopReadOnly();
144 if (ImGui::IsItemHovered()) {
145 ImGui::SetTooltip("Can't remove the root node!");
146 }
147 }
148 ImGui::EndPopup();
149 }
150 }
151
153 const SceneGraphNode::ChildContainer& children = sgn->getChildren();
154 SharedLock<SharedMutex> r_lock(children._lock);
155 const U32 childCount = children._count;
156 for (U32 i = 0u; i < childCount; ++i) {
158 nodeHasChildrenInView(children._data[i]))
159 {
160 return true;
161 }
162 }
163
164 return false;
165 }
166
168 SceneGraphNode* sgn,
169 const I32 nodeIDX,
170 const bool open,
171 const bool secondaryView,
172 const bool modifierPressed)
173 {
174 ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth;
175 //Conflicts with "Teleport to node on double click"
176 // | ImGuiTreeNodeFlags_OpenOnDoubleClick;
177 const bool wasSelected = secondaryView ? _tempParent != nullptr && _tempParent->getGUID() == sgn->getGUID() : sgn->hasFlag(SceneGraphNode::Flags::SELECTED);
178
180 {
182 }
183
184 if (open) {
185 node_flags |= ImGuiTreeNodeFlags_DefaultOpen;
186 }
187 if (wasSelected) {
188 node_flags |= ImGuiTreeNodeFlags_Selected;
189 }
190
191 if (sgn->getChildren()._count.load() == 0u) {
192 node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet;
193 }
194
195 const auto printNode = [&](const char* icon) {
196 if (s_onlyVisibleNodes && !Attorney::EditorSolutionExplorerWindow::isNodeInView(_parent, *sgn)) {
198 return false;
199 }
200 }
201 const bool isHovered = sgn->hasFlag(SceneGraphNode::Flags::HOVERED);
202
203 const bool isRoot = sgn->parent() == nullptr;
204 const bool nodeOpen = ImGui::TreeNodeEx((void*)(intptr_t)sgn->getGUID(),
205 node_flags,
206 "%s [%d] %s %s %s",
207 icon == nullptr ? ICON_FK_QUESTION : icon,
208 nodeIDX,
209 sgn->name().c_str(),
210 (modifierPressed && !isRoot) ? ICON_FK_CHECK_SQUARE_O : "",
211 (wasSelected ? ICON_FK_CHEVRON_CIRCLE_LEFT : isHovered ? ICON_FK_CHEVRON_LEFT : ""));
212
213 if (!secondaryView && wasSelected) {
214 drawContextMenu(sgn);
215 }
216
217 if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) {
218 if (secondaryView) {
219 _tempParent = sgn;
220 } else {
221 const bool parentSelected = !isRoot && sgn->parent()->hasFlag(SceneGraphNode::Flags::SELECTED);
222 const bool childrenSelected = sgn->getChildren()._count.load() > 0u && sgn->getChildren().getChild(0u)->hasFlag(SceneGraphNode::Flags::SELECTED);
223
224 if (modifierPressed || projectManager->resetSelection(0, false)) {
225 if (!wasSelected || parentSelected || childrenSelected) {
226 projectManager->setSelected(0, { sgn }, wasSelected);
227 }
229 }
230 }
231 }
232 if (!secondaryView && ImGui::IsMouseDoubleClicked(0) && ImGui::IsItemHovered(ImGuiHoveredFlags_None)) {
233 goToNode(sgn);
234 }
235 return nodeOpen;
236 };
237
238 if (_filter.Filters.empty()) {
239 if (printNode(getIconForNode(sgn))) {
240 const SceneGraphNode::ChildContainer& children = sgn->getChildren();
241 SharedLock<SharedMutex> r_lock(children._lock);
242 const U32 childCount = children._count;
243 for (U32 i = 0u; i < childCount; ++i) {
244 printSceneGraphNode(projectManager, children._data[i], i, false, secondaryView, modifierPressed);
245 }
246 ImGui::TreePop();
247 }
248 } else {
249 bool nodeOpen = false;
250 if (_filter.PassFilter(sgn->name().c_str())) {
251 nodeOpen = printNode(getIconForNode(sgn));
252 }
253 const SceneGraphNode::ChildContainer& children = sgn->getChildren();
254 SharedLock<SharedMutex> r_lock(children._lock);
255 const U32 childCount = children._count;
256 for (U32 i = 0u; i < childCount; ++i) {
257 printSceneGraphNode(projectManager, children._data[i], i, false, secondaryView, modifierPressed);
258 }
259 if (nodeOpen) {
260 ImGui::TreePop();
261 }
262 }
263 }
264
266 {
268
269 ProjectManager* projectManager = context().kernel().projectManager().get();
270 Scene* activeScene = projectManager->activeProject()->getActiveScene();
271
272 const bool sceneValid = activeScene->getState() == ResourceState::RES_LOADED;
273
275
276 const bool lockExplorer = !sceneValid || Attorney::EditorSolutionExplorerWindow::lockSolutionExplorer(_parent);
278 const bool modifierPressed = imguiContext.IO.KeyShift;
279 if (lockExplorer)
280 {
281 PushReadOnly();
282 }
283 ImGui::AlignTextToFramePadding();
284 ImGui::Text(ICON_FK_SEARCH" Find node: ");
285 ImGui::SameLine();
286 ImGui::PushID("GraphSearchFilter");
287 _filter.Draw("", 160);
288 ImGui::PopID();
289 ImGui::SameLine();
290 ImGui::Checkbox(ICON_FK_EYE, &s_onlyVisibleNodes);
291 if (ImGui::IsItemHovered())
292 {
293 ImGui::SetTooltip("Only visible nodes");
294 }
295 ImGui::BeginChild("SceneGraph", ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetWindowHeight() * .5f), true, 0);
296
297 if (ImGui::TreeNodeEx(activeScene->resourceName().c_str(), ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_SpanAvailWidth, "%s%s", ICON_FK_HOME, activeScene->resourceName().c_str()))
298 {
299 if ( sceneValid )
300 {
301
302 ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, ImGui::GetFontSize() * 3); // Increase spacing to differentiate leaves from expanded contents.
303 printCameraNode(projectManager, _parent.editorCamera());
304 printCameraNode(projectManager, _parent.nodePreviewCamera());
305 for (PlayerIndex i = 0; i < static_cast<PlayerIndex>(Config::MAX_LOCAL_PLAYER_COUNT); ++i)
306 {
307 printCameraNode(projectManager, Attorney::ProjectManagerCameraAccessor::playerCamera(projectManager, i, true));
308 }
309 {
310 PROFILE_SCOPE("Print SceneGraph", Profiler::Category::GUI);
311 SceneGraphNode* root = activeScene->sceneGraph()->getRoot();
312 printSceneGraphNode(projectManager, root, 0, true, false, modifierPressed);
313 }
314 }
315
316 ImGui::PopStyleVar();
317 ImGui::TreePop();
318 }
319
320 ImGui::EndChild();
321 if (lockExplorer)
322 {
323 PopReadOnly();
324 }
325 ImGui::Separator();
326
327 // Calculate and show framerate
328 static F32 max_ms_per_frame = 0;
329
330 static F32 ms_per_frame[g_maxEntryCount] = { 0 };
331 static I32 ms_per_frame_idx = 0;
332 static F32 ms_per_frame_accum = 0.0f;
333 ms_per_frame_accum -= ms_per_frame[ms_per_frame_idx];
334 ms_per_frame[ms_per_frame_idx] = ImGui::GetIO().DeltaTime * 1000.0f;
335 ms_per_frame_accum += ms_per_frame[ms_per_frame_idx];
336 ms_per_frame_idx = (ms_per_frame_idx + 1) % g_maxEntryCount;
337 const F32 ms_per_frame_avg = ms_per_frame_accum / g_maxEntryCount;
338 if (ms_per_frame_avg + Config::TARGET_FRAME_RATE / 1000.0f > max_ms_per_frame) {
339 max_ms_per_frame = ms_per_frame_avg + Config::TARGET_FRAME_RATE / 1000.0f;
340 }
341
342 // We need this bit to get a nice "flowing" feel
343 g_framerateBuffer.push_back(ms_per_frame_avg);
344 if (g_framerateBuffer.size() > g_maxEntryCount)
345 {
346 g_framerateBuffer.pop_front();
347 }
348 efficient_clear( g_framerateBufferCont );
349 g_framerateBufferCont.insert(cbegin(g_framerateBufferCont),
350 cbegin(g_framerateBuffer),
351 cend(g_framerateBuffer));
352 ImGui::PushItemWidth(-1);
353 {
354 ImGui::PlotHistogram("",
355 g_framerateBufferCont.data(),
356 to_I32(g_framerateBufferCont.size()),
357 0,
358 Util::StringFormat("{:.3f} ms/frame ({:.1f} FPS)", ms_per_frame_avg, ms_per_frame_avg > 0.01f ? 1000.0f / ms_per_frame_avg : 0.0f).c_str(),
359 0.0f,
360 max_ms_per_frame,
361 ImVec2(0, 50));
362 }
363 ImGui::PopItemWidth();
364
365 static bool performanceStatsWereEnabled = false;
366 static U32 s_maxLocksInFlight = 0u;
367 if (ImGui::CollapsingHeader(ICON_FK_TACHOMETER" Performance Stats"))
368 {
369 I32 fpsLimit = to_I32(context().config().runtime.frameRateLimit);
370 bool limit = fpsLimit > 0;
371 if (ImGui::Checkbox("Limit FPS", &limit))
372 {
373 context().config().runtime.frameRateLimit = limit ? 120 : 0;
374 context().config().changed(true);
375 }
376 if (!limit)
377 {
378 PushReadOnly();
379 }
380 if (ImGui::SliderInt("FPS limit", &fpsLimit, 10, 320))
381 {
383 context().config().changed(true);
384 }
385 if (!limit)
386 {
387 PopReadOnly();
388 }
389 PROFILE_SCOPE("Get/Print Performance Stats", Profiler::Category::GUI);
390 performanceStatsWereEnabled = context().gfx().queryPerformanceStats();
391 context().gfx().queryPerformanceStats(true);
392 const auto& rpm = _context.kernel().renderPassManager();
393
394 static std::array<I32, to_base(RenderStage::COUNT)> maxDrawCallCount{};
395 static std::array<I32, to_base(RenderStage::COUNT)> crtDrawCallCount{};
396 for (U8 i = 0u; i < to_base(RenderStage::COUNT); ++i)
397 {
398 crtDrawCallCount[i] = rpm->drawCallCount(static_cast<RenderStage>(i));
399 }
400
401 static PerformanceMetrics perfMetrics{};
402
403 {
404 const PerformanceMetrics& perfMetricsSource = context().gfx().getPerformanceMetrics();
405 perfMetrics._gpuTimeInMS = (perfMetrics._gpuTimeInMS * 0.99f + perfMetricsSource._gpuTimeInMS * 0.01f);
406 perfMetrics._verticesSubmitted = to_U64(perfMetrics._verticesSubmitted * 0.99f + perfMetricsSource._verticesSubmitted * 0.01f);
407 perfMetrics._primitivesGenerated = to_U64(perfMetrics._primitivesGenerated * 0.99f + perfMetricsSource._primitivesGenerated * 0.01f);
408 perfMetrics._tessellationPatches = to_U64(perfMetrics._tessellationPatches * 0.99f + perfMetricsSource._tessellationPatches * 0.01f);
409 perfMetrics._tessellationInvocations = to_U64(perfMetrics._tessellationInvocations * 0.99f + perfMetricsSource._tessellationInvocations * 0.01f);
410
411 // This data should be stable
412 perfMetrics._generatedRenderTargetCount = perfMetricsSource._generatedRenderTargetCount;
413 perfMetrics._queuedGPUFrames = perfMetricsSource._queuedGPUFrames;
414 perfMetrics._syncObjectsInFlight[0] = perfMetricsSource._syncObjectsInFlight[0];
415 perfMetrics._syncObjectsInFlight[1] = perfMetricsSource._syncObjectsInFlight[1];
416 perfMetrics._syncObjectsInFlight[2] = perfMetricsSource._syncObjectsInFlight[2];
417 perfMetrics._scratchBufferQueueUsage[0] = perfMetricsSource._scratchBufferQueueUsage[0];
418 perfMetrics._scratchBufferQueueUsage[1] = perfMetricsSource._scratchBufferQueueUsage[1];
419 perfMetrics._uniformBufferVRAMUsage = perfMetricsSource._uniformBufferVRAMUsage;
420 perfMetrics._bufferVRAMUsage = perfMetricsSource._bufferVRAMUsage;
421 }
422 const vec4<U32>& cullCount = context().gfx().lastCullCount();
423 static U32 cachedSyncCount[3]{};
424 static U32 cachedCamWrites[2]{};
425 if (ms_per_frame_idx % 2 == 0)
426 {
427 std::memcpy(cachedSyncCount, perfMetrics._syncObjectsInFlight, 3 * sizeof(U32));
428 std::memcpy(cachedCamWrites, perfMetrics._scratchBufferQueueUsage, 2 * sizeof(U32));
429 s_maxLocksInFlight = std::max(cachedSyncCount[0], s_maxLocksInFlight);
430 }
431
432 ImGui::NewLine();
433 ImGui::Columns(to_base(RenderStage::COUNT) + 1u, "draw_call_columns");
434 ImGui::Separator();
435
436 ImGui::Text("Data"); ImGui::NextColumn();
437 ImGui::Text("Display"); ImGui::NextColumn();
438 ImGui::Text("Shadows"); ImGui::NextColumn();
439 ImGui::Text("Reflections"); ImGui::NextColumn();
440 ImGui::Text("Refractions"); ImGui::NextColumn();
441 ImGui::Separator();
442
443 static bool maxCalls = false;
444 if (maxCalls)
445 {
446 ImGui::Text(ICON_FK_PENCIL" (Max)");
447 }
448 else
449 {
450 ImGui::Text(ICON_FK_PENCIL);
451 }
452 if (ImGui::IsItemHovered())
453 {
454 ImGui::SetTooltip("Draw Calls. Click to toggle between Max Calls and Current Calls");
455 }
456 if (ImGui::IsItemClicked())
457 {
458 maxCalls = !maxCalls;
459 }
460 ImGui::NextColumn();
461
462 for (U8 i = 0u; i < to_base(RenderStage::COUNT); ++i)
463 {
464 ImGui::Text("%d", maxCalls ? maxDrawCallCount[i] : crtDrawCallCount[i]);
465 if (ImGui::IsItemHovered())
466 {
467 if (maxCalls)
468 {
469 ImGui::SetTooltip("Current calls: %d", crtDrawCallCount[i]);
470 }
471 else
472 {
473 ImGui::SetTooltip("Max calls: %d", maxDrawCallCount[i]);
474 }
475 }
476 ImGui::NextColumn();
477 }
478
479 ImGui::Text(ICON_FK_EYE);
480 if (ImGui::IsItemHovered())
481 {
482 ImGui::SetTooltip("Visible Nodes");
483 }
484 ImGui::NextColumn();
485
486 for (U8 i = 0u; i < to_base(RenderStage::COUNT); ++i)
487 {
488 ImGui::Text("%d", rpm->getLastTotalBinSize(static_cast<RenderStage>(i)));
489 ImGui::NextColumn();
490 }
491
492 ImGui::Columns(1);
493 ImGui::Separator();
494 ImGui::NewLine();
495 bool enableHiZ = context().gfx().enableOcclusionCulling();
496 ImGui::PushID("ToggleHiZCheckBox");
497 if (ImGui::Checkbox("", &enableHiZ))
498 {
499 context().gfx().enableOcclusionCulling(enableHiZ);
500 }
501 if (ImGui::IsItemHovered())
502 {
503 ImGui::SetTooltip("Enable / Disable GPU Hi-Z occlusion culling");
504 }
505 ImGui::PopID();
506 ImGui::SameLine();
507 ImGui::Text("HiZ Cull Counts: %d | %d | %d | %d", cullCount.x, cullCount.y, cullCount.z, cullCount.w);
508
509 ImGui::NewLine();
510 ImGui::Text("GPU Frame Time: %.2f ms", perfMetrics._gpuTimeInMS);
511 ImGui::NewLine();
512 ImGui::Text("Submitted Vertices: %s", Util::commaprint(perfMetrics._verticesSubmitted));
513 ImGui::NewLine();
514 ImGui::Text("Primitves Generated: %s", Util::commaprint(perfMetrics._primitivesGenerated));
515 ImGui::NewLine();
516 ImGui::Text("Tessellation Patches: %s", Util::commaprint(perfMetrics._tessellationPatches));
517 ImGui::NewLine();
518 ImGui::Text("Tessellation Invocations: %s", Util::commaprint(perfMetrics._tessellationInvocations));
519 ImGui::NewLine();
520 ImGui::Text("Generated Render Targets: %d", perfMetrics._generatedRenderTargetCount);
521 ImGui::NewLine();
522 ImGui::Text("Queued GPU Frames: %d", perfMetrics._queuedGPUFrames);
523 ImGui::NewLine();
524 ImGui::Text("Per Frame shader uniforms VRAM usage: %.2f Kb", (perfMetrics._uniformBufferVRAMUsage / 1024.f));
525 ImGui::NewLine();
526 ImGui::Text("Total buffers VRAM usage: %.2f Mb", (perfMetrics._bufferVRAMUsage / 1024.f / 1024.f));
527 ImGui::NewLine();
528 ImGui::Text("Sync objects in flight : %d / %d / %d Max: %d", cachedSyncCount[0], cachedSyncCount[1], cachedSyncCount[2], s_maxLocksInFlight);
529
530 if (ImGui::IsItemHovered())
531 {
532 ImGui::SetTooltip("[ Current Frame - 2 ] / [ Current Frame - 1] / [ Current Frame ]");
533 }
534
535 ImGui::Text("Cam Buffer Writes: %d | Render Buffer Writes: %d", cachedCamWrites[0], cachedCamWrites[1]);
536
537 ImGui::NewLine();
538 ImGui::Separator();
539 ImGui::NewLine();
540 for (U8 i = 0u; i < to_base(RenderStage::COUNT); ++i)
541 {
542 maxDrawCallCount[i] = std::max(rpm->drawCallCount(static_cast<RenderStage>(i)), maxDrawCallCount[i]);
543 }
544 }
545 else
546 {
547 if (!performanceStatsWereEnabled && context().gfx().queryPerformanceStats())
548 {
549 context().gfx().queryPerformanceStats(false);
550 }
551 s_maxLocksInFlight = 0u;
552 }
553
554 if (ImGui::CollapsingHeader( ICON_FK_SUN_O " / " ICON_FK_MOON_O " Day/Night Settings", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_SpanAvailWidth))
555 {
556 bool dayNightEnabled = activeScene->dayNightCycleEnabled();
557 if (ImGui::Checkbox("Enable day/night cycle", &dayNightEnabled))
558 {
559 activeScene->dayNightCycleEnabled(dayNightEnabled);
560 }
561
562 ImGui::Text(ICON_FK_CLOCK_O" Time of Day:");
563 SimpleTime time = activeScene->getTimeOfDay();
564 SimpleLocation location = activeScene->getGeographicLocation();
565
566 constexpr U8 min = 0u;
567 constexpr U8 maxHour = 24u;
568 constexpr U8 maxMinute = 59u;
569
570 const bool hourChanged = ImGui::SliderScalar("Hour", ImGuiDataType_U8, &time._hour, &min, &maxHour, "%02d");
571 const bool minutesChanged = ImGui::SliderScalar("Minute", ImGuiDataType_U8, &time._minutes, &min, &maxMinute, "%02d");
572 if (hourChanged || minutesChanged)
573 {
574 activeScene->setTimeOfDay(time);
575 }
576 F32 timeFactor = activeScene->getDayNightCycleTimeFactor();
577 if (ImGui::InputFloat("Time factor", &timeFactor))
578 {
579 activeScene->setDayNightCycleTimeFactor(CLAMPED(timeFactor, -500.f, 500.f));
580 }
581
582 ImGui::Text(ICON_FK_GLOBE_W" Global positioning:");
583 const bool latitudeChanged = ImGui::SliderFloat("Latitude", &location._latitude, -90.f, 90.f, "%.6f");
584 const bool longitudeChanged = ImGui::SliderFloat("Longitude", &location._longitude, -180.f, 180.f, "%.6f");
585 if (latitudeChanged || longitudeChanged)
586 {
587 activeScene->setGeographicLocation(location);
588 }
589
590 if ( ImGui::CollapsingHeader( "Day/Night details", ImGuiTreeNodeFlags_SpanAvailWidth ) )
591 {
592 const SunInfo sun = activeScene->getCurrentSunDetails();
593 const vec3<F32> sunPosition = activeScene->getSunPosition();
594 const vec3<F32> sunDirection = activeScene->getSunDirection();
595
596 const Angle::DEGREES<F32> sunAltitude = Angle::RadiansToDegrees( sun.altitude );
597 const Angle::DEGREES<F32> sunAzimuth = Angle::RadiansToDegrees( sun.azimuth );
598 constexpr Angle::DEGREES<F32> twilightDegrees{ -18.f };
599 constexpr Angle::DEGREES<F32> sunriseAzimuth{ 70.f };
600 constexpr Angle::DEGREES<F32> sunsetAzimuth{ 280.f };
601 const bool isNight = sunAltitude < twilightDegrees;
602 const bool isTwilight = IS_IN_RANGE_INCLUSIVE( sunAltitude, twilightDegrees, 0.f );
603 const bool isDawn = isTwilight && sunAzimuth < sunriseAzimuth;
604 const bool isDusk = isTwilight && sunAzimuth > sunsetAzimuth;
605
606 {
607 ImGui::Text( ICON_FK_CLOCK_O ); ImGui::SameLine();
608 ImGui::Text("Sunset: %02d:%02d", sun.sunsetTime._hour, sun.sunsetTime._minutes);
609 ImGui::SameLine(); ImGui::Text(" | "); ImGui::SameLine();
610 ImGui::Text("Sunrise: %02d:%02d", sun.sunriseTime._hour, sun.sunriseTime._minutes);
611 ImGui::SameLine(); ImGui::Text(" | "); ImGui::SameLine();
612 ImGui::Text("Noon: %02d:%02d", sun.noonTime._hour, sun.noonTime._minutes);
613 }
614 {
615 ImGui::Text( ICON_FK_SUN_O ); ImGui::SameLine();
616 ImGui::Text("Sun Pos|Dir: (%1.2f, %1.2f, %1.2f) | (%1.2f, %1.2f, %1.2f)", sunPosition.x, sunPosition.y, sunPosition.z, sunDirection.x, sunDirection.y, sunDirection.z);
617 }
618 {
619 ImGui::Text( ICON_FK_SUN_O ); ImGui::SameLine();
620 ImGui::Text( "Sun altitude | Max altitude : (%3.2f | %3.2f) degrees", sunAltitude, sun.altitudeMax );
621 }
622 {
623 ImGui::Text( ICON_FK_SUN_O ); ImGui::SameLine();
624 ImGui::Text( "Sun azimuth | Declination : (%3.2f | %3.2f) degrees", sun.azimuth, sun.declination );
625 }
626 {
627 ImGui::Text( ICON_FK_MOON_O ); ImGui::SameLine();
628 ImGui::Text( "Time of day: %s", isNight ? "Night" : isDawn ? "Dawn" : isDusk ? "Dusk" : "Day" );
629 }
630 }
631 }
632
637 }
638
640 {
641 if (_nodeToRemove == -1)
642 {
643 return;
644 }
645
646 Util::OpenCenteredPopup("Confirm Remove");
647
648 if (ImGui::BeginPopupModal("Confirm Remove", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
649 {
650 ImGui::Text("Are you sure you want remove the selected node [ %zu ]?", _nodeToRemove);
651 ImGui::Separator();
652
653 if (ImGui::Button("Cancel", ImVec2(120, 0)))
654 {
655 ImGui::CloseCurrentPopup();
656 _nodeToRemove = -1;
657 }
658 ImGui::SetItemDefaultFocus();
659 ImGui::SameLine();
660 if (ImGui::Button("Yes", ImVec2(120, 0)))
661 {
662
663 ImGui::CloseCurrentPopup();
665 _nodeToRemove = -1;
666 }
667 ImGui::EndPopup();
668 }
669 }
670
672 {
674 {
675 return;
676 }
677
678 Util::OpenCenteredPopup("Confirm Re-parent");
679
680 if (ImGui::BeginPopupModal("Confirm Re-parent", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
681 {
682 ImGui::Text("Are you sure you want change the selected node's [ %s ] parent?", _childNode->name().c_str());
683 ImGui::Text("Old Parent [ %s ] | New Parent [ %s ]", _childNode->parent()->name().c_str(), _tempParent->name().c_str());
684 ImGui::Separator();
685
686 if (ImGui::Button("Cancel", ImVec2(120, 0)))
687 {
688 ImGui::CloseCurrentPopup();
690 }
691 ImGui::SetItemDefaultFocus();
692 ImGui::SameLine();
693 if (ImGui::Button("Yes", ImVec2(120, 0)))
694 {
696 _childNode = nullptr;
697 _tempParent = nullptr;
699 ImGui::CloseCurrentPopup();
701 }
702 ImGui::EndPopup();
703 }
704 }
705
707 {
708 if (_parentNode == nullptr)
709 {
710 return;
711 }
712
713 Util::OpenCenteredPopup("Create New Node");
714
715 if (ImGui::BeginPopupModal("Create New Node", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
716 ImGui::Text("Creating a child node for SGN [ %d ][ %s ]?", _parentNode->getGUID(), _parentNode->name().c_str());
717 ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);
718 ImGui::BeginChild("Node Properties", ImVec2(0, 400), true, 0);
719
720 static char buf[64];
721 ImGui::Text( "Name:" ); ImGui::SameLine();
722 if (ImGui::InputText("##Name:", &buf[0], 61))
723 {
724 g_nodeDescriptor._name = buf;
725 }
726
727 const char* currentType = Names::sceneNodeType[to_base(g_currentNodeType)];
728 if (ImGui::BeginCombo("Node Type", currentType, ImGuiComboFlags_PopupAlignLeft))
729 {
730 for (U8 t = 0; t < to_U8(SceneNodeType::COUNT); ++t)
731 {
732 const SceneNodeType type = static_cast<SceneNodeType>(t);
733 const bool isSelected = g_currentNodeType == type;
734 const bool valid = type != SceneNodeType::TYPE_SKY &&
736 !Is3DObject(type);
737
738 if (ImGui::Selectable(Names::sceneNodeType[t], isSelected, valid ? 0 : ImGuiSelectableFlags_Disabled))
739 {
740 g_currentNodeType = type;
741 }
742 }
743 ImGui::EndCombo();
744 }
745 ImGui::BeginChild("Components", ImVec2(0, 260), true, 0);
746
747 U32& componentMask = g_nodeDescriptor._componentMask;
748 if (g_currentNodeType == SceneNodeType::TYPE_WATER || g_currentNodeType == SceneNodeType::TYPE_PARTICLE_EMITTER)
749 {
750 componentMask |= to_U32(ComponentType::NAVIGATION) |
753 }
754 else if (g_currentNodeType == SceneNodeType::TYPE_INFINITEPLANE)
755 {
756 componentMask |= to_U32(ComponentType::RENDERING);
757 }
758
759 if (g_currentNodeType == SceneNodeType::TYPE_PARTICLE_EMITTER)
760 {
761 componentMask |= to_U32(ComponentType::SELECTION);
762 }
763
764 for (auto i = 1u; i < to_base(ComponentType::COUNT) + 1; ++i)
765 {
766 const U32 componentBit = 1 << i;
767 bool required = componentBit == to_U32(ComponentType::TRANSFORM) ||
768 componentBit == to_U32(ComponentType::BOUNDS);
769
770 if (g_currentNodeType == SceneNodeType::TYPE_WATER ||
771 g_currentNodeType == SceneNodeType::TYPE_PARTICLE_EMITTER)
772 {
773 required = required ||
774 componentBit == to_U32(ComponentType::NAVIGATION) ||
775 componentBit == to_U32(ComponentType::RIGID_BODY) ||
776 componentBit == to_U32(ComponentType::RENDERING);
777 if (g_currentNodeType == SceneNodeType::TYPE_PARTICLE_EMITTER)
778 {
779 required = required || componentBit == to_U32(ComponentType::SELECTION);
780 }
781 }
782 else if (g_currentNodeType == SceneNodeType::TYPE_INFINITEPLANE)
783 {
784 required = required || componentBit == to_U32(ComponentType::RENDERING);
785 }
786
787 const bool invalid = componentBit == to_U32(ComponentType::INVERSE_KINEMATICS) ||
788 componentBit == to_U32(ComponentType::ANIMATION) ||
789 componentBit == to_U32(ComponentType::RAGDOLL);
790 if (required || invalid)
791 {
792 PushReadOnly();
793 }
794
795 bool componentEnabled = componentMask & componentBit;
796 const char* compLabel = TypeUtil::ComponentTypeToString(static_cast<ComponentType>(componentBit));
797 if (ImGui::Checkbox(compLabel, &componentEnabled))
798 {
799 componentMask |= componentBit;
800 }
801 if (ImGui::IsItemHovered())
802 {
803 if (required)
804 {
805 ImGui::SetTooltip("Required component for current node type!");
806 }
807 else if (invalid)
808 {
809 ImGui::SetTooltip("Component type not (yet) supported!");
810 }
811 }
812 if (required || invalid)
813 {
814 PopReadOnly();
815 }
816 }
817
818 ImGui::EndChild();
819 ImGui::Separator();
820 bool nodeDynamic = g_nodeDescriptor._usageContext == NodeUsageContext::NODE_DYNAMIC;
821 if (ImGui::Checkbox("Dynamic", &nodeDynamic))
822 {
824 }
825 if (ImGui::IsItemHovered())
826 {
827 ImGui::SetTooltip("Static or dynamic node? Affects navigation, collision detection and other systems.");
828 }
829
830 ImGui::Checkbox("Serializable", &g_nodeDescriptor._serialize);
831 if (ImGui::IsItemHovered())
832 {
833 ImGui::SetTooltip("State is saved and loaded to and from external files?");
834 }
835 ImGui::EndChild();
836 ImGui::PopStyleVar();
837
839
840 if (ImGui::Button("Cancel", ImVec2(120, 0)))
841 {
842 ImGui::CloseCurrentPopup();
843 _parentNode = nullptr;
844 }
845 ImGui::SetItemDefaultFocus();
846 ImGui::SameLine();
847
848 if (ImGui::Button("Yes", ImVec2(120, 0)))
849 {
850 createNode();
852
853 ImGui::CloseCurrentPopup();
854
855 _parentNode = nullptr;
856 }
857 ImGui::EndPopup();
858 }
859 }
860
862 {
863 if (g_currentNodeType == SceneNodeType::TYPE_PARTICLE_EMITTER)
864 {
865 ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);
866 ImGui::BeginChild("Type Specific Properties", ImVec2(0, 200), true, 0);
867 if (g_particleEmitterData == nullptr)
868 {
869 constexpr U32 options =
875 g_particleEmitterData = std::make_shared<ParticleData>(context().gfx(), 1000, options);
876 g_particleSource = std::make_shared<ParticleSource>(context().gfx(), 250.f);
877
878 g_particleEmitterData->_textureFileName = "particle.DDS";
879
880 std::shared_ptr<ParticleBoxGenerator> boxGenerator = std::make_shared<ParticleBoxGenerator>();
881 boxGenerator->halfExtent({ 0.3f, 0.0f, 0.3f });
882 g_particleSource->addGenerator(boxGenerator);
883
884 std::shared_ptr<ParticleColourGenerator> colGenerator = std::make_shared<ParticleColourGenerator>();
885 colGenerator->_minStartCol.set(g_particleStartColour);
886 colGenerator->_maxStartCol.set(g_particleStartColour);
887 colGenerator->_minEndCol.set(g_particleEndColour);
888 colGenerator->_maxEndCol.set(g_particleEndColour);
889 g_particleSource->addGenerator(colGenerator);
890
891 std::shared_ptr<ParticleVelocityGenerator> velGenerator = std::make_shared<ParticleVelocityGenerator>();
892 velGenerator->_minStartVel = { -1.0f, 0.22f, -1.0f };
893 velGenerator->_maxStartVel = { 1.0f, 3.45f, 1.0f };
894 g_particleSource->addGenerator(velGenerator);
895
896 const std::shared_ptr<ParticleTimeGenerator> timeGenerator = std::make_shared<ParticleTimeGenerator>();
897 timeGenerator->_minTime = 8.5f;
898 timeGenerator->_maxTime = 20.5f;
899 g_particleSource->addGenerator(timeGenerator);
900 }
901
902 PushReadOnly();
903 ImGui::Text( "Texture File Name:" ); ImGui::SameLine();
904 ImGui::InputText("##Texture File Name:", g_particleEmitterData->_textureFileName.data(), 128);
905 PopReadOnly();
906
907 U32 componentMask = g_particleEmitterData->optionsMask();
908 U32 particleCount = g_particleEmitterData->totalCount();
909 ImGui::Text( "Particle Count:" ); ImGui::SameLine();
910 if (ImGui::InputScalar("##Particle Count:", ImGuiDataType_U32, &particleCount))
911 {
912 if (particleCount == 0)
913 {
914 particleCount = 1;
915 }
916 g_particleEmitterData->generateParticles(particleCount, componentMask);
917 }
918
919 F32 emitRate = g_particleSource->emitRate();
920 ImGui::Text( "Emit rate:" ); ImGui::SameLine();
921 if (ImGui::InputFloat("##Emit rate:", &emitRate))
922 {
923 if (emitRate <= EPSILON_F32 )
924 {
925 emitRate = 1.0f;
926 }
927 g_particleSource->updateEmitRate(emitRate);
928 }
929
930 for (U8 i = 1; i < to_U8(ParticleDataProperties::COUNT) + 1; ++i)
931 {
932 const U32 componentBit = 1 << i;
933 bool componentEnabled = componentMask & componentBit;
934 const char* compLabel = Names::particleDataProperties[i - 1];
935 if (ImGui::Checkbox(compLabel, &componentEnabled))
936 {
937 componentMask |= componentBit;
938 g_particleEmitterData->generateParticles(particleCount, componentMask);
939 }
940 }
941
942 ImGui::EndChild();
943 ImGui::PopStyleVar();
944 }
945 }
946
948 {
950 }
951
953 {
955 }
956
958 {
960 }
961
963 {
964 switch ( g_currentNodeType )
965 {
967 {
968 const ResourceDescriptor<ParticleEmitter> descriptor( g_nodeDescriptor._name + "_node" );
970 if ( handle == INVALID_HANDLE<ParticleEmitter>)
971 {
972 return;
973 }
974
975 ResourcePtr<ParticleEmitter> emitter = Get(handle);
976 DIVIDE_ASSERT( emitter != nullptr );
977
978 if ( emitter->initData( g_particleEmitterData ) )
979 {
980 emitter->addSource( g_particleSource );
981
982 std::shared_ptr<ParticleEulerUpdater> eulerUpdater = std::make_shared<ParticleEulerUpdater>( context() );
983 eulerUpdater->_globalAcceleration.set( g_particleAcceleration );
984 emitter->addUpdater( eulerUpdater );
985 const std::shared_ptr<ParticleFloorUpdater> floorUpdater = std::make_shared<ParticleFloorUpdater>( context() );
986 floorUpdater->_bounceFactor = g_particleBounceFactor;
987 emitter->addUpdater( floorUpdater );
988 emitter->addUpdater( std::make_shared<ParticleBasicTimeUpdater>( context() ) );
989 emitter->addUpdater( std::make_shared<ParticleBasicColourUpdater>( context() ) );
990 }
991
992 g_nodeDescriptor._nodeHandle = FromHandle(handle);
993 _parentNode->addChildNode( g_nodeDescriptor );
994
995 g_particleEmitterData.reset();
996 g_particleSource.reset();
997
998 }
1010
1012 }
1013
1014 return;
1015 }
1016
1018 {
1020 {
1021 Util::OpenCenteredPopup("Select New Parent");
1022
1023 if (ImGui::BeginPopupModal("Select New Parent", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
1024 {
1025 ProjectManager* projectManager = context().kernel().projectManager().get();
1026 SceneGraphNode* root = projectManager->activeProject()->getActiveScene()->sceneGraph()->getRoot();
1027
1028 ImGui::Text("Selecting a new parent for SGN [ %d ][ %s ]?", _childNode->getGUID(), _childNode->name().c_str());
1029
1030 if (ImGui::BeginChild("SceneGraph", ImVec2(0, 400), true, 0))
1031 {
1032 ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, ImGui::GetFontSize() * 3); // Increase spacing to differentiate leaves from expanded contents.
1033 printSceneGraphNode(projectManager, root, 0, true, true, false);
1034 ImGui::PopStyleVar();
1035
1036 ImGui::EndChild();
1037 }
1038
1039 if (ImGui::Button("Cancel", ImVec2(120, 0)))
1040 {
1041 _childNode = nullptr;
1042 _tempParent = nullptr;
1043
1044 ImGui::CloseCurrentPopup();
1046 }
1047
1048 ImGui::SameLine();
1049 if (ImGui::Button("Done", ImVec2(120, 0)))
1050 {
1052
1053 ImGui::CloseCurrentPopup();
1055 }
1056 ImGui::EndPopup();
1057 }
1058 }
1059 }
1060} //namespace Divide
#define DIVIDE_ASSERT(...)
#define DIVIDE_UNEXPECTED_CALL()
#define PROFILE_SCOPE_AUTO(CATEGORY)
Definition: Profiler.h:87
#define PROFILE_SCOPE(NAME, CATEGORY)
Definition: Profiler.h:86
static void setPreviewNode(Editor &editor, SceneGraphNode *previewNode) noexcept
Definition: Editor.h:721
static ImGuiContext & getImGuiContext(Editor &editor, const Editor::ImGuiContextType type) noexcept
Definition: Editor.h:686
static void registerUnsavedSceneChanges(Editor &editor) noexcept
Definition: Editor.h:656
static void queueRemoveNode(Editor &editor, const I64 nodeGUID)
Definition: Editor.h:486
static BoundingSphere teleportToNode(const Editor &editor, Camera *camera, const SceneGraphNode *targetNode)
Definition: Editor.h:471
static void saveNode(const Editor &editor, const SceneGraphNode *targetNode)
Definition: Editor.h:476
static void setSelectedCamera(Editor &editor, Camera *camera) noexcept
Definition: Editor.h:451
static bool lockSolutionExplorer(const Editor &editor)
Definition: Editor.h:491
static bool isNodeInView(const Editor &editor, const SceneGraphNode &node)
Definition: Editor.h:496
static void loadNode(const Editor &editor, SceneGraphNode *targetNode)
Definition: Editor.h:481
static const Camera * getSelectedCamera(const Editor &editor) noexcept
Definition: Editor.h:456
static Camera * playerCamera(const Divide::ProjectManager *mgr, const bool skipOverride=false) noexcept
const Descriptor & descriptor() const noexcept
Definition: DockedWindow.h:69
const char * getIconForNode(const SceneGraphNode *sgn) noexcept
PerformanceMetrics & getPerformanceMetrics() noexcept
Definition: GFXDevice.inl:194
FORCE_INLINE I64 getGUID() const noexcept
Definition: GUIDWrapper.h:51
PlatformContext & context() noexcept
Kernel & kernel() noexcept
Editor & editor() noexcept
GFXDevice & gfx() noexcept
Configuration & config() noexcept
void setSelected(PlayerIndex idx, const vector< SceneGraphNode * > &SGNs, bool recursive)
bool resetSelection(PlayerIndex idx, const bool resetIfLocked)
ResourceState getState() const noexcept
Definition: Resource.cpp:17
void setParent(SceneGraphNode *parent, bool defer=false)
Changing a node's parent means removing this node from the current parent's child list and appending ...
SceneGraphNode * addChildNode(const SceneGraphNodeDescriptor &descriptor)
Add child node increments the node's ref counter if the node was already added to the scene graph.
T & getNode() noexcept
bool hasFlag(const Flags flag) const noexcept
Returns true only if the current node has the specified flag. Does not check children!
ChildContainer & getChildren() noexcept
vec3< F32 > getSunPosition() const
Definition: Scene.cpp:2300
const SimpleTime & getTimeOfDay() const noexcept
Definition: Scene.cpp:2284
vec3< F32 > getSunDirection() const
Definition: Scene.cpp:2309
const SimpleLocation & getGeographicLocation() const noexcept
Definition: Scene.cpp:2295
void setTimeOfDay(const SimpleTime &time) noexcept
Definition: Scene.cpp:2278
void setGeographicLocation(const SimpleLocation &location) noexcept
Definition: Scene.cpp:2289
SunInfo getCurrentSunDetails() const noexcept
Definition: Scene.cpp:2319
void setDayNightCycleTimeFactor(F32 factor) noexcept
Negative values should work.
Definition: Scene.cpp:2268
F32 getDayNightCycleTimeFactor() const noexcept
Definition: Scene.cpp:2273
void printCameraNode(ProjectManager *projectManager, Camera *const camera) const
void saveNode(const SceneGraphNode *sgn) const
void drawContextMenu(SceneGraphNode *sgn)
bool _reparentSelectRequested
Used when changing parents.
void loadNode(SceneGraphNode *sgn) const
void goToNode(const SceneGraphNode *sgn) const
bool nodeHasChildrenInView(const SceneGraphNode *sgn) const
void printSceneGraphNode(ProjectManager *projectManager, SceneGraphNode *sgn, I32 nodeIDX, bool open, bool secondaryView, bool modifierPressed)
SolutionExplorerWindow(Editor &parent, PlatformContext &context, const Descriptor &descriptor)
SceneGraphNode * _parentNode
Used for adding child nodes.
constexpr T RadiansToDegrees(T angleRadians) noexcept
Return the degree equivalent of the given radian value.
Definition: MathHelper.inl:436
constexpr U16 TARGET_FRAME_RATE
Application desired framerate for physics and input simulations.
Definition: config.h:97
constexpr U8 MAX_LOCAL_PLAYER_COUNT
Maximum number of players we support locally. We store per-player data such as key-bindings,...
Definition: config.h:144
FColour4 WHITE
Random stuff added for convenience.
Definition: Colours.cpp:8
const char * sceneNodeType[to_base(SceneNodeType::COUNT)+1u]
Definition: SceneNode.cpp:21
static const char * particleDataProperties[]
Definition: ParticleData.h:53
constexpr Optick::Category::Type GUI
Definition: Profiler.h:64
const char * ComponentTypeToString(const ComponentType compType) noexcept
Str StringFormat(const char *fmt, Args &&...args)
void OpenCenteredPopup(const char *name, ImGui::ImGuiPopupFlags popup_flags=0)
char * commaprint(U64 number) noexcept
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
constexpr U32 to_U32(const T value)
constexpr U64 to_U64(const T value)
bool IS_IN_RANGE_INCLUSIVE(const T x, const U min, const U max) noexcept
void PushReadOnly(const bool fade)
Definition: Editor.cpp:2961
int32_t I32
T * ResourcePtr
Definition: Resource.h:112
uint8_t U8
@ RES_LOADED
The resource is available for usage.
SceneNodeType
ToDo: Move particle emitter to components (it will make them way more dynamic) - Ionut.
Definition: SceneNodeFwd.h:47
constexpr F32 EPSILON_F32
eastl::vector< Type > vector
Definition: Vector.h:42
std::shared_lock< mutex > SharedLock
Definition: SharedMutex.h:49
void PopReadOnly()
Definition: Editor.cpp:2971
Project & parent
Definition: DefaultScene.h:41
FORCE_INLINE Handle< T > CreateResource(const ResourceDescriptor< T > &descriptor, bool &wasInCache, std::atomic_uint &taskCounter)
void efficient_clear(eastl::fixed_vector< T, nodeCount, bEnableOverflow, OverflowAllocator > &fixed_vector)
Definition: Vector.h:52
constexpr U8 to_U8(const T value)
::value constexpr T CLAMPED(T n, T min, T max) noexcept
Definition: MathHelper.inl:126
constexpr I16 to_I16(const T value)
FORCE_INLINE constexpr bool Is3DObject(const SceneNodeType type) noexcept
Definition: SceneNodeFwd.h:98
SceneNodeHandle FromHandle(const Handle< T > handle)
constexpr I32 to_I32(const T value)
FORCE_INLINE T * Get(const Handle< T > handle)
uint32_t U32
constexpr auto to_base(const Type value) -> Type
struct Divide::Configuration::Runtime runtime
Queries are expensive, so this result MAY BE SEVERAL frames out of date!
U64 _verticesSubmitted
Returns the time in milliseconds that it took to render one frame.
size_t _uniformBufferVRAMUsage
Total VRAM used for shader uniform storage across all used shader programs.
U64 _tessellationInvocations
Number of times the tessellation control shader has been invoked.
U32 _syncObjectsInFlight[3]
Number of active sync objects.
size_t _bufferVRAMUsage
Total VRAM usage for all shader buffers.
U32 _scratchBufferQueueUsage[2]
Scratch buffer queue usage.
U64 _queuedGPUFrames
How many frames are still queued up for execution on the GPU.
U64 _tessellationPatches
Number of patches processed by the tessellation control shader.
SceneGraphNode * getChild(const U32 idx)
Return a specific child by index. Does not recurse.
eastl::fixed_vector< SceneGraphNode *, 32, true > _data
Angle::RADIANS< F32 > azimuth
Definition: Sun.h:45
SimpleTime sunsetTime
Definition: Sun.h:42
Angle::DEGREES< F32 > declination
Definition: Sun.h:47
SimpleTime sunriseTime
Definition: Sun.h:41
Angle::RADIANS< F32 > altitude
Definition: Sun.h:44
SimpleTime noonTime
Definition: Sun.h:43
Angle::DEGREES< F32 > altitudeMax
Definition: Sun.h:46