Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
PropertyWindow.cpp
Go to the documentation of this file.
1
2
6
10
21
27
31
32#include <IconsForkAwesome.h>
33#include <imgui_internal.h>
34
35
36#ifdef _MSC_VER
37#pragma warning(push)
38#pragma warning(disable: 4127) // conditional expression is constant
39#endif
40
41namespace Divide
42{
43 namespace
44 {
46 std::array<U64, 1024> s_openProperties = {};
47
48 // Separate activate is used for stuff that does continuous value changes, e.g. colour selectors, but you only want to register the old val once
49 template<typename T, bool SeparateActivate, typename Pred>
50 void RegisterUndo( Editor& editor, PushConstantType Type, const T& oldVal, const T& newVal, const char* name, Pred&& dataSetter )
51 {
52 UndoEntry<T> undo = {};
53 if ( !SeparateActivate || ImGui::IsItemActivated() )
54 {
55 undo._oldVal = oldVal;
56 }
57
58 if ( ImGui::IsItemDeactivatedAfterEdit() )
59 {
60 undo._type = Type;
61 undo._name = name;
62 undo._newVal = newVal;
63 undo._dataSetter = dataSetter;
64 editor.registerUndoEntry( undo );
65 }
66 }
67
68 bool IsRequiredComponentType( SceneGraphNode* selection, const ComponentType componentType )
69 {
70 if ( selection != nullptr )
71 {
72 return selection->getNode().requiredComponentMask() & to_U32( componentType );
73 }
74
75 return false;
76 }
77
78 template<typename Pred>
79 void ApplyToMaterials( const Material& baseMaterial, Material* instanceRoot, Pred&& predicate )
80 {
81 if ( instanceRoot != nullptr )
82 {
83 predicate( baseMaterial, instanceRoot );
84
85 //ToDo: Not thread safe
86 instanceRoot->lockInstancesForRead();
87 const auto& instances = instanceRoot->getInstancesLocked();
88 for ( Handle<Material> mat : instances )
89 {
90 ApplyToMaterials( baseMaterial, Get(mat), predicate );
91 }
92 instanceRoot->unlockInstancesForRead();
93 }
94 }
95
96 template<typename Pred>
97 void ApplyAllButton( I32& id, const bool readOnly, const Material& material, Pred&& predicate )
98 {
99 ImGui::SameLine( ImGui::GetWindowContentRegionMax().x - 40 );
100 ImGui::PushID( 4321234 + id++ );
101 if ( readOnly )
102 {
103 PushReadOnly();
104 }
105 if ( ImGui::SmallButton( "A" ) )
106 {
107 ApplyToMaterials( material, material.baseMaterial(), MOV( predicate ) );
108 }
109 if ( ImGui::IsItemHovered( ImGuiHoveredFlags_AllowWhenDisabled ) )
110 {
111 ImGui::SetTooltip( "Apply to all instances" );
112 }
113 if ( readOnly )
114 {
115 PopReadOnly();
116 }
117 ImGui::PopID();
118 }
119
120 bool PreviewTextureButton( I32& id, const Handle<Texture> tex, const bool readOnly )
121 {
122 bool ret = false;
123 ImGui::SameLine( ImGui::GetWindowContentRegionMax().x - 15 );
124 ImGui::PushID( 4321234 + id++ );
125 if ( readOnly )
126 {
127 PushReadOnly();
128 }
129 if ( ImGui::SmallButton( "T" ) )
130 {
131 ret = true;
132 }
133 if ( tex != INVALID_HANDLE<Texture> && ImGui::IsItemHovered( ImGuiHoveredFlags_AllowWhenDisabled ) )
134 {
135 ImGui::SetTooltip( Util::StringFormat( "Preview texture : {}", Get(tex)->assetName()).c_str() );
136 }
137 if ( readOnly )
138 {
139 PopReadOnly();
140 }
141 ImGui::PopID();
142 return ret;
143 }
144 }
145
147 : DockedWindow( parent, descriptor )
148 , PlatformContextComponent( context )
149 {
150 }
151
153 {
154 if ( _lockedComponent._editorComp == nullptr )
155 {
156 return;
157 }
158
159 if ( comp.getGUID() == _lockedComponent._editorComp->getGUID() )
160 {
161 _lockedComponent = { nullptr, nullptr };
162 }
163 }
164
166 {
167 bool sceneChanged = false;
168 if ( cam == nullptr )
169 {
170 return false;
171 }
172
173 const char* camName = cam->resourceName().c_str();
174 const U64 camID = _ID( camName );
175 ImGui::PushID( to_I32( camID ) * 54321 );
176
177 if ( ImGui::CollapsingHeader( camName, ImGuiTreeNodeFlags_DefaultOpen ) )
178 {
179 const char* modeStr = TypeUtil::CameraModeToString( cam->mode() );
180 ImGui::PushID( modeStr );
181 ImGui::LabelText( "", "Camera Mode: [ %s ]", modeStr );
182 ImGui::PopID();
183
184 ImGui::Separator();
186 {
187 vec3<F32> eye = cam->snapshot()._eye;
188 EditorComponentField camField = {};
189 camField._name = "Eye";
192 camField._readOnly = false;
193 camField._data = eye._v;
194 camField._dataSetter = [cam]( const void* val ) noexcept
195 {
196 cam->setEye( *static_cast<const vec3<F32>*>(val) );
197 };
198 sceneChanged = processField( camField ) || sceneChanged;
199 }
200 {
201 constexpr const char* CamRotateLabels[] = {
202 "P", "Y", "R"
203 };
204
205 vec3<F32> euler = cam->euler();
206 EditorComponentField camField = {};
207 camField._name = "Euler";
208 camField._labels = CamRotateLabels;
209 camField._tooltip = "Change camera orientation using euler angles( degrees).\nP = Pitch, Y = Yaw, R = Roll";
212 camField._readOnly = false;
213 camField._data = euler._v;
214 camField._dataSetter = [cam]( const void* e ) noexcept
215 {
216 cam->setEuler( *static_cast<const vec3<F32>*>(e) );
217 };
218 sceneChanged = processField( camField ) || sceneChanged;
219 }
220 {
221 constexpr const char* CamSpeedLabels[] = {
222 "T", "M", "Z"
223 };
224
225 vec3<F32> speed = cam->speedFactor();
226
227 EditorComponentField camField = {};
228 camField._name = "Speed";
230 camField._labels = CamSpeedLabels;
233 camField._readOnly = false;
234 camField._tooltip = "Change camera speed factor. (units / second) \nT = Turn speed, M = Move speed, Z = Zoom speed";
235 camField._data = speed._v;
236 camField._resetValue = 5.f;
237 camField._dataSetter = [cam]( const void* e ) noexcept
238 {
239 const vec3<F32> speed = *static_cast<const vec3<F32>*>(e);
240 cam->speedFactor( speed );
241 };
242 sceneChanged = processField( camField ) || sceneChanged;
243 }
244 {
246 EditorComponentField camField = {};
247 camField._name = "Forward";
250 camField._readOnly = true;
251 camField._data = fwd._v;
252 sceneChanged = processField( camField ) || sceneChanged;
253 }
254 {
255 F32 aspect = cam->snapshot()._aspectRatio;
256 EditorComponentField camField = {};
257 camField._name = "Aspect";
260 camField._readOnly = false;
261 camField._data = &aspect;
262 camField._dataSetter = [cam]( const void* a ) noexcept
263 {
264 cam->setAspectRatio( *static_cast<const F32*>(a) );
265 };
266 sceneChanged = processField( camField ) || sceneChanged;
267 }
268 {
269 F32 horizontalFoV = cam->getHorizontalFoV();
270 EditorComponentField camField = {};
271 camField._name = "FoV (horizontal)";
274 camField._readOnly = false;
275 camField._data = &horizontalFoV;
276 camField._dataSetter = [cam]( const void* fov ) noexcept
277 {
278 cam->setHorizontalFoV( *static_cast<const F32*>(fov) );
279 };
280 sceneChanged = processField( camField ) || sceneChanged;
281 }
282 {
283 vec2<F32> zPlanes = cam->snapshot()._zPlanes;
284 EditorComponentField camField = {};
285 camField._name = "zPlanes";
288 camField._readOnly = false;
289 camField._data = zPlanes._v;
290 camField._dataSetter = [cam]( const void* planes )
291 {
292 if ( cam->snapshot()._isOrthoCamera )
293 {
294 cam->setProjection( cam->orthoRect(), *static_cast<const vec2<F32>*>(planes) );
295 }
296 else
297 {
298 cam->setProjection( cam->snapshot()._aspectRatio, cam->snapshot()._fov, *static_cast<const vec2<F32>*>(planes) );
299 }
300 };
301 sceneChanged = processField( camField ) || sceneChanged;
302 }
303 if ( cam->snapshot()._isOrthoCamera )
304 {
305 vec4<F32> orthoRect = cam->orthoRect();
306 EditorComponentField camField = {};
307 camField._name = "Ortho";
310 camField._readOnly = false;
311 camField._data = orthoRect._v;
312 camField._dataSetter = [cam]( const void* rect )
313 {
314 cam->setProjection( *static_cast<const vec4<F32>*>(rect), cam->snapshot()._zPlanes );
315 };
316 sceneChanged = processField( camField ) || sceneChanged;
317 }
318 {
319 mat4<F32> viewMatrix = cam->viewMatrix();
320 EditorComponentField worldMatrixField = {};
321 worldMatrixField._name = "View Matrix";
322 worldMatrixField._basicType = PushConstantType::MAT4;
324 worldMatrixField._readOnly = true;
325 worldMatrixField._data = &viewMatrix;
326 if ( processBasicField( worldMatrixField ) )
327 {
328 // Value changed
329 }
330 }
331 {
332 mat4<F32> projMatrix = cam->projectionMatrix();
333 EditorComponentField projMatrixField;
334 projMatrixField._basicType = PushConstantType::MAT4;
336 projMatrixField._readOnly = true;
337 projMatrixField._name = "Projection Matrix";
338 projMatrixField._data = &projMatrix;
339 if ( processBasicField( projMatrixField ) )
340 {
341 // Value changed
342 }
343 }
344 {
345 ImGui::Separator();
346 bool drawFustrum = g_debugFrustums.find( camID ) != eastl::cend( g_debugFrustums );
347 ImGui::PushID( to_I32( camID ) * 123456 );
348 if ( ImGui::Checkbox( "Draw debug frustum", &drawFustrum ) )
349 {
350 if ( drawFustrum )
351 {
352 g_debugFrustums[camID] = { cam->getFrustum(), DefaultColours::GREEN, true };
353 }
354 else
355 {
356 g_debugFrustums.erase( camID );
357 }
358 }
359 if ( drawFustrum )
360 {
361 auto& [frust, colour, realtime] = g_debugFrustums[camID];
362 ImGui::Checkbox( "Update realtime", &realtime );
363 const bool update = realtime || ImGui::Button( "Update frustum" );
364 if ( update )
365 {
366 frust = cam->getFrustum();
367 }
368 ImGui::PushID( cam->resourceName().c_str() );
369 ImGui::ColorEdit3( "Frust Colour", colour._v, ImGuiColorEditFlags_DefaultOptions_ );
370 ImGui::PopID();
371 }
372 ImGui::PopID();
373 }
374 {
375 F32 radius = cam->maxRadius();
376 EditorComponentField camField = {};
377 camField._name = "MAX Radius";
380 camField._readOnly = false;
381 camField._data = &radius;
382 camField._dataSetter = [cam]( const void* radius ) noexcept
383 {
384 cam->maxRadius( *static_cast<const F32*>(radius) );
385 };
386 sceneChanged = processField( camField ) || sceneChanged;
387 }
388 {
389 F32 radius = cam->maxRadius();
390 EditorComponentField camField = {};
391 camField._name = "MIN Radius";
394 camField._readOnly = false;
395 camField._data = &radius;
396 camField._dataSetter = [cam]( const void* radius ) noexcept
397 {
398 cam->maxRadius( *static_cast<const F32*>(radius) );
399 };
400 sceneChanged = processField( camField ) || sceneChanged;
401 }
402 {
403 F32 radius = cam->curRadius();
404 EditorComponentField camField = {};
405 camField._name = "Current Radius";
408 camField._readOnly = false;
409 camField._data = &radius;
410 camField._dataSetter = [cam]( const void* radius ) noexcept
411 {
412 cam->curRadius( *static_cast<const F32*>(radius) );
413 };
414 sceneChanged = processField( camField ) || sceneChanged;
415 }
416
418 }
419 ImGui::PopID();
420
421 return sceneChanged;
422 }
423
425 {
427
428 I64 guid = 12344231;
429 for ( const auto& it : g_debugFrustums )
430 {
431 const auto& [frustum, colour, realtime] = it.second;
433 descriptor.frustum = frustum;
434 descriptor.colour = Util::ToByteColour( colour );
436 }
437 }
438
439 bool PropertyWindow::printComponent( SceneGraphNode* sgnNode, EditorComponent* comp, const F32 xOffset, const F32 smallButtonWidth )
440 {
442
443 bool sceneChanged = false;
444
445 bool isLockedField = false;
446 bool fieldWasOpen = false;
447
449 {
450 fieldWasOpen = true;
451 isLockedField = true;
452 }
453
454 // always keep transforms open by default for convenience
455 const bool fieldAlwaysOpen = comp->componentType() == ComponentType::TRANSFORM || comp->componentType() == ComponentType::COUNT;
456
457 const string fieldNameStr = fieldWasOpen ? Util::StringFormat( "{} ({})", comp->name().c_str(), _lockedComponent._parentSGN->name().c_str() ) : comp->name().c_str();
458 const char* fieldName = fieldNameStr.c_str();
459 const U64 fieldHash = _ID( fieldName );
460 if ( !isLockedField )
461 {
462 for ( const U64 p : s_openProperties )
463 {
464 if ( p == fieldHash )
465 {
466 fieldWasOpen = true;
467 break;
468 }
469 }
470 }
471 if ( comp->fields().empty() )
472 {
473 PushReadOnly();
474 ImGui::CollapsingHeader( fieldName, ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet );
475 PopReadOnly();
476 }
477 else
478 {
479 if ( ImGui::CollapsingHeader( fieldName, ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth | ((fieldWasOpen || fieldAlwaysOpen) ? ImGuiTreeNodeFlags_DefaultOpen : 0u) ) )
480 {
481 if ( !fieldWasOpen )
482 {
483 for ( U64& p : s_openProperties )
484 {
485 if ( p == 0u )
486 {
487 p = fieldHash;
488 break;
489 }
490 }
491 }
492
493 ImGui::NewLine();
494 ImGui::SameLine( xOffset );
495 if ( ImGui::Button( ICON_FK_SEARCH" INSPECT", ImVec2( smallButtonWidth, 20 ) ) )
496 {
497 Attorney::EditorGeneralWidget::inspectMemory( _context.editor(), std::make_pair( comp, sizeof( EditorComponent ) ) );
498 }
499 if ( !isLockedField && comp->componentType() != ComponentType::COUNT && !IsRequiredComponentType( sgnNode, comp->componentType() ) )
500 {
501 ImGui::SameLine();
502 if ( ImGui::Button( ICON_FK_MINUS" REMOVE", ImVec2( smallButtonWidth, 20 ) ) )
503 {
504 Attorney::EditorGeneralWidget::inspectMemory( _context.editor(), std::make_pair( nullptr, 0 ) );
505
506 if ( Attorney::EditorGeneralWidget::removeComponent( _context.editor(), sgnNode, comp->componentType() ) )
507 {
508 sceneChanged = true;
509 return true;
510 }
511 }
512 }
513 ImGui::SameLine( ImGui::GetWindowSize().x - 80.f );
514 bool fieldLocked = _lockedComponent._editorComp != nullptr && _lockedComponent._editorComp->getGUID() == comp->getGUID();
515 ImGui::PushID( to_I32( fieldHash ) );
516 if ( ImGui::Checkbox( ICON_FK_LOCK" ", &fieldLocked ) )
517 {
518 if ( !fieldLocked )
519 {
520 _lockedComponent = { nullptr, nullptr };
521 }
522 else
523 {
524 _lockedComponent = { comp, sgnNode };
525 }
526 }
527 ImGui::PopID();
528 if ( ImGui::IsItemHovered() )
529 {
530 ImGui::SetTooltip( "Always keep this component visible in the editor regardless of the currently selected scene node" );
531 skipAutoTooltip( true );
532 }
533 ImGui::Separator();
535 for ( EditorComponentField& field : fields )
536 {
537 if ( processField( field ) && !field._readOnly )
538 {
540 sceneChanged = true;
541 }
542 ImGui::Spacing();
543 }
544 const U32 componentMask = sgnNode->componentMask();
545 if ( componentMask & to_base(ComponentType::ENVIRONMENT_PROBE ) )
546 {
548 if ( probe != nullptr )
549 {
550 ImGui::Text("Probe ID: %d", probe->getGUID());
551 }
552 }
553 Light* light = nullptr;
554 if ( componentMask & to_base(ComponentType::SPOT_LIGHT ) )
555 {
556 light = sgnNode->get<SpotLightComponent>();
557 }
558 else if ( componentMask & to_base(ComponentType::POINT_LIGHT ) )
559 {
560 light = sgnNode->get<PointLightComponent>();
561 }
562 else if ( componentMask & to_base(ComponentType::DIRECTIONAL_LIGHT ) )
563 {
564 light = sgnNode->get<DirectionalLightComponent>();
565 }
566 if ( light != nullptr )
567 {
568 if ( light->castsShadows() )
569 {
570 if ( ImGui::CollapsingHeader( "Light Shadow Settings", ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth ) )
571 {
572 ImGui::Text( "Shadow Offset: %d", to_U32( light->getShadowArrayOffset() ) );
573
574 switch ( light->getLightType() )
575 {
576 case LightType::POINT:
577 {
578 for ( U8 face = 0u; face < 6u; ++face )
579 {
580 Camera* shadowCamera = ShadowMap::shadowCameras( ShadowType::CUBEMAP )[face];
581 if ( drawCamera( shadowCamera ) )
582 {
583 sceneChanged = true;
584 }
585 }
586
587 } break;
588
589 case LightType::SPOT:
590 {
591 Camera* shadowCamera = ShadowMap::shadowCameras( ShadowType::SINGLE ).front();
592 if ( drawCamera( shadowCamera ) )
593 {
594 sceneChanged = true;
595 }
596 } break;
597
599 {
600 DirectionalLightComponent* dirLight = static_cast<DirectionalLightComponent*>(light);
601 for ( U8 split = 0u; split < dirLight->csmSplitCount(); ++split )
602 {
603 Camera* shadowCamera = ShadowMap::shadowCameras( ShadowType::CSM )[split];
604 if ( drawCamera( shadowCamera ) )
605 {
606 sceneChanged = true;
607 }
608 }
609 } break;
610
611 case LightType::COUNT:
612 {
614 } break;
615 }
616 }
617 }
618
619 if ( ImGui::CollapsingHeader( "Scene Shadow Settings", ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth ) )
620 {
621 auto& projectManager = context().kernel().projectManager();
622 auto& activeSceneState = projectManager->activeProject()->getActiveScene()->state();
623
624 {
625 F32 bleedBias = activeSceneState->lightBleedBias();
626 EditorComponentField tempField = {};
627 tempField._name = "Light bleed bias";
630 tempField._readOnly = false;
631 tempField._data = &bleedBias;
632 tempField._format = "%.6f";
633 tempField._range = { 0.0f, 1.0f };
634 tempField._dataSetter = [&activeSceneState]( const void* bias ) noexcept
635 {
636 activeSceneState->lightBleedBias( *static_cast<const F32*>(bias) );
637 };
638 sceneChanged = processField( tempField ) || sceneChanged;
639 }
640 {
641 F32 shadowVariance = activeSceneState->minShadowVariance();
642 EditorComponentField tempField = {};
643 tempField._name = "Minimum variance";
646 tempField._readOnly = false;
647 tempField._data = &shadowVariance;
648 tempField._range = { 0.00001f, 0.99999f };
649 tempField._format = "%.6f";
650 tempField._dataSetter = [&activeSceneState]( const void* variance ) noexcept
651 {
652 activeSceneState->minShadowVariance( *static_cast<const F32*>(variance) );
653 };
654 sceneChanged = processField( tempField ) || sceneChanged;
655 }
656 }
657 }
658 }
659 else
660 {
661 for ( U64& p : s_openProperties )
662 {
663 if ( p == fieldHash )
664 {
665 p = 0u;
666 break;
667 }
668 }
669 }
670 }
671 return sceneChanged;
672 }
673
675 {
677
678 constexpr F32 buttonWidth = 105.0f;
679 constexpr F32 smallButtonWidth = 90.0f;
680
681 skipAutoTooltip( false );
682
683 bool sceneChanged = false;
684 F32 xOffset = ImGui::GetWindowSize().x * 0.5f - (smallButtonWidth * 2);
685 I64 lockedGUID = -1;
686 if ( _lockedComponent._editorComp != nullptr )
687 {
688 lockedGUID = _lockedComponent._editorComp->getGUID();
689 sceneChanged = printComponent( _lockedComponent._parentSGN, _lockedComponent._editorComp, xOffset, smallButtonWidth );
690 }
691
692 const Selections crtSelections = selections();
693 const bool hasSelections = crtSelections._selectionCount > 0u;
694
695 bool lockSolutionExplorer = false;
697 if ( selectedCamera != nullptr )
698 {
699 sceneChanged = drawCamera( selectedCamera );
700 }
701 else if ( hasSelections )
702 {
703 for ( U8 i = 0u; i < crtSelections._selectionCount; ++i )
704 {
705 SceneGraphNode* sgnNode = node( crtSelections._selections[i] );
706 if ( sgnNode != nullptr )
707 {
708 ImGui::PushID( sgnNode->name().c_str() );
709
710 bool enabled = sgnNode->hasFlag( SceneGraphNode::Flags::ACTIVE );
711 if ( ImGui::Checkbox( Util::StringFormat( "{} {}", getIconForNode( sgnNode ), sgnNode->name().c_str() ).c_str(), &enabled ) )
712 {
713 if ( enabled )
714 {
716 }
717 else
718 {
720 }
721 sceneChanged = true;
722 }
723
724 ImGui::SameLine( ImGui::GetWindowSize().x - 80.f );
725 bool selectionLocked = sgnNode->hasFlag( SceneGraphNode::Flags::SELECTION_LOCKED );
726 if ( ImGui::Checkbox( ICON_FK_LOCK" ", &selectionLocked ) )
727 {
728 if ( selectionLocked )
729 {
731 }
732 else
733 {
735 }
736 }
737 if ( ImGui::IsItemHovered() )
738 {
739 ImGui::SetTooltip( "When ticked, prevents selection of a different node" );
740 skipAutoTooltip( true );
741 }
742 if ( selectionLocked )
743 {
744 lockSolutionExplorer = true;
745 }
746 ImGui::Separator();
747
748 // Root
749 if ( sgnNode->parent() == nullptr )
750 {
751 ImGui::Separator();
752 }
753
754 EditorComponent* nodeComp = sgnNode->node()->editorComponent();
755 if ( nodeComp != nullptr && lockedGUID != nodeComp->getGUID() && printComponent( sgnNode, nodeComp, xOffset, smallButtonWidth))
756 {
757 sceneChanged = true;
758 }
759
761 for ( EditorComponent* comp : editorComp )
762 {
763 if ( lockedGUID != comp->getGUID() && printComponent( sgnNode, comp, xOffset, smallButtonWidth ) )
764 {
765 sceneChanged = true;
766 }
767 }
768 ImGui::Separator();
769 ImGui::NewLine();
770 ImGui::NewLine();
771 ImGui::SameLine( ImGui::GetWindowSize().x * 0.5f - (buttonWidth * 0.5f) );
772 if ( ImGui::Button( ICON_FK_FLOPPY_O" Save Node", ImVec2( buttonWidth, 25 ) ) )
773 {
775 }
776 }
777 ImGui::PopID();
778 }
779
780 //ToDo: Speed this up. Also, how do we handle adding stuff like RenderingComponents and creating materials and the like?
781 const auto validComponentToAdd = [this, &crtSelections]( const ComponentType type ) -> bool
782 {
783 if ( type == ComponentType::COUNT )
784 {
785 return false;
786 }
787
788 if ( type == ComponentType::SCRIPT )
789 {
790 return true;
791 }
792
793 bool missing = false;
794 for ( U8 i = 0u; i < crtSelections._selectionCount; ++i )
795 {
796 const SceneGraphNode* sgn = node( crtSelections._selections[i] );
797 if ( sgn != nullptr && !( sgn->componentMask() & to_U32( type ) ) )
798 {
799 missing = true;
800 break;
801 }
802 }
803
804 return missing;
805 };
806
807 xOffset = ImGui::GetWindowSize().x - buttonWidth - 20.0f;
808 ImGui::NewLine();
809 ImGui::Separator();
810 ImGui::NewLine();
811 ImGui::SameLine( xOffset );
812 if ( ImGui::Button( ICON_FK_PLUS" ADD NEW", ImVec2( buttonWidth, 25 ) ) )
813 {
814 Util::OpenCenteredPopup( "COMP_SELECTION_GROUP" );
815 }
816 static ComponentType selectedType = ComponentType::COUNT;
817
818 if ( ImGui::BeginPopup( "COMP_SELECTION_GROUP" ) )
819 {
820 for ( auto i = 1u; i < to_base( ComponentType::COUNT ) + 1; ++i )
821 {
822 const U32 componentBit = 1 << i;
823 const ComponentType type = static_cast<ComponentType>(componentBit);
824 if ( type == ComponentType::COUNT || !validComponentToAdd( type ) )
825 {
826 continue;
827 }
828
829 if ( ImGui::Selectable( TypeUtil::ComponentTypeToString( type ) ) )
830 {
831 selectedType = type;
832 }
833 }
834 ImGui::EndPopup();
835 }
836 if ( selectedType != ComponentType::COUNT )
837 {
838 Util::OpenCenteredPopup( "Add new component" );
839 }
840
841 if ( ImGui::BeginPopupModal( "Add new component", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) )
842 {
843 ImGui::Text( "Add new %s component?", TypeUtil::ComponentTypeToString( selectedType ) );
844 ImGui::Separator();
845
846 if ( ImGui::Button( "OK", ImVec2( 120, 0 ) ) )
847 {
848 if ( Attorney::EditorGeneralWidget::addComponent( _context.editor(), crtSelections, selectedType ) )
849 {
850 sceneChanged = true;
851 }
852 selectedType = ComponentType::COUNT;
853 ImGui::CloseCurrentPopup();
854 }
855
856 ImGui::SetItemDefaultFocus();
857 ImGui::SameLine();
858 if ( ImGui::Button( "Cancel", ImVec2( 120, 0 ) ) )
859 {
860 selectedType = ComponentType::COUNT;
861 ImGui::CloseCurrentPopup();
862 }
863 ImGui::EndPopup();
864 }
865 }
866 else
867 {
868 ImGui::Separator();
869 ImGui::Text( "Please select a scene node \n to inspect its properties" );
870 ImGui::Separator();
871 }
872
873 if ( _previewTexture != INVALID_HANDLE<Texture> )
874 {
875 if ( Attorney::EditorGeneralWidget::modalTextureView( _context.editor(), Util::StringFormat( "Image Preview: {}", Get(_previewTexture)->resourceName().c_str() ).c_str(), _previewTexture, vec2<F32>( 512, 512 ), true, false ) )
876 {
877 _previewTexture = INVALID_HANDLE<Texture>;
878 }
879 }
880
882
883 if ( sceneChanged )
884 {
886 }
887 }
888
890 {
891 Scene* activeScene = context().kernel().projectManager()->activeProject()->getActiveScene();
892 return activeScene->getCurrentSelection();
893 }
894
896 {
897 Scene* activeScene = context().kernel().projectManager()->activeProject()->getActiveScene();
898 return activeScene->sceneGraph()->findNode( guid );
899 }
900
902 {
903 if ( field._labels == nullptr )
904 {
906 }
907
908 const auto printFieldName = [&field]()
909 {
910 ImGui::Text( "[%s]", field._name.c_str() );
911 };
912 bool ret = false;
913 switch ( field._type )
914 {
916 {
917 printFieldName();
918 ImGui::Separator();
919 }break;
921 {
922 if ( field._readOnly )
923 {
924 PushReadOnly();
925 }
926 if ( field._range.y - field._range.x > 1.0f )
927 {
928 ret = ImGui::Button( field._name.c_str(), ImVec2( field._range.x, field._range.y ) );
929 }
930 else
931 {
932 ret = ImGui::Button( field._name.c_str() );
933 }
934 if ( field._readOnly )
935 {
936 PopReadOnly();
937 }
938 }break;
942 {
943 ImGui::PushItemWidth( ImGui::GetContentRegionAvail().x * 0.5f );
944 {
945 ret = processBasicField( field );
946 }ImGui::PopItemWidth();
947 } break;
949 {
950 if ( field._readOnly )
951 {
952 PushReadOnly();
953 }
954
955 printFieldName();
956 const U8 entryStart = to_U8( field._range.offset );
957 const U8 entryCount = to_U8( field._range.count );
958 static UndoEntry<I32> typeUndo = {};
959 if ( entryCount > 0 && entryStart <= entryCount )
960 {
961 ImGui::PushID( field._name.c_str() );
962
963 const U8 crtMode = field.get<U8>();
964 ret = ImGui::BeginCombo( "", field.getDisplayName( crtMode ) );
965 if ( ret )
966 {
967 for ( U8 n = entryStart; n < entryCount; ++n )
968 {
969 const bool isSelected = crtMode == n;
970 if ( ImGui::Selectable( field.getDisplayName( n ), isSelected ) )
971 {
972 typeUndo._type = PushConstantType::UINT;
973 typeUndo._name = "Drop Down Selection";
974 typeUndo._oldVal = to_U32( crtMode );
975 typeUndo._newVal = to_U32( n );
976 typeUndo._dataSetter = [&field]( const U32& data )
977 {
978 field.set( to_U8( data ) );
979 };
980
981 field.set( n );
982 _context.editor().registerUndoEntry( typeUndo );
983 break;
984 }
985 if ( isSelected )
986 {
987 ImGui::SetItemDefaultFocus();
988 }
989 }
990 ImGui::EndCombo();
991 }
992 ImGui::PopID();
993 }
994 if ( field._readOnly )
995 {
996 PopReadOnly();
997 }
998 }break;
1000 {
1001 printFieldName();
1002 BoundingBox bb = {};
1003 field.get<BoundingBox>( bb );
1004
1007 vec3<F32> halfExtent = bb.getHalfExtent();
1008 vec3<F32> bbCenter = bb.getCenter();
1009 {
1010 EditorComponentField bbField = {};
1011 bbField._name = "Min ";
1014 bbField._readOnly = field._readOnly;
1015 bbField._data = bbMin;
1016 bbField._hexadecimal = field._hexadecimal;
1017 bbField._dataSetter = [&field]( const void* val )
1018 {
1019 BoundingBox aabb = {};
1020 field.get<BoundingBox>( aabb );
1021 aabb.setMin( *static_cast<const vec3<F32>*>(val) );
1022 field.set<BoundingBox>( aabb );
1023 };
1024 ret = processField( bbField ) || ret;
1025 }
1026 {
1027 EditorComponentField bbField = {};
1028 bbField._name = "Max ";
1031 bbField._readOnly = field._readOnly;
1032 bbField._data = bbMax;
1033 bbField._hexadecimal = field._hexadecimal;
1034 bbField._dataSetter = [&field]( const void* val )
1035 {
1036 BoundingBox aabb = {};
1037 field.get<BoundingBox>( aabb );
1038 aabb.setMax( *static_cast<const vec3<F32>*>(val) );
1039 field.set<BoundingBox>( aabb );
1040 };
1041 ret = processField( bbField ) || ret;
1042 }
1043 {
1044 EditorComponentField bbField = {};
1045 bbField._name = "Half Extents ";
1048 bbField._readOnly = true;
1049 bbField._data = halfExtent;
1050 bbField._hexadecimal = field._hexadecimal;
1051 ret = processField( bbField ) || ret;
1052 }
1053 {
1054 EditorComponentField bbField = {};
1055 bbField._name = "Center ";
1058 bbField._readOnly = true;
1059 bbField._data = bbCenter;
1060 bbField._hexadecimal = field._hexadecimal;
1061 ret = processField( bbField ) || ret;
1062 }
1063 }break;
1065 {
1066 printFieldName();
1067 OBB obb = {};
1068 field.get<OBB>( obb );
1069 vec3<F32> position = obb.position();
1070 vec3<F32> hExtents = obb.halfExtents();
1071 OBB::OBBAxis axis = obb.axis();
1072 {
1073 EditorComponentField bbField = {};
1074 bbField._name = "Center ";
1077 bbField._readOnly = true;
1078 bbField._hexadecimal = field._hexadecimal;
1079 bbField._data = position._v;
1080 ret = processField( bbField ) || ret;
1081 }
1082 {
1083 EditorComponentField bbField = {};
1084 bbField._name = "Half Extents ";
1087 bbField._readOnly = true;
1088 bbField._hexadecimal = field._hexadecimal;
1089 bbField._data = hExtents._v;
1090 ret = processField( bbField ) || ret;
1091 }
1092 for ( U8 i = 0; i < 3; ++i )
1093 {
1094 EditorComponentField bbField = {};
1095 Util::StringFormat( bbField._name, "Axis [ {} ]", i );
1098 bbField._readOnly = true;
1099 bbField._hexadecimal = field._hexadecimal;
1100 bbField._data = axis[i]._v;
1101 ret = processField( bbField ) || ret;
1102 }
1103 }break;
1105 {
1106 printFieldName();
1107
1108 BoundingSphere bs = {};
1109 field.get<BoundingSphere>( bs );
1112 {
1113 EditorComponentField bbField = {};
1114 bbField._name = "Center ";
1117 bbField._readOnly = field._readOnly;
1118 bbField._hexadecimal = field._hexadecimal;
1119 bbField._data = center;
1120 bbField._dataSetter = [&field]( const void* c )
1121 {
1122 BoundingSphere bSphere = {};
1123 field.get<BoundingSphere>( bSphere );
1124 bSphere.setCenter( *static_cast<const vec3<F32>*>(c) );
1125 field.set<BoundingSphere>( bSphere );
1126 };
1127 ret = processField( bbField ) || ret;
1128 }
1129 {
1130 EditorComponentField bbField = {};
1131 bbField._name = "Radius ";
1134 bbField._readOnly = field._readOnly;
1135 bbField._hexadecimal = field._hexadecimal;
1136 bbField._data = &radius;
1137 bbField._dataSetter = [&field]( const void* r )
1138 {
1139 BoundingSphere bSphere = {};
1140 field.get<BoundingSphere>( bSphere );
1141 bSphere.setRadius( *static_cast<const F32*>(r) );
1142 field.set<BoundingSphere>( bSphere );
1143 };
1144 ret = processField( bbField ) || ret;
1145 }
1146 }break;
1148 {
1149 assert( !field._dataSetter && "Need direct access to memory" );
1150 ret = processTransform( field.getPtr<TransformComponent>(), field._readOnly, field._hexadecimal );
1151 }break;
1152
1154 {
1155 assert( !field._dataSetter && "Need direct access to memory" );
1156 ret = processMaterial( field.getPtr<Material>(), field._readOnly, field._hexadecimal );
1157 }break;
1159 }
1160
1161 if ( !skipAutoTooltip() && ImGui::IsItemHovered( ImGuiHoveredFlags_AllowWhenDisabled ) )
1162 {
1163 ImGui::SetTooltip( field._tooltip.empty() ? field._name.c_str() : field._tooltip.c_str() );
1164 }
1165
1166 return ret;
1167 }
1168
1169 bool PropertyWindow::processTransform( TransformComponent* transform, const bool readOnly, const bool hex )
1170 {
1171 if ( transform == nullptr )
1172 {
1173 return false;
1174 }
1175
1176 DIVIDE_ASSERT(!hex, "PropertyWindow::processTransform error: Hex fields not supported!");
1177
1179 bool ret = false;
1180 const bool transformReadOnly = readOnly || transform->editorLockPosition();
1181 const bool rotationReadOnly = readOnly || transform->editorLockRotation();
1182 const bool scaleReadOnly = readOnly || transform->editorLockScale();
1183
1184 const TransformValues transformValues = transform->getLocalValues();
1185 vec3<F32> pos = transformValues._translation;
1186 vec3<F32> rot = Angle::to_DEGREES( transformValues._orientation.getEuler() );
1187 vec3<F32> scale = transformValues._scale;
1188
1189 const vec3<F32> oldRot = rot;
1190 if ( Util::DrawVec<F32, 3, true>( ImGuiDataType_Float, "Position", pos._v, transformReadOnly ).wasChanged )
1191 {
1192 ret = true;
1193 RegisterUndo<vec3<F32>, false>( _parent,
1195 transformValues._translation,
1196 pos,
1197 "Transform position",
1198 [transform]( const vec3<F32>& val )
1199 {
1200 transform->setPosition( val );
1201 } );
1202 transform->setPosition( pos );
1203 }
1204 if ( Util::DrawVec<F32, 3, true>( ImGuiDataType_Float, "Rotation", rot._v, rotationReadOnly ).wasChanged )
1205 {
1206 ret = true;
1207 RegisterUndo<vec3<F32>, false>( _parent,
1209 oldRot,
1210 rot,
1211 "Transform rotation",
1212 [transform]( const vec3<F32>& val )
1213 {
1214 transform->setRotationEuler( val );
1215 } );
1216 transform->setRotationEuler( rot );
1217 }
1218 TransformComponent::ScalingMode scalingMode = transform->scalingMode();
1219 bool nonUniformScalingEnabled = scalingMode != TransformComponent::ScalingMode::UNIFORM;
1220
1221 bool scaleChanged = false;
1222 if ( nonUniformScalingEnabled )
1223 {
1224 scaleChanged = Util::DrawVec<F32, 3, true>( ImGuiDataType_Float, "Scale", scale._v, scaleReadOnly, 1.f ).wasChanged;
1225 }
1226 else
1227 {
1228 if ( Util::DrawVec<F32, 1, true>( ImGuiDataType_Float, "Scale", scale._v, scaleReadOnly, 1.f ).wasChanged )
1229 {
1230 scaleChanged = true;
1231 scale.z = scale.y = scale.x;
1232 }
1233 }
1234
1235 if ( scaleChanged )
1236 {
1237 ret = true;
1238 // Scale is tricky as it may invalidate everything if it's set wrong!
1239 for ( U8 i = 0; i < 3; ++i )
1240 {
1241 scale[i] = std::max( EPSILON_F32, scale[i] );
1242 }
1243 RegisterUndo<vec3<F32>, false>( _parent,
1245 transformValues._scale,
1246 scale,
1247 "Transform scale",
1248 [transform]( const vec3<F32>& val )
1249 {
1250 transform->setScale( val );
1251 } );
1252 transform->setScale( scale );
1253 }
1254
1255 if ( ImGui::Checkbox( "Non-uniform scaling", &nonUniformScalingEnabled ) )
1256 {
1258 RegisterUndo<bool, false>( _parent,
1260 to_U32( scalingMode ),
1261 to_U32( newMode ),
1262 "Non-uniform scaling",
1263 [transform]( const bool& oldVal ) noexcept
1264 {
1265 transform->scalingMode( static_cast<TransformComponent::ScalingMode>(oldVal) );
1266 } );
1267
1268 transform->scalingMode( newMode );
1269 ret = true;
1270 }
1271 if ( ImGui::IsItemHovered() )
1272 {
1273 ImGui::SetTooltip( "Toggle per-axis independent scale values.\nAllow shear/tear/squash/etc.\nBreaks the scene hierarchy in many ways but should be fine for leaf nodes" );
1274 skipAutoTooltip( true );
1275 }
1276
1277 if ( ImGui::Button( ICON_FK_UNDO" RESET", ImVec2( 90.f, 20 ) ) )
1278 {
1279 transform->reset();
1280 const TransformValues newTransformValues = transform->getLocalValues();
1281 RegisterUndo<TransformValues, false>( _parent,
1283 transformValues,
1284 newTransformValues,
1285 "Transform Reset",
1286 [transform]( const TransformValues& oldVal ) noexcept
1287 {
1288 transform->setTransform(oldVal);
1289 } );
1290
1291 ret = true;
1292 }
1294 return ret;
1295 }
1296
1297 bool PropertyWindow::processMaterial( Material* material, bool readOnly, bool hex )
1298 {
1299 if ( material == nullptr )
1300 {
1301 return false;
1302 }
1303
1304 if ( readOnly )
1305 {
1306 PushReadOnly();
1307 }
1308
1309 bool ret = false;
1311 {
1312 const char* crtStageName = TypeUtil::RenderStageToString( currentStagePass._stage );
1313 const char* crtPassName = TypeUtil::RenderPassTypeToString( currentStagePass._passType );
1314 if ( ImGui::BeginCombo( "Stage", crtStageName, ImGuiComboFlags_PopupAlignLeft ) )
1315 {
1316 for ( U8 n = 0; n < to_U8( RenderStage::COUNT ); ++n )
1317 {
1318 const RenderStage mode = static_cast<RenderStage>(n);
1319 const bool isSelected = currentStagePass._stage == mode;
1320
1321 if ( ImGui::Selectable( TypeUtil::RenderStageToString( mode ), isSelected ) )
1322 {
1323 currentStagePass._stage = mode;
1324 }
1325 if ( isSelected )
1326 {
1327 ImGui::SetItemDefaultFocus();
1328 }
1329 }
1330 ImGui::EndCombo();
1331 }
1332 if ( ImGui::BeginCombo( "PassType", crtPassName, ImGuiComboFlags_PopupAlignLeft ) )
1333 {
1334 for ( U8 n = 0; n < to_U8( RenderPassType::COUNT ); ++n )
1335 {
1336 const RenderPassType pass = static_cast<RenderPassType>(n);
1337 const bool isSelected = currentStagePass._passType == pass;
1338
1339 if ( ImGui::Selectable( TypeUtil::RenderPassTypeToString( pass ), isSelected ) )
1340 {
1341 currentStagePass._passType = pass;
1342 }
1343 if ( isSelected )
1344 {
1345 ImGui::SetItemDefaultFocus();
1346 }
1347 }
1348 ImGui::EndCombo();
1349 }
1350
1351 {
1352 constexpr U8 min = 0u, max = to_U8( RenderStagePass::VariantType::COUNT );
1353 ImGui::SliderScalar( "Variant", ImGuiDataType_U8, &currentStagePass._variant, &min, &max );
1354 }
1355 {
1356 constexpr U8 min = 0u, max = to_U8( RenderStagePass::PassIndex::COUNT );
1357 ImGui::SliderScalar( "Pass", ImGuiDataType_U16, &currentStagePass._pass, &min, &max );
1358 }
1359
1360 ImGui::InputScalar( "Index", ImGuiDataType_U16, &currentStagePass._index, nullptr, nullptr, (hex ? "%08X" : nullptr), (hex ? ImGuiInputTextFlags_CharsHexadecimal : 0u) );
1361 }
1362
1363 RenderStateBlock stateBlock{};
1364 string shaderName = "None";
1365 ShaderProgram* program = nullptr;
1366 if ( currentStagePass._stage != RenderStage::COUNT && currentStagePass._passType != RenderPassType::COUNT )
1367 {
1368 const Handle<ShaderProgram> shaderHandle = material->computeAndGetProgramHandle( currentStagePass );
1369 if ( shaderHandle != INVALID_HANDLE<ShaderProgram> )
1370 {
1371 program = Get( shaderHandle );
1372 shaderName = program->resourceName().c_str();
1373 }
1374 stateBlock = material->getOrCreateRenderStateBlock( currentStagePass );
1375 }
1376
1377 if ( ImGui::CollapsingHeader( ("Program: " + shaderName).c_str() ) )
1378 {
1379 if ( program != nullptr )
1380 {
1381 const ShaderProgramDescriptor& descriptor = program->descriptor();
1382 for ( const ShaderModuleDescriptor& module : descriptor._modules )
1383 {
1384 const char* stages[] = { "PS", "VS", "GS", "HS", "DS","CS" };
1385 if ( ImGui::CollapsingHeader( Util::StringFormat( "{}: File [ {} ] Variant [ {} ]",
1386 stages[to_base( module._moduleType )],
1387 module._sourceFile.data(),
1388 module._variant.empty() ? "-" : module._variant.c_str() ).c_str() ) )
1389 {
1390 ImGui::Text( "Defines: " );
1391 ImGui::Separator();
1392 for ( const auto& [text, appendPrefix] : module._defines )
1393 {
1394 ImGui::Text( text.c_str() );
1395 }
1396 if ( ImGui::Button( "Open Source File" ) )
1397 {
1399 if ( textEditor.empty() )
1400 {
1401 Attorney::EditorGeneralWidget::showStatusMessage( _context.editor(), "ERROR: No text editor specified!", Time::SecondsToMilliseconds<F32>( 3 ), true );
1402 }
1403 else
1404 {
1405 if ( openFile( textEditor.string(), Paths::Shaders::GLSL::g_GLSLShaderLoc, module._sourceFile.c_str() ) != FileError::NONE )
1406 {
1407 Attorney::EditorGeneralWidget::showStatusMessage( _context.editor(), "ERROR: Couldn't open specified source file!", Time::SecondsToMilliseconds<F32>( 3 ), true );
1408 }
1409 }
1410 }
1411 }
1412 if ( !descriptor._globalDefines.empty() )
1413 {
1414 ImGui::Text( "Global Defines: " );
1415 ImGui::Separator();
1416 for ( const auto& [text, appendPrefix] : descriptor._globalDefines )
1417 {
1418 ImGui::Text( text.c_str() );
1419 }
1420 }
1421 }
1422
1423 ImGui::Separator();
1424 if ( ImGui::Button( "Rebuild from source" ) && !readOnly )
1425 {
1426 Attorney::EditorGeneralWidget::showStatusMessage( _context.editor(), "Rebuilding shader from source ...", Time::SecondsToMilliseconds<F32>( 3 ), false );
1427 bool skipped = false;
1428 if ( !program->recompile( skipped ) )
1429 {
1430 Attorney::EditorGeneralWidget::showStatusMessage( _context.editor(), "ERROR: Failed to rebuild shader from source!", Time::SecondsToMilliseconds<F32>( 3 ), true );
1431 }
1432 else
1433 {
1434 Attorney::EditorGeneralWidget::showStatusMessage( _context.editor(), skipped ? "Rebuilt shader not needed!" : "Rebuilt shader from source!", Time::SecondsToMilliseconds<F32>( 3 ), false );
1435 ret = true;
1436 }
1437 }
1438 ImGui::Separator();
1439 }
1440 }
1441
1442 const size_t stateHash = GetHash(stateBlock);
1443 static bool renderStateWasOpen = false;
1444 if ( !ImGui::CollapsingHeader( Util::StringFormat( "Render State: {}", stateHash ).c_str(), (renderStateWasOpen ? ImGuiTreeNodeFlags_DefaultOpen : 0u) | ImGuiTreeNodeFlags_SpanAvailWidth ) )
1445 {
1446 renderStateWasOpen = false;
1447 }
1448 else if ( stateHash > 0 )
1449 {
1450 renderStateWasOpen = true;
1451
1452 bool changed = false;
1453 {
1454 P32 colourWrite = stateBlock._colourWrite;
1455 constexpr const char* const names[] = { "R", "G", "B", "A" };
1456
1457 for ( U8 i = 0; i < 4; ++i )
1458 {
1459 if ( i > 0 )
1460 {
1461 ImGui::SameLine();
1462 }
1463
1464 bool val = colourWrite.b[i] == 1;
1465 if ( ImGui::Checkbox( names[i], &val ) )
1466 {
1467 RegisterUndo<bool, false>( _parent, PushConstantType::BOOL, !val, val, "Colour Mask", [&stateBlock, material, i]( const bool& oldVal )
1468 {
1469 stateBlock._colourWrite.b[i] = oldVal;
1470 material->setRenderStateBlock( stateBlock, currentStagePass._stage, currentStagePass._passType, currentStagePass._variant );
1471 } );
1472 colourWrite.b[i] = val ? 1 : 0;
1473 stateBlock._colourWrite = colourWrite;
1474 changed = true;
1475 }
1476 }
1477 }
1478
1479 F32 zBias = stateBlock._zBias;
1480 F32 zUnits = stateBlock._zUnits;
1481 {
1482 EditorComponentField tempField = {};
1483 tempField._name = "ZBias";
1486 tempField._readOnly = readOnly;
1487 tempField._data = &zBias;
1488 tempField._range = { 0.0f, 1000.0f };
1489 const RenderStagePass tempPass = currentStagePass;
1490 tempField._dataSetter = [material, &stateBlock, tempPass]( const void* data )
1491 {
1492 stateBlock._zBias = *static_cast<const F32*>(data);
1493 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1494 };
1495 changed = processField( tempField ) || changed;
1496 }
1497 {
1498 EditorComponentField tempField = {};
1499 tempField._name = "ZUnits";
1502 tempField._readOnly = readOnly;
1503 tempField._data = &zUnits;
1504 tempField._range = { 0.0f, 65536.0f };
1505 const RenderStagePass tempPass = currentStagePass;
1506 tempField._dataSetter = [material, &stateBlock, tempPass]( const void* data )
1507 {
1508 stateBlock._zUnits = *static_cast<const F32*>(data);
1509 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1510 };
1511 changed = processField( tempField ) || changed;
1512 }
1513
1514 ImGui::Text( "Tessellation control points: %d", stateBlock._tessControlPoints );
1515
1516 {
1517 CullMode cMode = stateBlock._cullMode;
1518
1519 static UndoEntry<I32> cullUndo = {};
1520 const char* crtMode = TypeUtil::CullModeToString( cMode );
1521 if ( ImGui::BeginCombo( "Cull Mode", crtMode, ImGuiComboFlags_PopupAlignLeft ) )
1522 {
1523 for ( U8 n = 0; n < to_U8( CullMode::COUNT ); ++n )
1524 {
1525 const CullMode mode = static_cast<CullMode>(n);
1526 const bool isSelected = cMode == mode;
1527
1528 if ( ImGui::Selectable( TypeUtil::CullModeToString( mode ), isSelected ) )
1529 {
1530 cullUndo._type = PushConstantType::INT;
1531 cullUndo._name = "Cull Mode";
1532 cullUndo._oldVal = to_I32( cMode );
1533 cullUndo._newVal = to_I32( mode );
1534 const RenderStagePass tempPass = currentStagePass;
1535 cullUndo._dataSetter = [material, &stateBlock, tempPass]( const I32& data )
1536 {
1537 stateBlock._cullMode = static_cast<CullMode>(data);
1538 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1539 };
1540 _context.editor().registerUndoEntry( cullUndo );
1541
1542 cMode = mode;
1543 stateBlock._cullMode = mode;
1544 changed = true;
1545 }
1546 if ( isSelected )
1547 {
1548 ImGui::SetItemDefaultFocus();
1549 }
1550 }
1551 ImGui::EndCombo();
1552 }
1553 }
1554 {
1555 static UndoEntry<I32> fillUndo = {};
1556 FillMode fMode = stateBlock._fillMode;
1557 const char* crtMode = TypeUtil::FillModeToString( fMode );
1558 if ( ImGui::BeginCombo( "Fill Mode", crtMode, ImGuiComboFlags_PopupAlignLeft ) )
1559 {
1560 for ( U8 n = 0; n < to_U8( FillMode::COUNT ); ++n )
1561 {
1562 const FillMode mode = static_cast<FillMode>(n);
1563 const bool isSelected = fMode == mode;
1564
1565 if ( ImGui::Selectable( TypeUtil::FillModeToString( mode ), isSelected ) )
1566 {
1567 fillUndo._type = PushConstantType::INT;
1568 fillUndo._name = "Fill Mode";
1569 fillUndo._oldVal = to_I32( fMode );
1570 fillUndo._newVal = to_I32( mode );
1571 const RenderStagePass tempPass = currentStagePass;
1572 fillUndo._dataSetter = [material, &stateBlock, tempPass]( const I32& data )
1573 {
1574 stateBlock._fillMode = static_cast<FillMode>(data);
1575 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1576 };
1577 _context.editor().registerUndoEntry( fillUndo );
1578
1579 fMode = mode;
1580 stateBlock._fillMode = mode;
1581 changed = true;
1582 }
1583 if ( isSelected )
1584 {
1585 ImGui::SetItemDefaultFocus();
1586 }
1587 }
1588 ImGui::EndCombo();
1589 }
1590 }
1591
1592 U32 stencilReadMask = stateBlock._stencilMask;
1593 U32 stencilWriteMask = stateBlock._stencilWriteMask;
1594 if ( ImGui::InputScalar( "Stencil mask", ImGuiDataType_U32, &stencilReadMask, nullptr, nullptr, "%08X", ImGuiInputTextFlags_CharsHexadecimal ) )
1595 {
1596 const RenderStagePass tempPass = currentStagePass;
1597 RegisterUndo<U32, false>( _parent, PushConstantType::UINT, stateBlock._stencilMask, stencilReadMask, "Stencil mask", [material, &stateBlock, tempPass]( const U32& oldVal )
1598 {
1599 stateBlock._stencilMask = oldVal;
1600 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1601 } );
1602
1603 stateBlock._stencilMask = stencilReadMask;
1604 changed = true;
1605 }
1606
1607 if ( ImGui::InputScalar( "Stencil write mask", ImGuiDataType_U32, &stencilWriteMask, nullptr, nullptr, "%08X", ImGuiInputTextFlags_CharsHexadecimal ) )
1608 {
1609 const RenderStagePass tempPass = currentStagePass;
1610 RegisterUndo<U32, false>( _parent, PushConstantType::UINT, stateBlock._stencilWriteMask, stencilWriteMask, "Stencil write mask", [material, &stateBlock, tempPass]( const U32& oldVal )
1611 {
1612 stateBlock._stencilWriteMask = oldVal;
1613 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1614 } );
1615
1616 stateBlock._stencilWriteMask = stencilWriteMask;
1617 changed = true;
1618 }
1619 {
1620 static UndoEntry<I32> depthUndo = {};
1621 const char* crtMode = TypeUtil::ComparisonFunctionToString( stateBlock._zFunc );
1622 if ( ImGui::BeginCombo( "Depth function", crtMode, ImGuiComboFlags_PopupAlignLeft ) )
1623 {
1624 for ( U8 n = 0; n < to_U8( ComparisonFunction::COUNT ); ++n )
1625 {
1626 const ComparisonFunction func = static_cast<ComparisonFunction>(n);
1627 const bool isSelected = stateBlock._zFunc == func;
1628
1629 if ( ImGui::Selectable( TypeUtil::ComparisonFunctionToString( func ), isSelected ) )
1630 {
1631 depthUndo._type = PushConstantType::INT;
1632 depthUndo._name = "Depth function";
1633 depthUndo._oldVal = to_I32( stateBlock._zFunc );
1634 depthUndo._newVal = to_I32( func );
1635 const RenderStagePass tempPass = currentStagePass;
1636 depthUndo._dataSetter = [material, &stateBlock, tempPass]( const I32& data )
1637 {
1638 stateBlock._zFunc = static_cast<ComparisonFunction>(data);
1639 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1640 };
1641 _context.editor().registerUndoEntry( depthUndo );
1642
1643 stateBlock._zFunc = func;
1644 changed = true;
1645 }
1646 if ( isSelected )
1647 {
1648 ImGui::SetItemDefaultFocus();
1649 }
1650 }
1651 ImGui::EndCombo();
1652 }
1653 }
1654
1655 bool stencilDirty = false;
1656 U32 stencilRef = stateBlock._stencilRef;
1657 bool stencilEnabled = stateBlock._stencilEnabled;
1658 StencilOperation sFailOp = stateBlock._stencilFailOp;
1659 StencilOperation sZFailOp = stateBlock._stencilZFailOp;
1660 StencilOperation sPassOp = stateBlock._stencilPassOp;
1661 ComparisonFunction sFunc = stateBlock._stencilFunc;
1662
1663 if ( ImGui::InputScalar( "Stencil reference mask", ImGuiDataType_U32, &stencilRef, nullptr, nullptr, "%08X", ImGuiInputTextFlags_CharsHexadecimal ) )
1664 {
1665 const RenderStagePass tempPass = currentStagePass;
1666 RegisterUndo<U32, false>( _parent, PushConstantType::UINT, stateBlock._stencilRef, stencilRef, "Stencil reference mask", [material, &stateBlock, tempPass]( const U32& oldVal )
1667 {
1668 stateBlock._stencilRef = oldVal;
1669 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1670 } );
1671 stencilDirty = true;
1672 }
1673
1674 {
1675 static UndoEntry<I32> stencilUndo = {};
1676 const char* crtMode = TypeUtil::StencilOperationToString( sFailOp );
1677 if ( ImGui::BeginCombo( "Stencil fail op", crtMode, ImGuiComboFlags_PopupAlignLeft ) )
1678 {
1679 for ( U8 n = 0; n < to_U8( StencilOperation::COUNT ); ++n )
1680 {
1681 const StencilOperation op = static_cast<StencilOperation>(n);
1682 const bool isSelected = sFailOp == op;
1683
1684 if ( ImGui::Selectable( TypeUtil::StencilOperationToString( op ), isSelected ) )
1685 {
1686 stencilUndo._type = PushConstantType::INT;
1687 stencilUndo._name = "Stencil fail op";
1688 stencilUndo._oldVal = to_I32( sFailOp );
1689 stencilUndo._newVal = to_I32( op );
1690 const RenderStagePass tempPass = currentStagePass;
1691 stencilUndo._dataSetter = [material, &stateBlock, tempPass]( const I32& data )
1692 {
1693 stateBlock._stencilFailOp = static_cast<StencilOperation>(data);
1694 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1695 };
1696 _context.editor().registerUndoEntry( stencilUndo );
1697
1698 sFailOp = op;
1699 stencilDirty = true;
1700 }
1701 if ( isSelected )
1702 {
1703 ImGui::SetItemDefaultFocus();
1704 }
1705 }
1706 ImGui::EndCombo();
1707 }
1708 }
1709 {
1710 static UndoEntry<I32> stencilUndo = {};
1711 const char* crtMode = TypeUtil::StencilOperationToString( sZFailOp );
1712 if ( ImGui::BeginCombo( "Stencil depth fail op", crtMode, ImGuiComboFlags_PopupAlignLeft ) )
1713 {
1714 for ( U8 n = 0; n < to_U8( StencilOperation::COUNT ); ++n )
1715 {
1716 const StencilOperation op = static_cast<StencilOperation>(n);
1717 const bool isSelected = sZFailOp == op;
1718
1719 if ( ImGui::Selectable( TypeUtil::StencilOperationToString( op ), isSelected ) )
1720 {
1721 stencilUndo._type = PushConstantType::INT;
1722 stencilUndo._name = "Stencil depth fail op";
1723 stencilUndo._oldVal = to_I32( sZFailOp );
1724 stencilUndo._newVal = to_I32( op );
1725 const RenderStagePass tempPass = currentStagePass;
1726 stencilUndo._dataSetter = [material, &stateBlock, tempPass]( const I32& data )
1727 {
1728 stateBlock._stencilZFailOp = static_cast<StencilOperation>(data);
1729 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1730 };
1731 _context.editor().registerUndoEntry( stencilUndo );
1732
1733 sZFailOp = op;
1734 stencilDirty = true;
1735 }
1736 if ( isSelected )
1737 {
1738 ImGui::SetItemDefaultFocus();
1739 }
1740 }
1741 ImGui::EndCombo();
1742 }
1743 }
1744 {
1745 static UndoEntry<I32> stencilUndo = {};
1746 const char* crtMode = TypeUtil::StencilOperationToString( sPassOp );
1747 if ( ImGui::BeginCombo( "Stencil pass op", crtMode, ImGuiComboFlags_PopupAlignLeft ) )
1748 {
1749 for ( U8 n = 0; n < to_U8( StencilOperation::COUNT ); ++n )
1750 {
1751 const StencilOperation op = static_cast<StencilOperation>(n);
1752 const bool isSelected = sPassOp == op;
1753
1754 if ( ImGui::Selectable( TypeUtil::StencilOperationToString( op ), isSelected ) )
1755 {
1756
1757 stencilUndo._type = PushConstantType::INT;
1758 stencilUndo._name = "Stencil pass op";
1759 stencilUndo._oldVal = to_I32( sPassOp );
1760 stencilUndo._newVal = to_I32( op );
1761 const RenderStagePass tempPass = currentStagePass;
1762 stencilUndo._dataSetter = [material, &stateBlock, tempPass]( const I32& data )
1763 {
1764 stateBlock._stencilPassOp = static_cast<StencilOperation>(data);
1765 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1766 };
1767 _context.editor().registerUndoEntry( stencilUndo );
1768
1769 sPassOp = op;
1770 stencilDirty = true;
1771 }
1772 if ( isSelected )
1773 {
1774 ImGui::SetItemDefaultFocus();
1775 }
1776 }
1777 ImGui::EndCombo();
1778 }
1779 }
1780 {
1781 static UndoEntry<I32> stencilUndo = {};
1782 const char* crtMode = TypeUtil::ComparisonFunctionToString( sFunc );
1783 if ( ImGui::BeginCombo( "Stencil function", crtMode, ImGuiComboFlags_PopupAlignLeft ) )
1784 {
1785 for ( U8 n = 0; n < to_U8( ComparisonFunction::COUNT ); ++n )
1786 {
1787 const ComparisonFunction mode = static_cast<ComparisonFunction>(n);
1788 const bool isSelected = sFunc == mode;
1789
1790 if ( ImGui::Selectable( TypeUtil::ComparisonFunctionToString( mode ), isSelected ) )
1791 {
1792
1793 stencilUndo._type = PushConstantType::INT;
1794 stencilUndo._name = "Stencil function";
1795 stencilUndo._oldVal = to_I32( sFunc );
1796 stencilUndo._newVal = to_I32( mode );
1797 const RenderStagePass tempPass = currentStagePass;
1798 stencilUndo._dataSetter = [material, &stateBlock, tempPass]( const I32& data )
1799 {
1800 stateBlock._stencilFunc = static_cast<ComparisonFunction>(data);
1801 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1802 };
1803 _context.editor().registerUndoEntry( stencilUndo );
1804
1805 sFunc = mode;
1806 stencilDirty = true;
1807 }
1808 if ( isSelected )
1809 {
1810 ImGui::SetItemDefaultFocus();
1811 }
1812 }
1813 ImGui::EndCombo();
1814 }
1815 }
1816
1817 bool frontFaceCCW = stateBlock._frontFaceCCW;
1818 if ( ImGui::Checkbox( "CCW front face", &frontFaceCCW ) )
1819 {
1820 const RenderStagePass tempPass = currentStagePass;
1821 RegisterUndo<bool, false>( _parent, PushConstantType::BOOL, !frontFaceCCW, frontFaceCCW, "CCW front face", [material, &stateBlock, tempPass]( const bool& oldVal )
1822 {
1823 stateBlock._frontFaceCCW = oldVal;
1824 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1825 } );
1826
1827 stateBlock._frontFaceCCW = frontFaceCCW;
1828 changed = true;
1829 }
1830
1831 bool scissorEnabled = stateBlock._scissorTestEnabled;
1832 if ( ImGui::Checkbox( "Scissor test", &scissorEnabled ) )
1833 {
1834 const RenderStagePass tempPass = currentStagePass;
1835 RegisterUndo<bool, false>( _parent, PushConstantType::BOOL, !scissorEnabled, scissorEnabled, "Scissor test", [material, &stateBlock, tempPass]( const bool& oldVal )
1836 {
1837 stateBlock._scissorTestEnabled = oldVal;
1838 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1839 } );
1840
1841 stateBlock._scissorTestEnabled = scissorEnabled;
1842 changed = true;
1843 }
1844
1845 bool depthTestEnabled = stateBlock._depthTestEnabled;
1846 if ( ImGui::Checkbox( "Depth test", &depthTestEnabled ) )
1847 {
1848 const RenderStagePass tempPass = currentStagePass;
1849 RegisterUndo<bool, false>( _parent, PushConstantType::BOOL, !depthTestEnabled, depthTestEnabled, "Depth test", [material, &stateBlock, tempPass]( const bool& oldVal )
1850 {
1851 stateBlock._depthTestEnabled = oldVal;
1852 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1853 } );
1854
1855 stateBlock._depthTestEnabled = depthTestEnabled;
1856 changed = true;
1857 }
1858
1859 bool depthWriteEnabled = stateBlock._depthWriteEnabled;
1860 if ( ImGui::Checkbox( "Depth write", &depthWriteEnabled ) )
1861 {
1862 const RenderStagePass tempPass = currentStagePass;
1863 RegisterUndo<bool, false>( _parent, PushConstantType::BOOL, !depthWriteEnabled, depthWriteEnabled, "Depth write", [material, &stateBlock, tempPass]( const bool& oldVal )
1864 {
1865 stateBlock._depthWriteEnabled = oldVal;
1866 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1867 } );
1868
1869 stateBlock._depthWriteEnabled = depthWriteEnabled;
1870 changed = true;
1871 }
1872
1873 if ( ImGui::Checkbox( "Stencil test", &stencilEnabled ) )
1874 {
1875 const RenderStagePass tempPass = currentStagePass;
1876 RegisterUndo<bool, false>( _parent, PushConstantType::BOOL, !stencilEnabled, stencilEnabled, "Stencil test", [material, &stateBlock, tempPass]( const bool& oldVal )
1877 {
1878 stateBlock._stencilEnabled = oldVal;
1879 material->setRenderStateBlock( stateBlock, tempPass._stage, tempPass._passType, tempPass._variant );
1880 } );
1881
1882 stencilDirty = true;
1883 }
1884
1885 if ( stencilDirty )
1886 {
1887 stateBlock._stencilRef = stencilRef;
1888 stateBlock._stencilEnabled = stencilEnabled;
1889 stateBlock._stencilFailOp = sFailOp;
1890 stateBlock._stencilZFailOp = sZFailOp;
1891 stateBlock._stencilPassOp = sPassOp;
1892 stateBlock._stencilFunc = sFunc;
1893 changed = true;
1894 }
1895
1896 if ( changed && !readOnly )
1897 {
1898 material->setRenderStateBlock( stateBlock, currentStagePass._stage, currentStagePass._passType, currentStagePass._variant );
1899 ret = true;
1900 }
1901 }
1902
1903 static bool shadingModeWasOpen = false;
1904 const ShadingMode crtMode = material->properties().shadingMode();
1905 const char* crtModeName = TypeUtil::ShadingModeToString( crtMode );
1906 if ( !ImGui::CollapsingHeader( crtModeName, (shadingModeWasOpen ? ImGuiTreeNodeFlags_DefaultOpen : 0u) | ImGuiTreeNodeFlags_SpanAvailWidth ) )
1907 {
1908 shadingModeWasOpen = false;
1909 }
1910 else
1911 {
1912 skipAutoTooltip( true );
1913
1914 shadingModeWasOpen = true;
1915 {
1916 static UndoEntry<I32> modeUndo = {};
1917 ImGui::PushItemWidth( 250 );
1918 if ( ImGui::BeginCombo( "[Shading Mode]", crtModeName, ImGuiComboFlags_PopupAlignLeft ) )
1919 {
1920 for ( U8 n = 0; n < to_U8( ShadingMode::COUNT ); ++n )
1921 {
1922 const ShadingMode mode = static_cast<ShadingMode>(n);
1923 const bool isSelected = crtMode == mode;
1924
1925 if ( ImGui::Selectable( TypeUtil::ShadingModeToString( mode ), isSelected ) )
1926 {
1927
1928 modeUndo._type = PushConstantType::INT;
1929 modeUndo._name = "Shading Mode";
1930 modeUndo._oldVal = to_I32( crtMode );
1931 modeUndo._newVal = to_I32( mode );
1932 modeUndo._dataSetter = [material, mode]( [[maybe_unused]] const I32& data )
1933 {
1934 material->properties().shadingMode( mode );
1935 };
1936 _context.editor().registerUndoEntry( modeUndo );
1937
1938 material->properties().shadingMode( mode );
1939 }
1940 if ( isSelected )
1941 {
1942 ImGui::SetItemDefaultFocus();
1943 }
1944 }
1945 ImGui::EndCombo();
1946 }
1947 ImGui::PopItemWidth();
1948 }
1949 I32 id = 0;
1950
1951 ApplyAllButton( id, false, *material, []( const Material& baseMaterial, Material* matInstance )
1952 {
1953 matInstance->properties().shadingMode( baseMaterial.properties().shadingMode() );
1954 } );
1955
1956 bool fromTexture = false;
1957 Handle<Texture> texture = INVALID_HANDLE<Texture>;
1958 { //Base colour
1959 ImGui::Separator();
1960 ImGui::PushItemWidth( 250 );
1961 FColour4 diffuse = material->getBaseColour( fromTexture, texture );
1962 if ( Util::colourInput4( _parent, "[Albedo]", diffuse, readOnly, [material]( const FColour4& col )
1963 {
1964 material->properties().baseColour( col ); return true;
1965 } ) )
1966 {
1967 ret = true;
1968 }
1969 ImGui::PopItemWidth();
1970 if ( fromTexture && ImGui::IsItemHovered( ImGuiHoveredFlags_AllowWhenDisabled ) )
1971 {
1972 ImGui::SetTooltip( "Albedo is sampled from a texture. Base colour possibly unused!" );
1973 skipAutoTooltip( true );
1974 }
1975 ApplyAllButton( id, fromTexture || readOnly, *material, []( const Material& baseMaterial, Material* matInstance )
1976 {
1977 matInstance->properties().baseColour( baseMaterial.properties().baseColour() );
1978 } );
1979 if ( PreviewTextureButton( id, texture, !fromTexture ) )
1980 {
1981 _previewTexture = texture;
1982 }
1983 ImGui::Separator();
1984 }
1985 { //Second texture
1986 Handle<Texture> detailTex = material->getTexture( TextureSlot::UNIT1 );
1987 const bool ro = detailTex == INVALID_HANDLE<Texture>;
1988 ImGui::PushID( 4321234 + id++ );
1989 if ( ro || readOnly )
1990 {
1991 PushReadOnly();
1992 }
1993
1994 bool showTexture = false;
1995 if ( ImGui::Button( "Detail Texture" ) )
1996 {
1997 ret = true;
1998 showTexture = true;
1999 }
2000 if ( ro || readOnly )
2001 {
2002 PopReadOnly();
2003 }
2004 if ( ImGui::IsItemHovered( ImGuiHoveredFlags_AllowWhenDisabled ) )
2005 {
2006 if ( ro )
2007 {
2008 ImGui::SetTooltip( "No detail texture specified!" );
2009 }
2010 else
2011 {
2012 ImGui::SetTooltip( Util::StringFormat( "Preview texture : {}", Get(detailTex)->assetName() ).c_str() );
2013 }
2014 skipAutoTooltip( true );
2015 }
2016 ImGui::PopID();
2017 if ( showTexture && !ro )
2018 {
2019 _previewTexture = detailTex;
2020 }
2021 }
2022 { //Normal
2023 Handle<Texture> normalTex = material->getTexture( TextureSlot::NORMALMAP );
2024 const bool ro = normalTex == INVALID_HANDLE<Texture>;
2025 ImGui::PushID( 4321234 + id++ );
2026 if ( ro || readOnly )
2027 {
2028 PushReadOnly();
2029 }
2030 bool showTexture = false;
2031 if ( ImGui::Button( "Normal Map" ) )
2032 {
2033 ret = true;
2034 showTexture = true;
2035 }
2036 if ( ro || readOnly )
2037 {
2038 PopReadOnly();
2039 }
2040 if ( ImGui::IsItemHovered( ImGuiHoveredFlags_AllowWhenDisabled ) )
2041 {
2042 if ( ro )
2043 {
2044 ImGui::SetTooltip( "No normal map specified!" );
2045 }
2046 else
2047 {
2048 ImGui::SetTooltip( Util::StringFormat( "Preview texture : {}", Get(normalTex)->assetName() ).c_str() );
2049 }
2050 skipAutoTooltip( true );
2051 }
2052 ImGui::PopID();
2053 if ( showTexture && !ro )
2054 {
2055 _previewTexture = normalTex;
2056 }
2057 }
2058 { //Emissive
2059 ImGui::Separator();
2060
2061 ImGui::PushItemWidth( 250 );
2062 FColour3 emissive = material->getEmissive( fromTexture, texture );
2063 if ( Util::colourInput3( _parent, "[Emissive]", emissive, fromTexture || readOnly, [&material]( const FColour3& col )
2064 {
2065 material->properties().emissive( col ); return true;
2066 } ) )
2067 {
2068 ret = true;
2069 }
2070 ImGui::PopItemWidth();
2071 if ( fromTexture && ImGui::IsItemHovered( ImGuiHoveredFlags_AllowWhenDisabled ) )
2072 {
2073 ImGui::SetTooltip( "Control managed by application (e.g. is overriden by a texture)" );
2074 skipAutoTooltip( true );
2075 }
2076 ApplyAllButton( id, fromTexture || readOnly, *material, []( const Material& baseMaterial, Material* matInstance )
2077 {
2078 matInstance->properties().emissive( baseMaterial.properties().emissive() );
2079 } );
2080 if ( PreviewTextureButton( id, texture, !fromTexture ) )
2081 {
2082 _previewTexture = texture;
2083 }
2084 ImGui::Separator();
2085 }
2086 { //Ambient
2087 ImGui::Separator();
2088 ImGui::PushItemWidth( 250 );
2089 FColour3 ambient = material->getAmbient( fromTexture, texture );
2090 if ( Util::colourInput3( _parent, "[Ambient]", ambient, fromTexture || readOnly, [material]( const FColour3& colour )
2091 {
2092 material->properties().ambient( colour ); return true;
2093 } ) )
2094 {
2095 ret = true;
2096 }
2097 ImGui::PopItemWidth();
2098 if ( fromTexture && ImGui::IsItemHovered( ImGuiHoveredFlags_AllowWhenDisabled ) )
2099 {
2100 ImGui::SetTooltip( "Control managed by application (e.g. is overriden by a texture)" );
2101 skipAutoTooltip( true );
2102 }
2103 ApplyAllButton( id, fromTexture || readOnly, *material, []( const Material& baseMaterial, Material* matInstance )
2104 {
2105 matInstance->properties().ambient( baseMaterial.properties().ambient() );
2106 } );
2107 if ( PreviewTextureButton( id, texture, !fromTexture ) )
2108 {
2109 _previewTexture = texture;
2110 }
2111 ImGui::Separator();
2112 }
2113 if ( material->properties().shadingMode() != ShadingMode::PBR_MR &&
2114 material->properties().shadingMode() != ShadingMode::PBR_SG )
2115 {
2116 FColour3 specular = material->getSpecular( fromTexture, texture );
2117 F32 shininess = material->properties().shininess();
2118
2119 { //Specular power
2120 EditorComponentField tempField = {};
2121 tempField._name = "Shininess";
2124 tempField._readOnly = readOnly;
2125 tempField._data = &shininess;
2126 tempField._range = { 0.0f, Material::MAX_SHININESS };
2127 tempField._dataSetter = [&material]( const void* s )
2128 {
2129 material->properties().shininess( *static_cast<const F32*>(s) );
2130 };
2131
2132 ImGui::PushItemWidth( 175 );
2133 ret = processBasicField( tempField ) || ret;
2134 ImGui::PopItemWidth();
2135 ImGui::SameLine();
2136 ApplyAllButton( id, fromTexture || readOnly, *material, []( const Material& baseMaterial, Material* matInstance )
2137 {
2138 matInstance->properties().shininess( baseMaterial.properties().shininess() );
2139 } );
2140 }
2141 { //Specular colour
2142 ImGui::Separator();
2143 ImGui::PushItemWidth( 250 );
2144 if ( Util::colourInput3( _parent, "[Specular]", specular, fromTexture || readOnly, [material]( const FColour3& col )
2145 {
2146 material->properties().specular( col ); return true;
2147 } ) )
2148 {
2149 ret = true;
2150 }
2151 ImGui::PopItemWidth();
2152 if ( fromTexture && ImGui::IsItemHovered( ImGuiHoveredFlags_AllowWhenDisabled ) )
2153 {
2154 ImGui::SetTooltip( "Control managed by application (e.g. is overriden by a texture)" );
2155 }
2156 ApplyAllButton( id, fromTexture || readOnly, *material, []( const Material& baseMaterial, Material* matInstance )
2157 {
2158 matInstance->properties().specular( baseMaterial.properties().specular() );
2159 } );
2160 if ( PreviewTextureButton( id, texture, !fromTexture ) )
2161 {
2162 _previewTexture = texture;
2163 }
2164 skipAutoTooltip( true );
2165 ImGui::Separator();
2166 }
2167 }
2168 else
2169 {
2170 { // Metallic
2171 ImGui::Separator();
2172 F32 metallic = material->getMetallic( fromTexture, texture );
2173 EditorComponentField tempField = {};
2174 tempField._name = "Metallic";
2177 tempField._readOnly = fromTexture || readOnly;
2178 if ( fromTexture )
2179 {
2180 tempField._tooltip = "Control managed by application (e.g. is overriden by a texture)";
2181 }
2182 tempField._data = &metallic;
2183 tempField._range = { 0.0f, 1.0f };
2184 tempField._dataSetter = [material]( const void* m )
2185 {
2186 material->properties().metallic( *static_cast<const F32*>(m) );
2187 };
2188
2189 ImGui::PushItemWidth( 175 );
2190 ret = processBasicField( tempField ) || ret;
2191 ImGui::PopItemWidth();
2192
2193 ImGui::SameLine();
2194 ApplyAllButton( id, fromTexture || readOnly, *material, []( const Material& baseMaterial, Material* matInstance )
2195 {
2196 matInstance->properties().metallic( baseMaterial.properties().metallic() );
2197 } );
2198 if ( PreviewTextureButton( id, texture, !fromTexture ) )
2199 {
2200 _previewTexture = texture;
2201 }
2202 ImGui::Separator();
2203 }
2204 { // Roughness
2205 ImGui::Separator();
2206 F32 roughness = material->getRoughness( fromTexture, texture );
2207 EditorComponentField tempField = {};
2208 tempField._name = "Roughness";
2211 tempField._readOnly = fromTexture || readOnly;
2212 if ( fromTexture )
2213 {
2214 tempField._tooltip = "Control managed by application (e.g. is overriden by a texture)";
2215 }
2216 tempField._data = &roughness;
2217 tempField._range = { 0.0f, 1.0f };
2218 tempField._dataSetter = [material]( const void* r )
2219 {
2220 material->properties().roughness( *static_cast<const F32*>(r) );
2221 };
2222 ImGui::PushItemWidth( 175 );
2223 ret = processBasicField( tempField ) || ret;
2224 ImGui::PopItemWidth();
2225 ImGui::SameLine();
2226 ApplyAllButton( id, fromTexture || readOnly, *material, []( const Material& baseMaterial, Material* matInstance )
2227 {
2228 matInstance->properties().roughness( baseMaterial.properties().roughness() );
2229 } );
2230 if ( PreviewTextureButton( id, texture, !fromTexture ) )
2231 {
2232 _previewTexture = texture;
2233 }
2234 ImGui::Separator();
2235 }
2236
2237 { // Occlusion
2238 ImGui::Separator();
2239 F32 occlusion = material->getOcclusion( fromTexture, texture );
2240 EditorComponentField tempField = {};
2241 tempField._name = "Occlusion";
2244 tempField._readOnly = fromTexture || readOnly;
2245 if ( fromTexture )
2246 {
2247 tempField._tooltip = "Control managed by application (e.g. is overriden by a texture)";
2248 }
2249 tempField._data = &occlusion;
2250 tempField._range = { 0.0f, 1.0f };
2251 tempField._dataSetter = [material]( const void* m )
2252 {
2253 material->properties().occlusion( *static_cast<const F32*>(m) );
2254 };
2255
2256 ImGui::PushItemWidth( 175 );
2257 ret = processBasicField( tempField ) || ret;
2258 ImGui::PopItemWidth();
2259
2260 ImGui::SameLine();
2261 ApplyAllButton( id, fromTexture || readOnly, *material, []( const Material& baseMaterial, Material* matInstance )
2262 {
2263 matInstance->properties().occlusion( baseMaterial.properties().occlusion() );
2264 } );
2265 if ( PreviewTextureButton( id, texture, !fromTexture ) )
2266 {
2267 _previewTexture = texture;
2268 }
2269 ImGui::Separator();
2270 }
2271 }
2272 { // Parallax
2273 ImGui::Separator();
2274 F32 parallax = material->properties().parallaxFactor();
2275 EditorComponentField tempField = {};
2276 tempField._name = "Parallax";
2279 tempField._readOnly = readOnly;
2280 tempField._data = &parallax;
2281 tempField._range = { 0.0f, 1.0f };
2282 tempField._dataSetter = [material]( const void* p )
2283 {
2284 material->properties().parallaxFactor( *static_cast<const F32*>(p) );
2285 };
2286 ImGui::PushItemWidth( 175 );
2287 ret = processBasicField( tempField ) || ret;
2288 ImGui::PopItemWidth();
2289 ImGui::SameLine();
2290 ApplyAllButton( id, fromTexture || readOnly, *material, []( const Material& baseMaterial, Material* matInstance )
2291 {
2292 matInstance->properties().parallaxFactor( baseMaterial.properties().parallaxFactor() );
2293 } );
2294 ImGui::Separator();
2295 }
2296 { // Texture operations
2297 constexpr const char* const names[] = {
2298 "Tex operation [Albedo - Tex0]",
2299 "Tex operation [(Albedo*Tex0) - Tex1]",
2300 "Tex operation [SpecColour - SpecMap]"
2301 };
2302
2303 static UndoEntry<I32> opUndo = {};
2304 for ( U8 i = 0; i < 3; ++i )
2305 {
2306 const TextureSlot targetTex = i == 0 ? TextureSlot::UNIT0
2307 : i == 1 ? TextureSlot::UNIT1
2309
2310 const bool hasTexture = material->getTexture( targetTex ) != INVALID_HANDLE<Texture>;
2311
2312 ImGui::PushID( 4321234 + id++ );
2313 if ( !hasTexture )
2314 {
2315 PushReadOnly();
2316 }
2317 const TextureOperation crtOp = material->getTextureInfo( targetTex )._operation;
2318 ImGui::Text( names[i] );
2319 if ( ImGui::BeginCombo( "", TypeUtil::TextureOperationToString( crtOp ), ImGuiComboFlags_PopupAlignLeft ) )
2320 {
2321 for ( U8 n = 0; n < to_U8( TextureOperation::COUNT ); ++n )
2322 {
2323 const TextureOperation op = static_cast<TextureOperation>(n);
2324 const bool isSelected = op == crtOp;
2325
2326 if ( ImGui::Selectable( TypeUtil::TextureOperationToString( op ), isSelected ) )
2327 {
2328
2330 opUndo._name = "Tex Operation " + Util::to_string( i );
2331 opUndo._oldVal = to_I32( crtOp );
2332 opUndo._newVal = to_I32( op );
2333 opUndo._dataSetter = [material, targetTex]( const I32& data )
2334 {
2335 material->setTextureOperation( targetTex, static_cast<TextureOperation>(data) );
2336 };
2337 _context.editor().registerUndoEntry( opUndo );
2338 material->setTextureOperation( targetTex, op );
2339 }
2340 if ( isSelected )
2341 {
2342 ImGui::SetItemDefaultFocus();
2343 }
2344 }
2345 ImGui::EndCombo();
2346 }
2347 ApplyAllButton( id, fromTexture || readOnly, *material, [targetTex]( const Material& baseMaterial, Material* matInstance )
2348 {
2349 matInstance->setTextureOperation( targetTex, baseMaterial.getTextureInfo( targetTex )._operation );
2350 } );
2351 if ( !hasTexture )
2352 {
2353 PopReadOnly();
2354 if ( ImGui::IsItemHovered() )
2355 {
2356 ImGui::SetTooltip( "Insuficient input textures for this operation!" );
2357 skipAutoTooltip( true );
2358 }
2359 }
2360 ImGui::PopID();
2361 }
2362
2363 ImGui::Separator();
2364 }
2365 bool ignoreTexAlpha = material->properties().overrides().ignoreTexDiffuseAlpha();
2366 bool doubleSided = material->properties().doubleSided();
2367 bool refractive = material->properties().isRefractive();
2368
2369 ImGui::Text( "[Double Sided]" ); ImGui::SameLine();
2370 if ( ImGui::ToggleButton( "[Double Sided]", &doubleSided ) && !readOnly )
2371 {
2372 RegisterUndo<bool, false>( _parent, PushConstantType::BOOL, !doubleSided, doubleSided, "DoubleSided", [material]( const bool& oldVal )
2373 {
2374 material->properties().doubleSided( oldVal );
2375 } );
2376 material->properties().doubleSided( doubleSided );
2377 ret = true;
2378 }
2379 ApplyAllButton( id, fromTexture || readOnly, *material, []( const Material& baseMaterial, Material* matInstance )
2380 {
2381 matInstance->properties().doubleSided( baseMaterial.properties().doubleSided() );
2382 } );
2383 ImGui::Text( "[Ignore texture Alpha]" ); ImGui::SameLine();
2384 if ( ImGui::ToggleButton( "[Ignore texture Alpha]", &ignoreTexAlpha ) && !readOnly )
2385 {
2386 RegisterUndo<bool, false>( _parent, PushConstantType::BOOL, !ignoreTexAlpha, ignoreTexAlpha, "IgnoretextureAlpha", [material]( const bool& oldVal )
2387 {
2388 material->properties().ignoreTexDiffuseAlpha( oldVal );
2389 } );
2390 material->properties().ignoreTexDiffuseAlpha( ignoreTexAlpha );
2391 ret = true;
2392 }
2393 ApplyAllButton( id, fromTexture || readOnly, *material, []( const Material& baseMaterial, Material* matInstance )
2394 {
2395 matInstance->properties().ignoreTexDiffuseAlpha( baseMaterial.properties().overrides().ignoreTexDiffuseAlpha() );
2396 } );
2397 ImGui::Text( "[Refractive]" ); ImGui::SameLine();
2398 if ( ImGui::ToggleButton( "[Refractive]", &refractive ) && !readOnly )
2399 {
2400 RegisterUndo<bool, false>( _parent, PushConstantType::BOOL, !refractive, refractive, "Refractive", [material]( const bool& oldVal )
2401 {
2402 material->properties().isRefractive( oldVal );
2403 } );
2404 material->properties().isRefractive( refractive );
2405 ret = true;
2406 }
2407 ApplyAllButton( id, fromTexture || readOnly, *material, []( const Material& baseMaterial, Material* matInstance )
2408 {
2409 matInstance->properties().isRefractive( baseMaterial.properties().isRefractive() );
2410 } );
2411 }
2412 ImGui::Separator();
2413
2414 if ( readOnly )
2415 {
2416 PopReadOnly();
2417 ret = false;
2418 }
2419 return ret;
2420 }
2421
2423 {
2424 const bool isSlider = field._type == EditorComponentFieldType::SLIDER_TYPE &&
2426 !field.isMatrix();
2427
2428 const ImGuiInputTextFlags flags = Util::GetDefaultFlagsForField(field);
2429
2430 const char* name = field._name.c_str();
2431
2432 ImGui::PushID( name );
2433 Util::PushTooltip( field._tooltip.c_str() );
2434
2435 const F32 step = field._step;
2436 bool ret = false;
2437 switch ( field._basicType )
2438 {
2440 {
2441 if ( field._readOnly )
2442 {
2443 PushReadOnly();
2444 }
2445
2446 ImFont* boldFont = ImGui::GetIO().Fonts->Fonts[1];
2447
2448 static UndoEntry<bool> undo = {};
2449 bool val = field.get<bool>();
2451 {
2452 ret = ImGui::ToggleButton( "", &val );
2453 }
2454 else
2455 {
2456 ret = ImGui::Checkbox( "", &val );
2457 }
2458 ImGui::SameLine();
2459 ImGui::PushFont( boldFont );
2460 ImGui::Text( name );
2461 ImGui::PopFont();
2462 if ( ImGui::IsItemHovered( ImGuiHoveredFlags_AllowWhenDisabled ) )
2463 {
2464 if ( Util::IsPushedTooltip() )
2465 {
2466 ImGui::SetTooltip( Util::PushedToolTip() );
2467 }
2468 else
2469 {
2470 ImGui::SetTooltip( name );
2471 }
2472 skipAutoTooltip( true );
2473 }
2474
2475 if ( ret && !field._readOnly )
2476 {
2477 RegisterUndo<bool, false>( _parent, PushConstantType::BOOL, !val, val, name, [&field]( const bool& oldVal )
2478 {
2479 field.set( oldVal );
2480 } );
2481 field.set<bool>( val );
2482 }
2483 if ( field._readOnly )
2484 {
2485 PopReadOnly();
2486 }
2487 }break;
2489 {
2490 switch ( field._basicTypeSize )
2491 {
2492 case PushConstantSize::QWORD: ret = Util::inputOrSlider<I64, I64, 1>( _parent, isSlider, name, step, ImGuiDataType_S64, field, flags, field._format ); break;
2493 case PushConstantSize::DWORD: ret = Util::inputOrSlider<I32, I32, 1>( _parent, isSlider, name, step, ImGuiDataType_S32, field, flags, field._format ); break;
2494 case PushConstantSize::WORD: ret = Util::inputOrSlider<I16, I16, 1>( _parent, isSlider, name, step, ImGuiDataType_S16, field, flags, field._format ); break;
2495 case PushConstantSize::BYTE: ret = Util::inputOrSlider<I8, I8, 1>( _parent, isSlider, name, step, ImGuiDataType_S8, field, flags, field._format ); break;
2497 }
2498 }break;
2500 {
2501 switch ( field._basicTypeSize )
2502 {
2503 case PushConstantSize::QWORD: ret = Util::inputOrSlider<U64, U64, 1>( _parent, isSlider, name, step, ImGuiDataType_U64, field, flags, field._format ); break;
2504 case PushConstantSize::DWORD: ret = Util::inputOrSlider<U32, U32, 1>( _parent, isSlider, name, step, ImGuiDataType_U32, field, flags, field._format ); break;
2505 case PushConstantSize::WORD: ret = Util::inputOrSlider<U16, U16, 1>( _parent, isSlider, name, step, ImGuiDataType_U16, field, flags, field._format ); break;
2506 case PushConstantSize::BYTE: ret = Util::inputOrSlider<U8, U8, 1>( _parent, isSlider, name, step, ImGuiDataType_U8, field, flags, field._format ); break;
2508 }
2509 }break;
2511 {
2512 ret = Util::inputOrSlider<D64, D64, 1>( _parent, isSlider, name, step, ImGuiDataType_Double, field, flags, field._format );
2513 }break;
2515 {
2516 ret = Util::inputOrSlider<F32, F32, 1>( _parent, isSlider, name, step, ImGuiDataType_Float, field, flags, field._format );
2517 }break;
2519 {
2520 switch ( field._basicTypeSize )
2521 {
2522 case PushConstantSize::QWORD: ret = Util::inputOrSlider<vec2<I64>, I64, 2>( _parent, isSlider, name, step, ImGuiDataType_S64, field, flags, field._format ); break;
2523 case PushConstantSize::DWORD: ret = Util::inputOrSlider<vec2<I32>, I32, 2>( _parent, isSlider, name, step, ImGuiDataType_S32, field, flags, field._format ); break;
2524 case PushConstantSize::WORD: ret = Util::inputOrSlider<vec2<I16>, I16, 2>( _parent, isSlider, name, step, ImGuiDataType_S16, field, flags, field._format ); break;
2525 case PushConstantSize::BYTE: ret = Util::inputOrSlider<vec2<I8>, I8, 2>( _parent, isSlider, name, step, ImGuiDataType_S8, field, flags, field._format ); break;
2527 }
2528 }break;
2530 {
2531 switch ( field._basicTypeSize )
2532 {
2533 case PushConstantSize::QWORD: ret = Util::inputOrSlider<vec3<I64>, I64, 3>( _parent, isSlider, name, step, ImGuiDataType_S64, field, flags, field._format ); break;
2534 case PushConstantSize::DWORD: ret = Util::inputOrSlider<vec3<I32>, I32, 3>( _parent, isSlider, name, step, ImGuiDataType_S32, field, flags, field._format ); break;
2535 case PushConstantSize::WORD: ret = Util::inputOrSlider<vec3<I16>, I16, 3>( _parent, isSlider, name, step, ImGuiDataType_S16, field, flags, field._format ); break;
2536 case PushConstantSize::BYTE: ret = Util::inputOrSlider<vec3<I8>, I8, 3>( _parent, isSlider, name, step, ImGuiDataType_S8, field, flags, field._format ); break;
2538 }
2539 }break;
2541 {
2542 switch ( field._basicTypeSize )
2543 {
2544 case PushConstantSize::QWORD: ret = Util::inputOrSlider<vec4<I64>, I64, 4>( _parent, isSlider, name, step, ImGuiDataType_S64, field, flags, field._format ); break;
2545 case PushConstantSize::DWORD: ret = Util::inputOrSlider<vec4<I32>, I32, 4>( _parent, isSlider, name, step, ImGuiDataType_S32, field, flags, field._format ); break;
2546 case PushConstantSize::WORD: ret = Util::inputOrSlider<vec4<I16>, I16, 4>( _parent, isSlider, name, step, ImGuiDataType_S16, field, flags, field._format ); break;
2547 case PushConstantSize::BYTE: ret = Util::inputOrSlider<vec4<I8>, I8, 4>( _parent, isSlider, name, step, ImGuiDataType_S8, field, flags, field._format ); break;
2549 }
2550 }break;
2552 {
2553 switch ( field._basicTypeSize )
2554 {
2555 case PushConstantSize::QWORD: ret = Util::inputOrSlider<vec2<U64>, U64, 2>( _parent, isSlider, name, step, ImGuiDataType_U64, field, flags, field._format ); break;
2556 case PushConstantSize::DWORD: ret = Util::inputOrSlider<vec2<U32>, U32, 2>( _parent, isSlider, name, step, ImGuiDataType_U32, field, flags, field._format ); break;
2557 case PushConstantSize::WORD: ret = Util::inputOrSlider<vec2<U16>, U16, 2>( _parent, isSlider, name, step, ImGuiDataType_U16, field, flags, field._format ); break;
2558 case PushConstantSize::BYTE: ret = Util::inputOrSlider<vec2<U8>, U8, 2>( _parent, isSlider, name, step, ImGuiDataType_U8, field, flags, field._format ); break;
2560 }
2561 }break;
2563 {
2564 switch ( field._basicTypeSize )
2565 {
2566 case PushConstantSize::QWORD: ret = Util::inputOrSlider<vec3<U64>, U64, 3>( _parent, isSlider, name, step, ImGuiDataType_U64, field, flags, field._format ); break;
2567 case PushConstantSize::DWORD: ret = Util::inputOrSlider<vec3<U32>, U32, 3>( _parent, isSlider, name, step, ImGuiDataType_U32, field, flags, field._format ); break;
2568 case PushConstantSize::WORD: ret = Util::inputOrSlider<vec3<U16>, U16, 3>( _parent, isSlider, name, step, ImGuiDataType_U16, field, flags, field._format ); break;
2569 case PushConstantSize::BYTE: ret = Util::inputOrSlider<vec3<U8>, U8, 3>( _parent, isSlider, name, step, ImGuiDataType_U8, field, flags, field._format ); break;
2571 }
2572 }break;
2574 {
2575 switch ( field._basicTypeSize )
2576 {
2577 case PushConstantSize::QWORD: ret = Util::inputOrSlider<vec4<U64>, U64, 4>( _parent, isSlider, name, step, ImGuiDataType_U64, field, flags, field._format ); break;
2578 case PushConstantSize::DWORD: ret = Util::inputOrSlider<vec4<U32>, U32, 4>( _parent, isSlider, name, step, ImGuiDataType_U32, field, flags, field._format ); break;
2579 case PushConstantSize::WORD: ret = Util::inputOrSlider<vec4<U16>, U16, 4>( _parent, isSlider, name, step, ImGuiDataType_U16, field, flags, field._format ); break;
2580 case PushConstantSize::BYTE: ret = Util::inputOrSlider<vec4<U8>, U8, 4>( _parent, isSlider, name, step, ImGuiDataType_U8, field, flags, field._format ); break;
2582 }
2583 }break;
2585 {
2586 ret = Util::inputOrSlider<vec2<F32>, F32, 2>( _parent, isSlider, name, step, ImGuiDataType_Float, field, flags, field._format );
2587 }break;
2589 {
2590 ret = Util::inputOrSlider<vec3<F32>, F32, 3>( _parent, isSlider, name, step, ImGuiDataType_Float, field, flags, field._format );
2591 }break;
2593 {
2594 ret = Util::inputOrSlider<vec4<F32>, F32, 4>( _parent, isSlider, name, step, ImGuiDataType_Float, field, flags, field._format );
2595 }break;
2597 {
2598 ret = Util::inputOrSlider<vec2<D64>, D64, 2>( _parent, isSlider, name, step, ImGuiDataType_Double, field, flags, field._format );
2599 }break;
2601 {
2602 ret = Util::inputOrSlider<vec3<D64>, D64, 3>( _parent, isSlider, name, step, ImGuiDataType_Double, field, flags, field._format );
2603 }break;
2605 {
2606 ret = Util::inputOrSlider<vec4<D64>, D64, 4>( _parent, isSlider, name, step, ImGuiDataType_Double, field, flags, field._format );
2607 }break;
2609 {
2610 switch ( field._basicTypeSize )
2611 {
2612 case PushConstantSize::QWORD: ret = Util::inputMatrix<mat2<I64>, 2>( _parent, name, step, ImGuiDataType_S64, field, flags, field._format ); break;
2613 case PushConstantSize::DWORD: ret = Util::inputMatrix<mat2<I32>, 2>( _parent, name, step, ImGuiDataType_S32, field, flags, field._format ); break;
2614 case PushConstantSize::WORD: ret = Util::inputMatrix<mat2<I16>, 2>( _parent, name, step, ImGuiDataType_S16, field, flags, field._format ); break;
2615 case PushConstantSize::BYTE: ret = Util::inputMatrix<mat2<I8>, 2>( _parent, name, step, ImGuiDataType_S8, field, flags, field._format ); break;
2617 }
2618 }break;
2620 {
2621 switch ( field._basicTypeSize )
2622 {
2623 case PushConstantSize::QWORD: ret = Util::inputMatrix<mat3<I64>, 3>( _parent, name, step, ImGuiDataType_S64, field, flags, field._format ); break;
2624 case PushConstantSize::DWORD: ret = Util::inputMatrix<mat3<I32>, 3>( _parent, name, step, ImGuiDataType_S32, field, flags, field._format ); break;
2625 case PushConstantSize::WORD: ret = Util::inputMatrix<mat3<I16>, 3>( _parent, name, step, ImGuiDataType_S16, field, flags, field._format ); break;
2626 case PushConstantSize::BYTE: ret = Util::inputMatrix<mat3<I8>, 3>( _parent, name, step, ImGuiDataType_S8, field, flags, field._format ); break;
2628 }
2629 ImGui::Separator();
2630 }break;
2632 {
2633 switch ( field._basicTypeSize )
2634 {
2635 case PushConstantSize::QWORD: ret = Util::inputMatrix<mat4<I64>, 4>( _parent, name, step, ImGuiDataType_S64, field, flags, field._format ); break;
2636 case PushConstantSize::DWORD: ret = Util::inputMatrix<mat4<I32>, 4>( _parent, name, step, ImGuiDataType_S32, field, flags, field._format ); break;
2637 case PushConstantSize::WORD: ret = Util::inputMatrix<mat4<I16>, 4>( _parent, name, step, ImGuiDataType_S16, field, flags, field._format ); break;
2638 case PushConstantSize::BYTE: ret = Util::inputMatrix<mat4<I8>, 4>( _parent, name, step, ImGuiDataType_S8, field, flags, field._format ); break;
2640 }
2641 ImGui::Separator();
2642 }break;
2644 {
2645 switch ( field._basicTypeSize )
2646 {
2647 case PushConstantSize::QWORD: ret = Util::inputMatrix<mat2<U64>, 2>( _parent, name, step, ImGuiDataType_U64, field, flags, field._format ); break;
2648 case PushConstantSize::DWORD: ret = Util::inputMatrix<mat2<U32>, 2>( _parent, name, step, ImGuiDataType_U32, field, flags, field._format ); break;
2649 case PushConstantSize::WORD: ret = Util::inputMatrix<mat2<U16>, 2>( _parent, name, step, ImGuiDataType_U16, field, flags, field._format ); break;
2650 case PushConstantSize::BYTE: ret = Util::inputMatrix<mat2<U8>, 2>( _parent, name, step, ImGuiDataType_U8, field, flags, field._format ); break;
2652 }
2653 }break;
2655 {
2656 switch ( field._basicTypeSize )
2657 {
2658 case PushConstantSize::QWORD: ret = Util::inputMatrix<mat3<U64>, 3>( _parent, name, step, ImGuiDataType_U64, field, flags, field._format ); break;
2659 case PushConstantSize::DWORD: ret = Util::inputMatrix<mat3<U32>, 3>( _parent, name, step, ImGuiDataType_U32, field, flags, field._format ); break;
2660 case PushConstantSize::WORD: ret = Util::inputMatrix<mat3<U16>, 3>( _parent, name, step, ImGuiDataType_U16, field, flags, field._format ); break;
2661 case PushConstantSize::BYTE: ret = Util::inputMatrix<mat3<U8>, 3>( _parent, name, step, ImGuiDataType_U8, field, flags, field._format ); break;
2663 }
2664 }break;
2666 {
2667 switch ( field._basicTypeSize )
2668 {
2669 case PushConstantSize::QWORD: ret = Util::inputMatrix<mat4<U64>, 4>( _parent, name, step, ImGuiDataType_U64, field, flags, field._format ); break;
2670 case PushConstantSize::DWORD: ret = Util::inputMatrix<mat4<U32>, 4>( _parent, name, step, ImGuiDataType_U32, field, flags, field._format ); break;
2671 case PushConstantSize::WORD: ret = Util::inputMatrix<mat4<U16>, 4>( _parent, name, step, ImGuiDataType_U16, field, flags, field._format ); break;
2672 case PushConstantSize::BYTE: ret = Util::inputMatrix<mat4<U8>, 4>( _parent, name, step, ImGuiDataType_U8, field, flags, field._format ); break;
2674 }
2675 }break;
2677 {
2678 ret = Util::inputMatrix<mat2<F32>, 2>( _parent, name, step, ImGuiDataType_Float, field, flags, field._format );
2679 }break;
2681 {
2682 ret = Util::inputMatrix<mat3<F32>, 3>( _parent, name, step, ImGuiDataType_Float, field, flags, field._format );
2683 }break;
2685 {
2686 ret = Util::inputMatrix<mat4<F32>, 4>( _parent, name, step, ImGuiDataType_Float, field, flags, field._format );
2687 }break;
2689 {
2690 ret = Util::inputMatrix<mat2<D64>, 2>( _parent, name, step, ImGuiDataType_Double, field, flags, field._format );
2691 }break;
2693 {
2694 ret = Util::inputMatrix<mat3<D64>, 3>( _parent, name, step, ImGuiDataType_Double, field, flags, field._format );
2695 }break;
2697 {
2698 ret = Util::inputMatrix<mat4<D64>, 4>( _parent, name, step, ImGuiDataType_Double, field, flags, field._format );
2699 }break;
2701 {
2702 ret = Util::colourInput3( _parent, field );
2703 }break;
2705 {
2706 ret = Util::colourInput4( _parent, field );
2707 }break;
2709 {
2710 ImGui::Text( name );
2711 }break;
2712 }
2714 ImGui::PopID();
2715
2716 return ret;
2717 }
2718
2720 {
2721 const Selections& nodes = selections();
2722 if ( nodes._selectionCount == 0 )
2723 {
2724 return DockedWindow::name();
2725 }
2726
2727 if ( nodes._selectionCount == 1 )
2728 {
2729 return node( nodes._selections[0] )->name().c_str();
2730 }
2731
2732 return Util::StringFormat( "{}, {}, ...", node( nodes._selections[0] )->name().c_str(), node( nodes._selections[1] )->name().c_str() );
2733 }
2734} //namespace Divide
2735
2736#ifdef _MSC_VER
2737#pragma warning(pop)
2738#endif
#define MOV(...)
#define DIVIDE_ASSERT(...)
#define DIVIDE_UNEXPECTED_CALL()
#define PROFILE_SCOPE_AUTO(CATEGORY)
Definition: Profiler.h:87
static F32 * max(BoundingBox &bb) noexcept
Definition: BoundingBox.h:140
static F32 * min(BoundingBox &bb) noexcept
Definition: BoundingBox.h:137
static F32 & radius(BoundingSphere &bs) noexcept
static F32 * center(BoundingSphere &bs) noexcept
static void onChanged(const EditorComponent &comp, const EditorComponentField &field)
static vector< EditorComponentField > & fields(EditorComponent &comp) noexcept
static bool modalTextureView(const Editor &editor, const std::string_view modalName, Handle< Texture > tex, const vec2< F32 > dimensions, const bool preserveAspect, const bool useModal)
Definition: Editor.h:676
static const ResourcePath & externalTextEditorPath(const Editor &editor) noexcept
Definition: Editor.h:716
static void showStatusMessage(const Editor &editor, const string &message, const F32 durationMS, const bool error)
Definition: Editor.h:711
static void inspectMemory(Editor &editor, const std::pair< bufferPtr, size_t > data) noexcept
Definition: Editor.h:671
static bool addComponent(const Editor &editor, const Selections &selections, const ComponentType newComponentType)
Definition: Editor.h:696
static bool removeComponent(const Editor &editor, SceneGraphNode *selection, const ComponentType newComponentType)
Definition: Editor.h:701
static void registerUnsavedSceneChanges(Editor &editor) noexcept
Definition: Editor.h:656
static void saveNode(const Editor &editor, const SceneGraphNode *targetNode)
Definition: Editor.h:520
static Camera * getSelectedCamera(const Editor &editor) noexcept
Definition: Editor.h:510
static void lockSolutionExplorer(Editor &editor, const bool state) noexcept
Definition: Editor.h:515
static vector< EditorComponent * > & editorComponents(SceneGraphNode *node) noexcept
void setMin(const vec3< F32 > &min) noexcept
vec3< F32 > getCenter() const noexcept
void setMax(const vec3< F32 > &max) noexcept
vec3< F32 > getHalfExtent() const noexcept
void setRadius(F32 radius) noexcept
void setCenter(const vec3< F32 > &center) noexcept
const mat4< F32 > & projectionMatrix() const noexcept
Returns the most recent/up-to-date projection matrix.
Definition: Camera.inl:168
const mat4< F32 > & setProjection(vec2< F32 > zPlanes)
Definition: Camera.cpp:516
const mat4< F32 > & viewMatrix() const noexcept
Returns the most recent/up-to-date view matrix.
Definition: Camera.inl:157
const Frustum & getFrustum() const noexcept
Returns the most recent/up-to-date frustum.
Definition: Camera.inl:202
Angle::DEGREES< F32 > getHorizontalFoV() const noexcept
Returns the horizontal field of view, calculated from the vertical FoV and aspect ratio.
Definition: Camera.cpp:586
const CameraSnapshot & snapshot() const noexcept
Returns the internal camera snapshot data (eye, orientation, etc)
Definition: Camera.inl:43
void setAspectRatio(F32 ratio) noexcept
Definition: Camera.cpp:568
static constexpr F32 MAX_CAMERA_MOVE_SPEED
Definition: Camera.h:93
void setHorizontalFoV(Angle::DEGREES< F32 > horizontalFoV) noexcept
Definition: Camera.cpp:580
void setEuler(const vec3< Angle::DEGREES< F32 > > &euler) noexcept
Set the camera's rotation to match the specified euler angles.
Definition: Camera.inl:147
void setEye(const F32 x, const F32 y, const F32 z) noexcept
Sets the camera's eye position.
Definition: Camera.inl:100
static constexpr F32 MAX_CAMERA_TURN_SPEED
Definition: Camera.h:94
const Descriptor & descriptor() const noexcept
Definition: DockedWindow.h:69
const char * getIconForNode(const SceneGraphNode *sgn) noexcept
virtual string name() const
Definition: DockedWindow.h:66
vector< EditorComponentField > & fields() noexcept
void registerUndoEntry(const UndoEntry< T > &entry)
Definition: Editor.inl:62
void debugDrawFrustum(const I64 ID, IM::FrustumDescriptor descriptor) noexcept
Definition: GFXDevice.cpp:2997
FORCE_INLINE I64 getGUID() const noexcept
Definition: GUIDWrapper.h:51
void setRotationEuler(const vec3< F32 > &euler)
Set the euler rotation in degrees.
A light object placed in the scene at a certain position.
Definition: Light.h:58
const LightType & getLightType() const noexcept
Get the light type. (see LightType enum)
Definition: Light.inl:46
U16 getShadowArrayOffset() const noexcept
Definition: Light.inl:79
void setRenderStateBlock(const RenderStateBlock &renderStateBlock, RenderStage stage, RenderPassType pass, RenderStagePass::VariantType variant=RenderStagePass::VariantType::COUNT)
Definition: Material.cpp:883
FColour4 getBaseColour(bool &hasTextureOverride, Handle< Texture > &textureOut) const noexcept
Definition: Material.cpp:1010
Handle< ShaderProgram > computeAndGetProgramHandle(RenderStagePass renderStagePass)
Definition: Material.cpp:516
Handle< Texture > getTexture(TextureSlot textureUsage) const
Definition: Material.inl:68
static constexpr F32 MAX_SHININESS
Definition: Material.h:129
F32 getRoughness(bool &hasTextureOverride, Handle< Texture > &textureOut) const noexcept
Definition: Material.cpp:1064
const TextureInfo & getTextureInfo(TextureSlot usage) const
Definition: Material.inl:117
F32 getMetallic(bool &hasTextureOverride, Handle< Texture > &textureOut) const noexcept
Definition: Material.cpp:1053
FColour3 getEmissive(bool &hasTextureOverride, Handle< Texture > &textureOut) const noexcept
Definition: Material.cpp:1022
FColour3 getAmbient(bool &hasTextureOverride, Handle< Texture > &textureOut) const noexcept
Definition: Material.cpp:1034
void lockInstancesForRead() const noexcept
Definition: Material.inl:37
void setTextureOperation(TextureSlot textureUsageSlot, TextureOperation op)
Definition: Material.cpp:425
const RenderStateBlock & getOrCreateRenderStateBlock(RenderStagePass renderStagePass)
Definition: Material.cpp:940
bool isRefractive() const noexcept
Definition: Material.inl:86
FColour3 getSpecular(bool &hasTextureOverride, Handle< Texture > &textureOut) const noexcept
Definition: Material.cpp:1042
const vector< Handle< Material > > & getInstancesLocked() const noexcept
Definition: Material.inl:57
F32 getOcclusion(bool &hasTextureOverride, Handle< Texture > &textureOut) const noexcept
Definition: Material.cpp:1075
void unlockInstancesForRead() const noexcept
Definition: Material.inl:42
std::array< vec3< F32 >, 3 > OBBAxis
Definition: OBB.h:51
PlatformContext & context() noexcept
Kernel & kernel() noexcept
Editor & editor() noexcept
GFXDevice & gfx() noexcept
bool processField(EditorComponentField &field)
void drawInternal() override
void backgroundUpdateInternal() override
Handle< Texture > _previewTexture
const Selections & selections() const
bool processBasicField(EditorComponentField &field)
bool printComponent(SceneGraphNode *sgnNode, EditorComponent *comp, F32 xOffset, F32 smallButtonWidth)
bool processMaterial(Material *material, bool readOnly, bool hex)
string name() const override
SceneGraphNode * node(I64 guid) const
PropertyWindow(Editor &parent, PlatformContext &context, const Descriptor &descriptor)
bool processTransform(TransformComponent *transform, bool readOnly, bool hex)
bool drawCamera(Camera *cam)
struct Divide::PropertyWindow::LockedComponent _lockedComponent
void onRemoveComponent(const EditorComponent &comp) override
vec3< Angle::RADIANS< T > > getEuler() const noexcept
Definition: Quaternion.inl:478
void clearFlag(Flags flag, bool recursive=true)
Clearing a flag might propagate to child nodes (e.g. selection).
void setFlag(Flags flag, bool recursive=true)
General purpose flag management. Certain flags propagate to children (e.g. selection)!
FORCE_INLINE T * get() const
Returns a pointer to a specific component. Returns null if the SGN does not have the component reques...
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!
const Selections & getCurrentSelection(const PlayerIndex index=0) const
Definition: Scene.cpp:2100
const ShaderProgramDescriptor & descriptor() const noexcept
static vector< Camera * > & shadowCameras(const ShadowType type) noexcept
Definition: ShadowMap.h:122
void setTransform(const TransformValues &values)
void setPosition(const vec3< F32 > &position) override
Component <-> Transform interface.
void setScale(const vec3< F32 > &amount) override
Set the local X,Y and Z scale factors.
TransformValues getLocalValues() const
vec3< T > getForwardDirection() const noexcept
Returns normalized(getForwardVec())
constexpr DEGREES< T > to_DEGREES(RADIANS< T > angle) noexcept
Definition: MathHelper.inl:380
constexpr Optick::Category::Type GUI
Definition: Profiler.h:64
const char * StencilOperationToString(StencilOperation op) noexcept
const char * RenderPassTypeToString(const RenderPassType pass) noexcept
Definition: GFXDevice.cpp:72
const char * CameraModeToString(const Camera::Mode mode) noexcept
Definition: Camera.cpp:34
const char * CullModeToString(CullMode mode) noexcept
const char * ComparisonFunctionToString(ComparisonFunction func) noexcept
const char * FillModeToString(FillMode mode) noexcept
const char * ShadingModeToString(ShadingMode shadingMode) noexcept
Definition: Material.cpp:82
const char * ComponentTypeToString(const ComponentType compType) noexcept
const char * TextureOperationToString(TextureOperation textureOp) noexcept
Definition: Material.cpp:100
const char * RenderStageToString(const RenderStage stage) noexcept
Definition: GFXDevice.cpp:54
Str StringFormat(const char *fmt, Args &&...args)
string to_string(GET_PASS_TYPE< T > value)
const char * PushedToolTip()
Definition: Utils.cpp:331
void PushTooltip(const char *tooltip)
Definition: Utils.cpp:316
void PushNarrowLabelWidth()
Definition: Utils.cpp:300
void PopNarrowLabelWidth()
Definition: Utils.cpp:308
void OpenCenteredPopup(const char *name, ImGui::ImGuiPopupFlags popup_flags=0)
UColour4 ToByteColour(const FColour4 &floatColour) noexcept
Definition: MathHelper.cpp:256
bool colourInput4(Editor &parent, EditorComponentField &field)
Definition: Utils.cpp:374
void PopTooltip()
Definition: Utils.cpp:321
ImGuiInputTextFlags GetDefaultFlagsForField(const EditorComponentField &field)
Definition: Utils.cpp:437
bool colourInput3(Editor &parent, EditorComponentField &field)
Definition: Utils.cpp:390
bool IsPushedTooltip()
Definition: Utils.cpp:326
constexpr const char * FieldLabels[]
Definition: Utils.h:69
void ApplyToMaterials(const Material &baseMaterial, Material *instanceRoot, Pred &&predicate)
hashMap< U64, std::tuple< Frustum, FColour3, bool > > g_debugFrustums
bool IsRequiredComponentType(SceneGraphNode *selection, const ComponentType componentType)
void ApplyAllButton(I32 &id, const bool readOnly, const Material &material, Pred &&predicate)
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
FileError openFile(const std::string_view cmd, const ResourcePath &filePath, const std::string_view fileName)
constexpr U32 to_U32(const T value)
void PushReadOnly(const bool fade)
Definition: Editor.cpp:2961
int32_t I32
uint8_t U8
int16_t I16
StencilOperation
Valid front and back stencil test actions.
@ COUNT
Place all properties above this.
size_t GetHash(const PropertyDescriptor< T > &descriptor) noexcept
Definition: Resource.inl:40
DirectionalLightComponent(SceneGraphNode *sgn, PlatformContext &context)
constexpr F32 EPSILON_F32
TextureOperation
How should each texture be added.
eastl::vector< Type > vector
Definition: Vector.h:42
hashAlg::unordered_map< K, V, HashFun, Predicate > hashMap
Definition: HashMap.h:55
@ COUNT
Place all properties above this.
void PopReadOnly()
Definition: Editor.cpp:2971
Project & parent
Definition: DefaultScene.h:41
uint16_t U16
constexpr U64 _ID(const char *const str, const U64 value=val_64_const) noexcept
FillMode
Defines all available fill modes for primitives.
@ COUNT
Place all properties above this.
constexpr U8 to_U8(const T value)
double D64
constexpr I32 to_I32(const T value)
CullMode
Specifies whether front- or back-facing facets are candidates for culling.
@ COUNT
Place all properties above this.
int64_t I64
FORCE_INLINE T * Get(const Handle< T > handle)
uint32_t U32
TextureSlot
Definition: Material.h:76
uint64_t U64
@ DROPDOWN_TYPE
Only U8 types supported!
constexpr auto to_base(const Type value) -> Type
bool ToggleButton(const char *str_id, bool *v)
Angle::DEGREES< F32 > _fov
void set(const T &dataIn)
DELEGATE_STD< void, const void * > _dataSetter
vec2< F32 > _range
Used by slider_type as a min / max range or dropdown as selected_index / count.
F32 _step
0.0f == no +- buttons
bool isMatrix() const noexcept
const char *const * _labels
EditorComponentFieldType _type
const char * getDisplayName(const U8 index) const
PushConstantType _type
Definition: UndoManager.h:42
string _name
Definition: UndoManager.h:43
TextureOperation _operation
Definition: Material.h:228
RenderPassType _passType
StringReturnType< N > string() const noexcept
Definition: ResourcePath.h:64
bool empty() const noexcept
std::array< I64, MAX_SELECTIONS > _selections
Definition: Scene.h:100
vec3< F32 > _translation
The object's position in the world as a 3 component vector.
Quaternion< F32 > _orientation
Definition: UndoManager.h:51
T _oldVal
Definition: UndoManager.h:52
T _newVal
Definition: UndoManager.h:53
DELEGATE_STD< void, const T & > _dataSetter
Definition: UndoManager.h:54