From e83752dc601214454540500769ff3bf99d55729e Mon Sep 17 00:00:00 2001 From: ccpnobody <55136070+ccpnobody@users.noreply.github.com> Date: Wed, 3 Jun 2026 13:39:16 +0000 Subject: [PATCH 01/21] Modifying the appState Making a meshState object that contains the state of each mesh. Moving away from the equally sized collections of states on the model --- src/viewer/appState.cpp | 22 +----- src/viewer/appState.h | 53 +++++++++++--- src/viewer/appState_template_impl.h | 46 ++++++++++++- .../rendering/renderable/geometryprepass.cpp | 17 ----- src/viewer/rendering/renderable/mesh.cpp | 69 +++++++++---------- src/viewer/rendering/renderable/mesh.h | 7 +- src/viewer/rendering/uiRenderer.cpp | 69 ++++++++++--------- src/viewer/rendering/uiRenderer.h | 2 +- 8 files changed, 163 insertions(+), 122 deletions(-) diff --git a/src/viewer/appState.cpp b/src/viewer/appState.cpp index ad403e1..30b9c41 100644 --- a/src/viewer/appState.cpp +++ b/src/viewer/appState.cpp @@ -20,27 +20,7 @@ void AppState::CallStateCallbacks() cmfPath.CallCallbacks( *this ); exitRequested.CallCallbacks( *this ); - modelState.selectedLod.CallCallbacks( *this ); - modelState.activeLod.CallCallbacks( *this ); - modelState.meshScreenSize.CallCallbacks( *this ); - modelState.visualizationShader.CallCallbacks( *this ); - modelState.availableShaders.CallCallbacks( *this ); - modelState.polygonMode.CallCallbacks( *this ); - modelState.meshVisibilityStates.CallCallbacks( *this ); - modelState.morphTargetWeight.CallCallbacks( *this ); - modelState.morphTargetEnabled.CallCallbacks( *this ); - modelState.meshWireframeOverlay.CallCallbacks( *this ); - modelState.audioOcclusionMesh.CallCallbacks( *this ); - modelState.meshBoundingBox.CallCallbacks( *this ); - modelState.modelBoundingBox.CallCallbacks( *this ); - modelState.currentAnimation.CallCallbacks( *this ); - modelState.currentAnimationTime.CallCallbacks( *this ); - modelState.availableShaders.CallCallbacks( *this ); - modelState.boneDebug.CallCallbacks( *this ); - modelState.jointDebug.CallCallbacks( *this ); - modelState.jointAxisDebug.CallCallbacks( *this ); - modelState.activeAnimationOwner.CallCallbacks( *this ); - modelState.selectedBones.CallCallbacks( *this ); + modelState.CallCallbacks( *this ); } void AppState::ResetModelState() diff --git a/src/viewer/appState.h b/src/viewer/appState.h index fdf05aa..86f30e6 100644 --- a/src/viewer/appState.h +++ b/src/viewer/appState.h @@ -29,6 +29,7 @@ class State public: State( T initialValue ); const T GetValue() const; + T& GetValue(); void SetValue( T newValue ); void ForceSetValue( T newValue ); void SetValueNoCallback( T newValue ); @@ -55,6 +56,8 @@ class StateCollection StateCollection( T initialValue ); size_t AddState(); size_t AddState( T initialValue ); + size_t AddState( std::function configurator ); + size_t AddState( T initialValue, std::function configurator ); void Clear(); void CallCallbacks( AppState& appState ); @@ -98,14 +101,34 @@ enum class CameraTrigger CAMERA_TRIGGER_LOOK_BACK, }; +struct MeshState +{ + State display{ true }; + StateCollection> morphs{ { 0.0f, true } }; + State wireframeOverlay{ false }; + State audioOcclusionMesh{ false }; + State renderBoundingBox{ false }; + State activeLod{ 0 }; + State meshScreenSize{ 0.0f }; + + void CallCallbacks( AppState& appState ) + { + display.CallCallbacks( appState ); + morphs.CallCallbacks( appState ); + wireframeOverlay.CallCallbacks( appState ); + audioOcclusionMesh.CallCallbacks( appState ); + renderBoundingBox.CallCallbacks( appState ); + activeLod.CallCallbacks( appState ); + meshScreenSize.CallCallbacks( appState ); + } +}; + struct ModelState { /// The lod selected by the user (-1 for auto) State selectedLod{ -1 }; - /// The lod of the mesh that is currently active - StateCollection activeLod{ 0 }; - StateCollection meshScreenSize{ 0.0f }; + StateCollection meshes{ {} }; State visualizationShader{ "" }; State> availableShaders{ {} }; @@ -114,12 +137,6 @@ struct ModelState State currentAnimation{ "" }; State currentAnimationTime{ 0.0f }; - StateCollection meshVisibilityStates{ true }; - StateCollection morphTargetWeight{ 0.0 }; - StateCollection morphTargetEnabled{ true }; - StateCollection meshWireframeOverlay{ false }; - StateCollection audioOcclusionMesh{ false }; - StateCollection meshBoundingBox{ false }; State boneDebug{ false }; State jointDebug{ false }; State jointAxisDebug{ false }; @@ -127,6 +144,24 @@ struct ModelState State> activeAnimationOwner{ nullptr }; StateCollection> animationOverrides{ nullptr }; StateCollection selectedBones{ 0xFF }; + + void CallCallbacks( AppState& appState ) + { + selectedLod.CallCallbacks( appState ); + meshes.CallCallbacks( appState ); + visualizationShader.CallCallbacks( appState ); + availableShaders.CallCallbacks( appState ); + polygonMode.CallCallbacks( appState ); + currentAnimation.CallCallbacks( appState ); + currentAnimationTime.CallCallbacks( appState ); + boneDebug.CallCallbacks( appState ); + jointDebug.CallCallbacks( appState ); + jointAxisDebug.CallCallbacks( appState ); + modelBoundingBox.CallCallbacks( appState ); + activeAnimationOwner.CallCallbacks( appState ); + animationOverrides.CallCallbacks( appState ); + selectedBones.CallCallbacks( appState ); + } }; struct AppState diff --git a/src/viewer/appState_template_impl.h b/src/viewer/appState_template_impl.h index 96cc6b9..2cb6a9e 100644 --- a/src/viewer/appState_template_impl.h +++ b/src/viewer/appState_template_impl.h @@ -44,9 +44,29 @@ void State::SetValueNoCallback( T newValue ) m_value = newValue; } +template +T& State::GetValue() +{ + return m_value; +} + +namespace detail +{ + template + struct has_call_callbacks : std::false_type {}; + + template + struct has_call_callbacks().CallCallbacks( std::declval() ) )>> : std::true_type {}; +} + template void State::CallCallbacks( AppState& appState ) { + if constexpr( detail::has_call_callbacks::value ) + { + m_value.CallCallbacks( appState ); + } + if( m_fireCallbacks ) { for( auto& callback : m_callbacks ) @@ -90,6 +110,26 @@ size_t StateCollection::AddState( T initialValue ) return m_states.size() - 1; } +template +size_t StateCollection::AddState( std::function configurator ) +{ + State state( m_initialValue ); + m_states.push_back( state ); + configurator( m_states.back().GetValue() ); + m_fireCallbacks = true; + return m_states.size() - 1; +} + +template +size_t StateCollection::AddState( T initialValue, std::function configurator ) +{ + State state( initialValue ); + m_states.push_back( state ); + configurator( m_states.back().GetValue() ); + m_fireCallbacks = true; + return m_states.size() - 1; +} + template void StateCollection::RemoveAt( size_t index ) { @@ -110,12 +150,16 @@ void StateCollection::RegisterCallback( std::function, A template void StateCollection::CallCallbacks( AppState& appState ) { + for( auto& state : m_states ) + { + state.CallCallbacks( appState ); + } + std::vector values; values.reserve( m_states.size() ); for( auto& state : m_states ) { values.push_back( state.GetValue() ); - state.CallCallbacks( appState ); } if( m_fireCallbacks ) diff --git a/src/viewer/rendering/renderable/geometryprepass.cpp b/src/viewer/rendering/renderable/geometryprepass.cpp index 330e672..0e5a571 100644 --- a/src/viewer/rendering/renderable/geometryprepass.cpp +++ b/src/viewer/rendering/renderable/geometryprepass.cpp @@ -28,23 +28,6 @@ GeometryPrePass::~GeometryPrePass() void GeometryPrePass::Initialize( AppState& appState ) { - auto firstMorphState = appState.modelState.morphTargetEnabled.size(); - - // go through the morph targets so we can register to the morph weight and display flags - for( size_t i = 0; i < m_cmfMesh.morphTargets.targets.size(); i++ ) - { - auto index = appState.modelState.morphTargetEnabled.AddState(); - appState.modelState.morphTargetWeight.AddState(); - - appState.modelState.morphTargetWeight[index].RegisterCallback( [this, index, firstMorphState]( float weight, AppState& ) { - SetMorphWeight( index - firstMorphState, weight ); - } ); - appState.modelState.morphTargetEnabled[index].RegisterCallback( [this, index, firstMorphState]( bool enabled, AppState& appState ) { - float weight = enabled ? appState.modelState.morphTargetWeight[index].GetValue() : 0.0f; - SetMorphWeight( index - firstMorphState, weight ); - } ); - } - m_weights.resize( m_cmfMesh.morphTargets.targets.size(), 0.0f ); const auto boneElement = std::find_if( m_cmfMesh.decl.begin(), m_cmfMesh.decl.end(), []( const cmf::VertexElement& elem ) { return elem.usage == cmf::Usage::BoneIndices; diff --git a/src/viewer/rendering/renderable/mesh.cpp b/src/viewer/rendering/renderable/mesh.cpp index 53ab48b..88076d1 100644 --- a/src/viewer/rendering/renderable/mesh.cpp +++ b/src/viewer/rendering/renderable/mesh.cpp @@ -42,6 +42,8 @@ MeshRenderable::MeshRenderable( std::shared_ptr data, const cmf::Mes void MeshRenderable::Initialize( AppState& appState ) { + m_prepass.Initialize( appState ); + appState.modelState.polygonMode.RegisterCallback( [this]( VkPolygonMode mode, AppState& appState ) { SetRenderingMode( appState.modelState.visualizationShader.GetValue(), mode ); } ); @@ -50,45 +52,39 @@ void MeshRenderable::Initialize( AppState& appState ) SetRenderingMode( shaderName, appState.modelState.polygonMode.GetValue() ); } ); - // Register mesh visibility state - size_t stateIndex = appState.modelState.meshVisibilityStates.AddState(); - appState.modelState.meshVisibilityStates[stateIndex].RegisterCallback( [this]( bool visible, AppState& appState ) { - m_display = visible; - } ); - - stateIndex = appState.modelState.meshWireframeOverlay.AddState(); - appState.modelState.meshWireframeOverlay[stateIndex].RegisterCallback( [this]( bool enabled, AppState& appState ) { - m_wireframe = enabled; - } ); - - stateIndex = appState.modelState.audioOcclusionMesh.AddState(); - appState.modelState.audioOcclusionMesh[stateIndex].RegisterCallback( [this]( bool enabled, AppState& appState ) { - m_audioOcclusion = enabled; - } ); - - stateIndex = appState.modelState.meshBoundingBox.AddState(); - appState.modelState.meshBoundingBox[stateIndex].RegisterCallback( [this]( bool enabled, AppState& appState ) { - m_showBoundingBox = enabled; - } ); - - m_meshScreenSizeStateIndex = appState.modelState.meshScreenSize.AddState(); - m_activeLodStateIndex = appState.modelState.activeLod.AddState(); - - appState.modelState.activeLod[m_activeLodStateIndex].RegisterCallback( [this]( uint32_t lodIndex, AppState& appState ) { - SetLod( lodIndex ); + m_meshIndex = appState.modelState.meshes.AddState( [this]( MeshState& meshState ) { + meshState.display.RegisterCallback( [this]( bool visible, AppState& ) { + m_display = visible; + } ); + meshState.wireframeOverlay.RegisterCallback( [this]( bool enabled, AppState& ) { + m_wireframe = enabled; + } ); + meshState.audioOcclusionMesh.RegisterCallback( [this]( bool enabled, AppState& ) { + m_audioOcclusion = enabled; + } ); + meshState.renderBoundingBox.RegisterCallback( [this]( bool enabled, AppState& ) { + m_showBoundingBox = enabled; + } ); + meshState.activeLod.RegisterCallback( [this]( uint32_t lodIndex, AppState& appState ) { + SetLod( lodIndex ); + } ); + SetLod( meshState.activeLod.GetValue() ); + for( size_t i = 0; i < m_cmfMesh.morphTargets.targets.size(); ++i ) + { + meshState.morphs.AddState(); + meshState.morphs[i].RegisterCallback( [this, i]( std::pair morph, AppState& ) { + m_prepass.SetMorphWeight( static_cast( i ), morph.second ? morph.first : 0.0f ); + } ); + } } ); appState.modelState.selectedLod.RegisterCallback( [this]( int32_t lodIndex, AppState& appState ) { if( lodIndex != -1 ) { - appState.modelState.activeLod[m_activeLodStateIndex].SetValue( lodIndex ); + appState.modelState.meshes[m_meshIndex].GetValue().activeLod.SetValue( lodIndex ); } } ); - m_prepass.Initialize( appState ); - - SetLod( appState.modelState.activeLod[m_activeLodStateIndex].GetValue() ); - m_boundingBox.Initialize(); if( !m_cmfMesh.audioOcclusionMesh.vertices.empty() && !m_cmfMesh.audioOcclusionMesh.indices.empty() ) @@ -118,7 +114,8 @@ void MeshRenderable::UpdateMeshCurves( float animationTime, const cmf::Animation // the line above is the one that updates the morph target weight in the prepass, // but we also need to update the app state so that the UI reflects the current weight // if we would do it the other way, then prepass would get the update with one frame delay - appState.modelState.morphTargetWeight[morphIndex].SetValueNoCallback( weight ); + auto& morphState = appState.modelState.meshes[m_meshIndex].GetValue().morphs[morphIndex]; + morphState.SetValueNoCallback( { weight, morphState.GetValue().second } ); } } } @@ -178,7 +175,8 @@ void MeshRenderable::Update( AppState& appState, const Camera& camera ) { // update the lod based on the camera and bounding sphere of the mesh auto sizeOnScreen = camera.GetSizeOnScreen( m_boundingSphere ); - appState.modelState.meshScreenSize[m_meshScreenSizeStateIndex].SetValueNoCallback( sizeOnScreen ); + auto& meshState = appState.modelState.meshes[m_meshIndex].GetValue(); + meshState.meshScreenSize.SetValueNoCallback( sizeOnScreen ); // find the closest lod that has the size on screen greater than the threshold uint32_t lodLevel = 0; @@ -192,10 +190,7 @@ void MeshRenderable::Update( AppState& appState, const Camera& camera ) if( m_currentLod != lodLevel ) { SetLod( lodLevel ); - if( m_activeLodStateIndex < appState.modelState.activeLod.size() ) - { - appState.modelState.activeLod[m_activeLodStateIndex].SetValueNoCallback( lodLevel ); - } + meshState.activeLod.SetValueNoCallback( lodLevel ); } } } diff --git a/src/viewer/rendering/renderable/mesh.h b/src/viewer/rendering/renderable/mesh.h index 6cf2028..a0ec560 100644 --- a/src/viewer/rendering/renderable/mesh.h +++ b/src/viewer/rendering/renderable/mesh.h @@ -49,8 +49,7 @@ class MeshRenderable uint32_t m_stride{ 0 }; uint32_t m_currentLod{ std::numeric_limits::max() }; - size_t m_activeLodStateIndex{ 0 }; - size_t m_meshScreenSizeStateIndex{ 0 }; + size_t m_meshIndex{ 0 }; CcpMath::Sphere m_boundingSphere{}; VkPolygonMode m_polygonMode{ VK_POLYGON_MODE_FILL }; VkPrimitiveTopology m_topology{ VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST }; @@ -58,6 +57,8 @@ class MeshRenderable bool m_display{ true }; bool m_wireframe{ false }; + bool m_audioOcclusion{ false }; + bool m_showBoundingBox{ false }; bool m_initialized{ false }; // effects @@ -75,11 +76,9 @@ class MeshRenderable // debug renderables // bounding box - bool m_showBoundingBox{ false }; PrimitiveRenderable m_boundingBox; Matrix m_boundingBoxTransform{}; // audio occlusion - bool m_audioOcclusion{ false }; PrimitiveRenderable m_audioOcclusionRenderable; }; \ No newline at end of file diff --git a/src/viewer/rendering/uiRenderer.cpp b/src/viewer/rendering/uiRenderer.cpp index 05c5a92..2335d76 100644 --- a/src/viewer/rendering/uiRenderer.cpp +++ b/src/viewer/rendering/uiRenderer.cpp @@ -457,13 +457,13 @@ void UIRenderer::SetupGeneralView( AppState& appState ) ImGui::TableNextColumn(); ImGui::Text( "Wireframe Overlay" ); ImGui::TableNextColumn(); - bool wireframeOverlay = std::all_of( appState.modelState.meshWireframeOverlay.begin(), appState.modelState.meshWireframeOverlay.end(), []( const State& state ) { - return state.GetValue(); + bool wireframeOverlay = std::all_of( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { + return state.GetValue().wireframeOverlay.GetValue(); } ) && - appState.modelState.meshWireframeOverlay.size() > 0; + appState.modelState.meshes.size() > 0; OnChange( ImGui::Checkbox( "##wireframecheckbox", &wireframeOverlay ), [&appState, &wireframeOverlay]() { - std::for_each( appState.modelState.meshWireframeOverlay.begin(), appState.modelState.meshWireframeOverlay.end(), [wireframeOverlay]( State& state ) { - state.SetValue( wireframeOverlay ); + std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [wireframeOverlay]( State& state ) { + state.GetValue().wireframeOverlay.SetValue( wireframeOverlay ); } ); } ); ImGui::TableNextRow(); @@ -475,13 +475,13 @@ void UIRenderer::SetupGeneralView( AppState& appState ) ImGui::TableNextColumn(); ImGui::Text( "Audio Occlusion Mesh" ); ImGui::TableNextColumn(); - bool audioOcclusion = std::all_of( appState.modelState.audioOcclusionMesh.begin(), appState.modelState.audioOcclusionMesh.end(), []( const State& state ) { - return state.GetValue(); + bool audioOcclusion = std::all_of( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { + return state.GetValue().audioOcclusionMesh.GetValue(); } ) && - appState.modelState.audioOcclusionMesh.size() > 0; + appState.modelState.meshes.size() > 0; OnChange( ImGui::Checkbox( "##audioocclusioncheckbox", &audioOcclusion ), [&appState, &audioOcclusion]() { - std::for_each( appState.modelState.audioOcclusionMesh.begin(), appState.modelState.audioOcclusionMesh.end(), [audioOcclusion]( State& state ) { - state.SetValue( audioOcclusion ); + std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [audioOcclusion]( State& state ) { + state.GetValue().audioOcclusionMesh.SetValue( audioOcclusion ); } ); } ); ImGui::TableNextRow(); @@ -557,7 +557,7 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) ImGui::TableNextColumn(); bool display = mesh.display; OnChange( ImGui::Checkbox( "##displaycheckbox", &display ), [&appState, &mesh, &display]() { - appState.modelState.meshVisibilityStates[mesh.meshIndex].SetValue( display ); + appState.modelState.meshes[mesh.meshIndex].GetValue().display.SetValue( display ); } ); ImGui::TableNextRow(); @@ -568,7 +568,7 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) ImGui::TableNextColumn(); bool boundingBox = mesh.boundingBox; OnChange( ImGui::Checkbox( "##boundingboxcheckbox", &boundingBox ), [&appState, &mesh, &boundingBox]() { - appState.modelState.meshBoundingBox[mesh.meshIndex].SetValue( boundingBox ); + appState.modelState.meshes[mesh.meshIndex].GetValue().renderBoundingBox.SetValue( boundingBox ); } ); ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -578,7 +578,7 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) ImGui::TableNextColumn(); bool wireframeOverlay = mesh.wireframeOverlay; OnChange( ImGui::Checkbox( "##wireframeoverlaycheckbox", &wireframeOverlay ), [&appState, &mesh, &wireframeOverlay]() { - appState.modelState.meshWireframeOverlay[mesh.meshIndex].SetValue( wireframeOverlay ); + appState.modelState.meshes[mesh.meshIndex].GetValue().wireframeOverlay.SetValue( wireframeOverlay ); } ); ImGui::TableNextRow(); @@ -589,7 +589,7 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) ImGui::TableNextColumn(); bool audioOcclusion = mesh.audioOcclusionMesh; OnChange( ImGui::Checkbox( "##audioocclusionmeshcheckbox", &audioOcclusion ), [&appState, &mesh, &audioOcclusion]() { - appState.modelState.audioOcclusionMesh[mesh.meshIndex].SetValue( audioOcclusion ); + appState.modelState.meshes[mesh.meshIndex].GetValue().audioOcclusionMesh.SetValue( audioOcclusion ); } ); ImGui::EndDisabled(); @@ -603,7 +603,7 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) uint32_t index = 0; for( const auto& morphTarget : mesh.morphTargets ) { - SetupMorphTarget( morphTarget, appState ); + SetupMorphTarget( morphTarget, mesh.meshIndex, appState ); } } } @@ -612,7 +612,7 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) } } -void UIRenderer::SetupMorphTarget( const MorphTargetUiState& morphTarget, AppState& appState ) +void UIRenderer::SetupMorphTarget( const MorphTargetUiState& morphTarget, size_t meshIndex, AppState& appState ) { ImGui::SeparatorText( morphTarget.name.c_str() ); @@ -628,14 +628,16 @@ void UIRenderer::SetupMorphTarget( const MorphTargetUiState& morphTarget, AppSta ImGui::TableNextColumn(); ImGui::SetNextItemWidth( ImGui::GetContentRegionAvail().x ); float weight = morphTarget.weight; - OnChange( ImGui::SliderFloat( "##slider", &weight, 0.0f, 1.0f, "%.6f" ), [&appState, &morphTarget, &weight]() { - appState.modelState.morphTargetWeight[morphTarget.index].SetValue( weight ); + OnChange( ImGui::SliderFloat( "##slider", &weight, 0.0f, 1.0f, "%.6f" ), [&appState, &morphTarget, &weight, meshIndex]() { + auto& morphState = appState.modelState.meshes[meshIndex].GetValue().morphs[morphTarget.index]; + morphState.SetValue( { weight, morphState.GetValue().second } ); } ); ImGui::TableNextColumn(); bool enabled = morphTarget.enabled; - OnChange( ImGui::Checkbox( "##checkbox", &enabled ), [&appState, &morphTarget, &enabled]() { - appState.modelState.morphTargetEnabled[morphTarget.index].SetValue( enabled ); + OnChange( ImGui::Checkbox( "##checkbox", &enabled ), [&appState, &morphTarget, &enabled, meshIndex]() { + auto& morphState = appState.modelState.meshes[meshIndex].GetValue().morphs[morphTarget.index]; + morphState.SetValue( { morphState.GetValue().first, enabled } ); } ); ImGui::SetItemTooltip( "Toggles the \"%s\" morph target", morphTarget.name.c_str() ); ImGui::EndTable(); @@ -1098,22 +1100,23 @@ void UIRenderer::UpdateUiState( AppState& appState ) for( const auto& mesh : cmfContent->m_cmfData->meshes ) { - if( meshIndex >= appState.modelState.meshVisibilityStates.size() ) + if( meshIndex >= appState.modelState.meshes.size() ) { break; } + const auto& meshAppState = appState.modelState.meshes[meshIndex].GetValue(); MeshUiState meshState{}; meshState.meshIndex = meshIndex; meshState.name = cmf::ToStdString( mesh.name ); - meshState.lod = appState.modelState.activeLod[meshIndex].GetValue(); + meshState.lod = meshAppState.activeLod.GetValue(); meshState.maxLodIndex = static_cast( mesh.lods.size() ) - 1; meshState.boundingSphereRadius = CcpMath::Sphere( mesh.bounds ).radius; - meshState.screenSize = appState.modelState.meshScreenSize[meshIndex].GetValue(); - meshState.display = appState.modelState.meshVisibilityStates[meshIndex].GetValue(); - meshState.wireframeOverlay = appState.modelState.meshWireframeOverlay[meshIndex].GetValue(); - meshState.audioOcclusionMesh = appState.modelState.audioOcclusionMesh[meshIndex].GetValue(); + meshState.screenSize = meshAppState.meshScreenSize.GetValue(); + meshState.display = meshAppState.display.GetValue(); + meshState.wireframeOverlay = meshAppState.wireframeOverlay.GetValue(); + meshState.audioOcclusionMesh = meshAppState.audioOcclusionMesh.GetValue(); meshState.hasAudioOcclusionMesh = !mesh.audioOcclusionMesh.vertices.empty() && !mesh.audioOcclusionMesh.indices.empty(); - meshState.boundingBox = appState.modelState.meshBoundingBox[meshIndex].GetValue(); + meshState.boundingBox = meshAppState.renderBoundingBox.GetValue(); maxLod = std::max( maxLod, mesh.lods.size() ); meshState.vertexCount = 0; @@ -1134,17 +1137,19 @@ void UIRenderer::UpdateUiState( AppState& appState ) m_uiState.modelStates.vertexCount += meshState.vertexCount; m_uiState.modelStates.indexCount += meshState.indexCount; - for( const auto& morphTarget : mesh.morphTargets.targets ) + for( size_t i = 0; i < mesh.morphTargets.targets.size(); ++i ) { - if( morphIndex >= appState.modelState.morphTargetWeight.size() ) + if( i >= meshAppState.morphs.size() ) { break; } + const auto& morphTarget = mesh.morphTargets.targets[i]; + const auto& morphPair = meshAppState.morphs[i].GetValue(); MorphTargetUiState morphTargetState{}; morphTargetState.name = cmf::ToStdString( morphTarget.name ); - morphTargetState.weight = appState.modelState.morphTargetWeight[morphIndex].GetValue(); - morphTargetState.enabled = appState.modelState.morphTargetEnabled[morphIndex].GetValue(); - morphTargetState.index = morphIndex++; + morphTargetState.weight = morphPair.first; + morphTargetState.enabled = morphPair.second; + morphTargetState.index = i; meshState.morphTargets.push_back( morphTargetState ); } diff --git a/src/viewer/rendering/uiRenderer.h b/src/viewer/rendering/uiRenderer.h index 39290cc..79c4441 100644 --- a/src/viewer/rendering/uiRenderer.h +++ b/src/viewer/rendering/uiRenderer.h @@ -142,7 +142,7 @@ class UIRenderer void SetupGeneralView( AppState& appState ); void SetupMeshListView( const ModelUiState& modelState, AppState& appState ); void SetupMeshView( const MeshUiState& mesh, AppState& appState ); - void SetupMorphTarget( const MorphTargetUiState& morphTarget, AppState& appState ); + void SetupMorphTarget( const MorphTargetUiState& morphTarget, size_t meshIndex, AppState& appState ); void SetupSkeletonOwners( const std::vector& skeletonOwners, AppState& appState ); void SetupSkeletons( const std::vector& skeletonStates, AppState& appState ); void SetupPlaybackControls( AppState& appState ); From affdb211fb5ed2f7ade448ef42ff54653463a37f Mon Sep 17 00:00:00 2001 From: ccpnobody <55136070+ccpnobody@users.noreply.github.com> Date: Thu, 4 Jun 2026 13:18:49 +0000 Subject: [PATCH 02/21] Update model.cpp Fixing the defect where the model that was loaded wasn't loaded as the animation owner --- src/viewer/rendering/renderable/model.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/viewer/rendering/renderable/model.cpp b/src/viewer/rendering/renderable/model.cpp index 227c35c..f6bc478 100644 --- a/src/viewer/rendering/renderable/model.cpp +++ b/src/viewer/rendering/renderable/model.cpp @@ -80,6 +80,8 @@ VkResult ModelRenderable::Initialize( AppState& appState ) UpdateAnimation( animationTime, appState ); } ); + appState.modelState.activeAnimationOwner.SetValue( m_cmfContent ); + for( auto& animationState : m_animationStates ) { From f098f5ee3853c98b32062b49e6e6abecb40c9581 Mon Sep 17 00:00:00 2001 From: ccpnobody <55136070+ccpnobody@users.noreply.github.com> Date: Thu, 4 Jun 2026 13:28:08 +0000 Subject: [PATCH 03/21] Adding vertex color visualization Added a shader for vertex colors. Since we can have multiple vertex attributes that can be visualized, I added support in the python script to create one shader for each usage index (but only if there are 2 vertex attributes required by the shader: position and the thing to visualize) I also added default shader selection --- src/viewer/assets/shaders/model_color.frag | 13 ++++++ src/viewer/assets/shaders/model_color.vert | 23 +++++++++++ src/viewer/rendering/sceneRenderer.cpp | 23 ++++++++++- src/viewer/rendering/vulkan/shadercache.cpp | 8 ++-- src/viewer/rendering/vulkan/shadercache.h | 1 + src/viewer/scripts/shaderCacheCreator.py | 46 ++++++++++++++------- 6 files changed, 95 insertions(+), 19 deletions(-) create mode 100644 src/viewer/assets/shaders/model_color.frag create mode 100644 src/viewer/assets/shaders/model_color.vert diff --git a/src/viewer/assets/shaders/model_color.frag b/src/viewer/assets/shaders/model_color.frag new file mode 100644 index 0000000..40d7b35 --- /dev/null +++ b/src/viewer/assets/shaders/model_color.frag @@ -0,0 +1,13 @@ + +#version 450 + +// Inputs +layout( location = 0 ) in vec3 color; + +// Outputs +layout( location = 0 ) out vec4 outFragColor; + +void main() +{ + outFragColor = vec4( vec3( color ), 1.0 ); +} \ No newline at end of file diff --git a/src/viewer/assets/shaders/model_color.vert b/src/viewer/assets/shaders/model_color.vert new file mode 100644 index 0000000..4128f94 --- /dev/null +++ b/src/viewer/assets/shaders/model_color.vert @@ -0,0 +1,23 @@ +#version 450 + +// Constants +layout( binding = 0 ) uniform PerFrame +{ + mat4 projectionMatrix; + mat4 viewMatrix; +} perframe; + + +// Inputs +layout( location = 0 ) in vec3 inPosition; +layout( location = 1 ) in vec3 inColor; + +// Outputs; +layout( location = 0 ) out vec3 color; + + +void main() +{ + color = inColor; + gl_Position = perframe.projectionMatrix * perframe.viewMatrix *vec4( inPosition, 1.0 ); +} \ No newline at end of file diff --git a/src/viewer/rendering/sceneRenderer.cpp b/src/viewer/rendering/sceneRenderer.cpp index 25d3240..6d91d6f 100644 --- a/src/viewer/rendering/sceneRenderer.cpp +++ b/src/viewer/rendering/sceneRenderer.cpp @@ -4,6 +4,14 @@ #include "vulkan/vulkanerrors.h" #include "vulkan/vulkanenums.h" +const std::vector DEFAULT_SHADER_ORDER +{ + "Normal", + "Packed Normal", + "Packed Normal Legacy", + "Face Normal" +}; + SceneRenderer::SceneRenderer( std::shared_ptr renderer ) : m_renderer( renderer ), m_graphicsCommandBuffer( renderer.get() ), @@ -120,7 +128,20 @@ void SceneRenderer::SetData( std::shared_ptr data, AppState& appStat if( foundItem == shaderNames.end() && shaderNames.size() > 0 ) { - appState.modelState.visualizationShader.SetValue( shaderNames[0] ); + // pick the shader that best matches our default shader order + auto defaultShaderIt = std::find_if( DEFAULT_SHADER_ORDER.begin(), DEFAULT_SHADER_ORDER.end(), [&]( auto defaultShaderName ) { + return std::find_if( shaderNames.begin(), shaderNames.end(), [&]( auto shaderName ) { + return shaderName == defaultShaderName; + } ) != shaderNames.end(); + } ); + if( defaultShaderIt != DEFAULT_SHADER_ORDER.end() ) + { + appState.modelState.visualizationShader.SetValue( *defaultShaderIt ); + } + else + { + appState.modelState.visualizationShader.SetValue( shaderNames[0] ); + } } appState.modelState.availableShaders.SetValue( shaderNames ); diff --git a/src/viewer/rendering/vulkan/shadercache.cpp b/src/viewer/rendering/vulkan/shadercache.cpp index 28e26c8..4e615b5 100644 --- a/src/viewer/rendering/vulkan/shadercache.cpp +++ b/src/viewer/rendering/vulkan/shadercache.cpp @@ -254,14 +254,14 @@ std::vector ShaderCache::GetAvailableShaderNames( const std::vector std::vector result; result.reserve( s_cache.size() ); - std::vector elements; + std::vector> elements; elements.reserve( availableVertexElements.size() ); std::transform( availableVertexElements.begin(), availableVertexElements.end(), std::back_inserter( elements ), []( const cmf::VertexElement& element ) { - return element.usage; + return std::make_pair( element.usage, element.usageIndex ); } ); std::for_each( s_cache.begin(), s_cache.end(), [&result, &elements]( const auto& keyValue ) { @@ -275,7 +275,7 @@ std::vector ShaderCache::GetAvailableShaderNames( const std::vector // check if there are any inputs that the shader expects that is not in the cmf vertex declaration for( const auto& inputLayout : shaderContainer.inputLayout ) { - if( std::find( elements.begin(), elements.end(), inputLayout.usage ) == elements.end() ) + if( std::find( elements.begin(), elements.end(), std::make_pair( inputLayout.usage, inputLayout.usageIndex ) ) == elements.end() ) { return; } @@ -318,7 +318,7 @@ void ShaderCache::GenerateVertexDescriptions( std::string shaderName, const std: for( const auto& layout : shaderContainer.inputLayout ) { auto foundElement = std::find_if( availableVertexElements.begin(), availableVertexElements.end(), [layout]( const cmf::VertexElement& element ) { - return element.usage == layout.usage; + return element.usage == layout.usage && element.usageIndex == layout.usageIndex; } ); if( foundElement != availableVertexElements.end() ) diff --git a/src/viewer/rendering/vulkan/shadercache.h b/src/viewer/rendering/vulkan/shadercache.h index 6fbabd4..3888a85 100644 --- a/src/viewer/rendering/vulkan/shadercache.h +++ b/src/viewer/rendering/vulkan/shadercache.h @@ -31,6 +31,7 @@ struct ShaderInputLayout { uint8_t location; cmf::Usage usage; + uint8_t usageIndex; }; struct ShaderContainer diff --git a/src/viewer/scripts/shaderCacheCreator.py b/src/viewer/scripts/shaderCacheCreator.py index 0316751..38f9c8a 100644 --- a/src/viewer/scripts/shaderCacheCreator.py +++ b/src/viewer/scripts/shaderCacheCreator.py @@ -45,7 +45,8 @@ def enumerate_files( sourceDir, binaryDir, output): raise e inputDeclarations.append( ( int(match.group(1)), usage )) - vertexDeclarations[name] = inputDeclarations + vertexDeclarations[name] = inputDeclarations + for filename in os.listdir(binaryDir): if not filename.endswith('.spv.h'): continue @@ -87,22 +88,39 @@ def enumerate_files( sourceDir, binaryDir, output): compShader = """Shader( { #include \"%s\" } )""" % shaders[2] - - + splitNames = name.replace("model_", "").split("_") + + shaderConfigs = [[(locationIndex, usage, 0) for (locationIndex, usage) in vertexDeclarations.get(name, [])]] + modelShader = name.startswith("model_") + displayName = name - if name.startswith("model_"): + if modelShader: displayName = " ".join(s[0].upper() + s[1:] for s in splitNames) - - code += """ -{\"%s\", { - %s, - %s, - %s, - %s, - { %s } - } -},""" % (displayName, vertShader, fragShader, compShader, str(name.startswith("model_")).lower(), ",\n".join(["{%d, %s}" % (index, usage) for (index, usage) in vertexDeclarations.get(name, {}) ])) + vertexElements = vertexDeclarations.get(name) + if len(vertexElements) == 2: + # recreate the config + shaderConfigs = [] + for i in range(0, 8): + config = [] + for index, usage in vertexDeclarations[name]: + if usage != "cmf::Usage::Position": + config.append( (index, usage, i) ) + else: + config.append( (index, usage, 0) ) + + shaderConfigs.append(config) + + for (i, shaderConfig) in enumerate(shaderConfigs): + code += """ + {\"%s\", { + %s, + %s, + %s, + %s, + { %s } + } + },""" % (displayName + ("" if i == 0 else " " + str(i)), vertShader, fragShader, compShader, str(modelShader).lower(), ",\n".join(["{%d, %s, %d}" % (locationIndex, usage, usageIndex) for (locationIndex, usage, usageIndex) in shaderConfig ])) code = code[:-1] with open(output, 'w') as f: From 0ca212e94c49277f8c0bf175cc97df4e71001459 Mon Sep 17 00:00:00 2001 From: ccpnobody <55136070+ccpnobody@users.noreply.github.com> Date: Mon, 8 Jun 2026 14:29:32 +0000 Subject: [PATCH 04/21] Add vertex normal/tangent/binormal visualization Add debug visualization for vertex normals, tangents and binormals. New GLSL shaders and a shared packed_tangent.inc helper were added (unpacked/packed/legacy variants and axis vertex/fragment shaders). Primitive and axis code were extended: axis primitives, creation helpers, and mesh-level PrimitiveRenderable instances are created/initialized and rendered conditionally. GraphicsEffect now defines a VertexUboData and supports vertexInputRate so axis rendering can use instance-rate vertex streams; ShaderCache uses this rate when creating pipelines. UI and AppState were extended with per-mesh and global toggles to show/hide normals/tangents/bitangents. Also refine CMake shader generation to only compile .comp/.vert/.frag files and simplify model_packed_normal shaders to include the shared packed tangent code. A small change was made to the shaderCacheCreator script to skip unknown shader types. --- src/viewer/CMakeLists.txt | 18 ++-- src/viewer/appState.h | 3 + src/viewer/assets/shaders/binormalaxis.frag | 11 +++ src/viewer/assets/shaders/binormalaxis.vert | 37 ++++++++ .../assets/shaders/model_packed_normal.vert | 44 +-------- .../shaders/model_packed_normal_legacy.vert | 35 +------ src/viewer/assets/shaders/normalaxis.frag | 11 +++ src/viewer/assets/shaders/normalaxis.vert | 37 ++++++++ src/viewer/assets/shaders/packed_tangent.inc | 64 +++++++++++++ .../assets/shaders/packedtangentaxis.frag | 11 +++ .../assets/shaders/packedtangentaxis.vert | 51 +++++++++++ .../shaders/packedtangentlegacyaxis.frag | 11 +++ .../shaders/packedtangentlegacyaxis.vert | 51 +++++++++++ src/viewer/assets/shaders/tangentaxis.frag | 11 +++ src/viewer/assets/shaders/tangentaxis.vert | 37 ++++++++ src/viewer/rendering/models/axis.cpp | 70 ++++++++++++++ src/viewer/rendering/models/axis.h | 11 +++ .../rendering/models/primitiveEffects.cpp | 86 ++++++++++++++++++ .../rendering/models/primitiveEffects.h | 12 +++ src/viewer/rendering/renderable/mesh.cpp | 67 +++++++++++++- src/viewer/rendering/renderable/mesh.h | 10 +- src/viewer/rendering/renderable/primitive.cpp | 50 +++++----- src/viewer/rendering/renderable/primitive.h | 2 + src/viewer/rendering/uiRenderer.cpp | 91 +++++++++++++++++-- src/viewer/rendering/uiRenderer.h | 3 + src/viewer/rendering/vulkan/graphicseffect.h | 7 ++ src/viewer/rendering/vulkan/shadercache.cpp | 2 +- src/viewer/scripts/shaderCacheCreator.py | 2 + 28 files changed, 721 insertions(+), 124 deletions(-) create mode 100644 src/viewer/assets/shaders/binormalaxis.frag create mode 100644 src/viewer/assets/shaders/binormalaxis.vert create mode 100644 src/viewer/assets/shaders/normalaxis.frag create mode 100644 src/viewer/assets/shaders/normalaxis.vert create mode 100644 src/viewer/assets/shaders/packed_tangent.inc create mode 100644 src/viewer/assets/shaders/packedtangentaxis.frag create mode 100644 src/viewer/assets/shaders/packedtangentaxis.vert create mode 100644 src/viewer/assets/shaders/packedtangentlegacyaxis.frag create mode 100644 src/viewer/assets/shaders/packedtangentlegacyaxis.vert create mode 100644 src/viewer/assets/shaders/tangentaxis.frag create mode 100644 src/viewer/assets/shaders/tangentaxis.vert diff --git a/src/viewer/CMakeLists.txt b/src/viewer/CMakeLists.txt index ea0fea6..fa18e7d 100644 --- a/src/viewer/CMakeLists.txt +++ b/src/viewer/CMakeLists.txt @@ -82,18 +82,20 @@ foreach( SHADER_PATH ${SHADERS} ) file( RELATIVE_PATH SHADER_RELATIVE_PATH ${SOURCE_SHADER_DIR} ${SHADER_PATH} ) get_filename_component( SHADER_NAME ${SHADER_RELATIVE_PATH} NAME ) + get_filename_component( SHADER_EXTENSION ${SHADER_RELATIVE_PATH} EXT ) if( NOT EXISTS ${GENERATED_SHADER_DIR} ) file( MAKE_DIRECTORY ${GENERATED_SHADER_DIR} ) endif() - - add_custom_command( - OUTPUT "${GENERATED_SHADER_DIR}/${SHADER_NAME}.spv.h" - DEPENDS ${SHADER_PATH} - COMMAND ${GLSLC_EXECUTABLE} "${SHADER_PATH}" -mfmt=num -o "${GENERATED_SHADER_DIR}/${SHADER_NAME}.spv.h" - COMMENT "Compiling shader: ${SHADER_PATH} to ${GENERATED_SHADER_DIR}/${SHADER_NAME}.spv.h" - ) - list( APPEND COMPILED_SHADERS "${GENERATED_SHADER_DIR}/${SHADER_NAME}.spv.h" ) + if( ${SHADER_EXTENSION} STREQUAL ".comp" OR ${SHADER_EXTENSION} STREQUAL ".vert" OR ${SHADER_EXTENSION} STREQUAL ".frag" ) + add_custom_command( + OUTPUT "${GENERATED_SHADER_DIR}/${SHADER_NAME}.spv.h" + DEPENDS ${SHADER_PATH} + COMMAND ${GLSLC_EXECUTABLE} "${SHADER_PATH}" -mfmt=num -o "${GENERATED_SHADER_DIR}/${SHADER_NAME}.spv.h" + COMMENT "Compiling shader: ${SHADER_PATH} to ${GENERATED_SHADER_DIR}/${SHADER_NAME}.spv.h" + ) + list( APPEND COMPILED_SHADERS "${GENERATED_SHADER_DIR}/${SHADER_NAME}.spv.h" ) + endif() endforeach() add_custom_command( diff --git a/src/viewer/appState.h b/src/viewer/appState.h index 86f30e6..ee5eb67 100644 --- a/src/viewer/appState.h +++ b/src/viewer/appState.h @@ -108,6 +108,9 @@ struct MeshState State wireframeOverlay{ false }; State audioOcclusionMesh{ false }; State renderBoundingBox{ false }; + State showVertexNormals{ false }; + State showVertexTangents{ false }; + State showVertexBinormals{ false }; State activeLod{ 0 }; State meshScreenSize{ 0.0f }; diff --git a/src/viewer/assets/shaders/binormalaxis.frag b/src/viewer/assets/shaders/binormalaxis.frag new file mode 100644 index 0000000..b2d9d55 --- /dev/null +++ b/src/viewer/assets/shaders/binormalaxis.frag @@ -0,0 +1,11 @@ +#version 450 + +// inputs +layout( location = 0 ) in vec3 inFragColor; + +// Outputs +layout( location = 0 ) out vec4 outFragColor; + +void main() { + outFragColor = vec4( inFragColor, 1.0 ); +} \ No newline at end of file diff --git a/src/viewer/assets/shaders/binormalaxis.vert b/src/viewer/assets/shaders/binormalaxis.vert new file mode 100644 index 0000000..c291f0b --- /dev/null +++ b/src/viewer/assets/shaders/binormalaxis.vert @@ -0,0 +1,37 @@ +#version 450 + +// Constants +layout( binding = 0 ) uniform PerFrame +{ + mat4 projectionMatrix; + mat4 viewMatrix; + mat4 modelMatrix; +} perframe; + + +layout( binding = 1 ) uniform AxisConfig +{ + vec3 color; + float scale; + int axisSelector; +} axisConfig; + +// Inputs +layout( location = 0 ) in vec3 inPosition; // per instance +layout( location = 1 ) in vec3 inBinormal; // per instance + +// Output +layout ( location = 0 ) out vec3 outColor; + +void main() +{ + vec4 position = vec4(inPosition, 1.0); + + if( gl_VertexIndex == 1 ) + { + position.xyz += axisConfig.scale * inBinormal; + } + + gl_Position = perframe.projectionMatrix * perframe.viewMatrix * position ; + outColor = axisConfig.color; +} \ No newline at end of file diff --git a/src/viewer/assets/shaders/model_packed_normal.vert b/src/viewer/assets/shaders/model_packed_normal.vert index 857de6e..4ed960a 100644 --- a/src/viewer/assets/shaders/model_packed_normal.vert +++ b/src/viewer/assets/shaders/model_packed_normal.vert @@ -1,5 +1,7 @@ #version 450 +#include "packed_tangent.inc" + // Constants layout( binding = 0 ) uniform PerFrame { @@ -16,48 +18,6 @@ layout( location = 1 ) in vec4 inPackedTangents; layout( location = 0 ) out vec3 viewPosition; layout( location = 1 ) out vec3 normal; - -struct TangentSpace { - vec3 tangent; - vec3 bitangent; - vec3 normal; -}; - -TangentSpace unpackTangentSpace( vec4 t ) { - // Heavily optimized shader code that constructs the TBN matrix. - - // Extract the xyz components and square them - float x = t.x; - float y = t.y; - float z = t.z; - float x2 = x * x; - float y2 = y * y; - float z2 = z * z; - - // Optimized fma() chain to reconstruct W = sqrt(1 - x2 - y2 - z2) - // Don't use the above x2, y2 and z2 values, to reduce pipeline dependencies. - float w2 = clamp( fma( z, -z, fma( y, -y, fma( x, -x, 1.0f ) ) ), 0.0f, 1.0f ); - float w = sqrt( w2 ); - - // Calculate shared values. - // These multiplications by 2.0f are free on some GPUs. - float xy = x * y * 2.0f; - float xz = x * z * 2.0f; - float yz = y * z * 2.0f; - float xw = x * w * 2.0f; - float yw = y * w * 2.0f; - float zw = z * w * 2.0f; - - // Compute the three vectors. - TangentSpace space; - - space.tangent = vec3( fma( -2.0f, y2, fma( -2.0f, z2, 1.0f ) ), + xy + zw, + xz - yw ); - space.bitangent = vec3( - zw + xy, fma( -2.0f, x2, fma( -2.0f, z2, 1.0f ) ), + yz + xw ); - space.normal = vec3( + yw + xz, + yz - xw, fma( -2.0f, x2, fma( -2.0f, y2, 1.0f ) ) ) * t.w; // packed normal sign multiplication - - return space; -} - void main() { viewPosition = ( perframe.viewMatrix * vec4( inPosition, 1.0 ) ).xyz; diff --git a/src/viewer/assets/shaders/model_packed_normal_legacy.vert b/src/viewer/assets/shaders/model_packed_normal_legacy.vert index d9825ad..262ee95 100644 --- a/src/viewer/assets/shaders/model_packed_normal_legacy.vert +++ b/src/viewer/assets/shaders/model_packed_normal_legacy.vert @@ -1,4 +1,5 @@ #version 450 +#include "packed_tangent.inc" // Constants layout( binding = 0 ) uniform PerFrame @@ -16,43 +17,11 @@ layout( location = 1 ) in vec4 inPackedTangentsLegacy; layout( location = 0 ) out vec3 viewPosition; layout( location = 1 ) out vec3 normal; - -struct TangentSpace { - vec3 tangent; - vec3 bitangent; - vec3 normal; -}; - -vec2 SinCosFloat( float angle ) -{ - return vec2( sin( angle ), cos( angle ) ); -} - -TangentSpace unpackTangentSpace( vec4 tangents ) -{ - vec4 angles = tangents * 6.28318530718 - 3.14159265359; - vec4 sc0, sc1; - sc0.xy = SinCosFloat( angles.x ); - sc0.zw = SinCosFloat( angles.y ); - sc1.xy = SinCosFloat( angles.z ); - sc1.zw = SinCosFloat( angles.w ); - - TangentSpace space; - - vec3 tangent = vec3( sc0.y * abs( sc0.z ), sc0.x * abs( sc0.z ), sc0.w ); - vec3 bitangent = vec3( sc1.y * abs( sc1.z ), sc1.x * abs( sc1.z ), sc1.w ); - vec3 normal = -cross( tangent, bitangent ); - //normal = all( angles.yw > 0.0 ) ? -normal : normal; - if( angles.y > 0.0 && angles.w > 0.0 ) normal = -normal; - - return TangentSpace( tangent, bitangent, normal ); -} - void main() { viewPosition = ( perframe.viewMatrix * vec4( inPosition, 1.0 ) ).xyz; gl_Position = perframe.projectionMatrix * vec4( viewPosition, 1.0 ); - TangentSpace space = unpackTangentSpace( inPackedTangentsLegacy ); + TangentSpace space = unpackTangentSpaceLegacy( inPackedTangentsLegacy ); normal = ( perframe.viewMatrix * vec4( space.normal, 0.0 ) ).xyz; } \ No newline at end of file diff --git a/src/viewer/assets/shaders/normalaxis.frag b/src/viewer/assets/shaders/normalaxis.frag new file mode 100644 index 0000000..b2d9d55 --- /dev/null +++ b/src/viewer/assets/shaders/normalaxis.frag @@ -0,0 +1,11 @@ +#version 450 + +// inputs +layout( location = 0 ) in vec3 inFragColor; + +// Outputs +layout( location = 0 ) out vec4 outFragColor; + +void main() { + outFragColor = vec4( inFragColor, 1.0 ); +} \ No newline at end of file diff --git a/src/viewer/assets/shaders/normalaxis.vert b/src/viewer/assets/shaders/normalaxis.vert new file mode 100644 index 0000000..81b959a --- /dev/null +++ b/src/viewer/assets/shaders/normalaxis.vert @@ -0,0 +1,37 @@ +#version 450 + +// Constants +layout( binding = 0 ) uniform PerFrame +{ + mat4 projectionMatrix; + mat4 viewMatrix; + mat4 modelMatrix; +} perframe; + + +layout( binding = 1 ) uniform AxisConfig +{ + vec3 color; + float scale; + int axisSelector; +} axisConfig; + +// Inputs +layout( location = 0 ) in vec3 inPosition; // per instance +layout( location = 1 ) in vec3 inNormal; // per instance + +// Output +layout ( location = 0 ) out vec3 outColor; + +void main() +{ + vec4 position = vec4(inPosition, 1.0); + + if( gl_VertexIndex == 1 ) + { + position.xyz += axisConfig.scale * inNormal; + } + + gl_Position = perframe.projectionMatrix * perframe.viewMatrix * position ; + outColor = axisConfig.color; +} \ No newline at end of file diff --git a/src/viewer/assets/shaders/packed_tangent.inc b/src/viewer/assets/shaders/packed_tangent.inc new file mode 100644 index 0000000..86ac37b --- /dev/null +++ b/src/viewer/assets/shaders/packed_tangent.inc @@ -0,0 +1,64 @@ +struct TangentSpace { + vec3 tangent; + vec3 binormal; + vec3 normal; +}; + +vec2 SinCosFloat( float angle ) +{ + return vec2( sin( angle ), cos( angle ) ); +} + +TangentSpace unpackTangentSpaceLegacy( vec4 tangents ) +{ + vec4 angles = tangents * 6.28318530718 - 3.14159265359; + vec4 sc0, sc1; + sc0.xy = SinCosFloat( angles.x ); + sc0.zw = SinCosFloat( angles.y ); + sc1.xy = SinCosFloat( angles.z ); + sc1.zw = SinCosFloat( angles.w ); + + TangentSpace space; + + vec3 tangent = vec3( sc0.y * abs( sc0.z ), sc0.x * abs( sc0.z ), sc0.w ); + vec3 binormal = vec3( sc1.y * abs( sc1.z ), sc1.x * abs( sc1.z ), sc1.w ); + vec3 normal = -cross( tangent, binormal ); + if( angles.y > 0.0 && angles.w > 0.0 ) normal = -normal; + + return TangentSpace( tangent, binormal, normal ); +} + +TangentSpace unpackTangentSpace( vec4 t ) { + // Heavily optimized shader code that constructs the TBN matrix. + + // Extract the xyz components and square them + float x = t.x; + float y = t.y; + float z = t.z; + float x2 = x * x; + float y2 = y * y; + float z2 = z * z; + + // Optimized fma() chain to reconstruct W = sqrt(1 - x2 - y2 - z2) + // Don't use the above x2, y2 and z2 values, to reduce pipeline dependencies. + float w2 = clamp( fma( z, -z, fma( y, -y, fma( x, -x, 1.0f ) ) ), 0.0f, 1.0f ); + float w = sqrt( w2 ); + + // Calculate shared values. + // These multiplications by 2.0f are free on some GPUs. + float xy = x * y * 2.0f; + float xz = x * z * 2.0f; + float yz = y * z * 2.0f; + float xw = x * w * 2.0f; + float yw = y * w * 2.0f; + float zw = z * w * 2.0f; + + // Compute the three vectors. + TangentSpace space; + + space.tangent = vec3( fma( -2.0f, y2, fma( -2.0f, z2, 1.0f ) ), + xy + zw, + xz - yw ); + space.binormal = vec3( - zw + xy, fma( -2.0f, x2, fma( -2.0f, z2, 1.0f ) ), + yz + xw ); + space.normal = vec3( + yw + xz, + yz - xw, fma( -2.0f, x2, fma( -2.0f, y2, 1.0f ) ) ) * t.w; // packed normal sign multiplication + + return space; +} \ No newline at end of file diff --git a/src/viewer/assets/shaders/packedtangentaxis.frag b/src/viewer/assets/shaders/packedtangentaxis.frag new file mode 100644 index 0000000..b2d9d55 --- /dev/null +++ b/src/viewer/assets/shaders/packedtangentaxis.frag @@ -0,0 +1,11 @@ +#version 450 + +// inputs +layout( location = 0 ) in vec3 inFragColor; + +// Outputs +layout( location = 0 ) out vec4 outFragColor; + +void main() { + outFragColor = vec4( inFragColor, 1.0 ); +} \ No newline at end of file diff --git a/src/viewer/assets/shaders/packedtangentaxis.vert b/src/viewer/assets/shaders/packedtangentaxis.vert new file mode 100644 index 0000000..1bae169 --- /dev/null +++ b/src/viewer/assets/shaders/packedtangentaxis.vert @@ -0,0 +1,51 @@ +#version 450 +#include "packed_tangent.inc" + +// Constants +layout( binding = 0 ) uniform PerFrame +{ + mat4 projectionMatrix; + mat4 viewMatrix; +} perframe; + +layout( binding = 1 ) uniform AxisConfig +{ + vec3 color; + float scale; + int axisSelector; +} axisConfig; + +// Inputs +layout( location = 0 ) in vec3 inPosition; +layout( location = 1 ) in vec4 inPackedTangents; + +// output +layout ( location = 0 ) out vec3 outColor; + + +void main() +{ + vec4 position = vec4(inPosition, 1.0); + if( gl_VertexIndex == 1 ) + { + TangentSpace space = unpackTangentSpace( inPackedTangents ); + vec3 axis = vec3(0.0); + if( axisConfig.axisSelector == 0 ) + { + axis = space.normal; + } + else if( axisConfig.axisSelector == 1 ) + { + axis = space.tangent; + } + else if( axisConfig.axisSelector == 2 ) + { + axis = space.binormal; + } + position.xyz += axisConfig.scale * axis; + } + + gl_Position = perframe.projectionMatrix * perframe.viewMatrix * position; + + outColor = axisConfig.color; +} \ No newline at end of file diff --git a/src/viewer/assets/shaders/packedtangentlegacyaxis.frag b/src/viewer/assets/shaders/packedtangentlegacyaxis.frag new file mode 100644 index 0000000..b2d9d55 --- /dev/null +++ b/src/viewer/assets/shaders/packedtangentlegacyaxis.frag @@ -0,0 +1,11 @@ +#version 450 + +// inputs +layout( location = 0 ) in vec3 inFragColor; + +// Outputs +layout( location = 0 ) out vec4 outFragColor; + +void main() { + outFragColor = vec4( inFragColor, 1.0 ); +} \ No newline at end of file diff --git a/src/viewer/assets/shaders/packedtangentlegacyaxis.vert b/src/viewer/assets/shaders/packedtangentlegacyaxis.vert new file mode 100644 index 0000000..7875794 --- /dev/null +++ b/src/viewer/assets/shaders/packedtangentlegacyaxis.vert @@ -0,0 +1,51 @@ +#version 450 +#include "packed_tangent.inc" + +// Constants +layout( binding = 0 ) uniform PerFrame +{ + mat4 projectionMatrix; + mat4 viewMatrix; +} perframe; + +layout( binding = 1 ) uniform AxisConfig +{ + vec3 color; + float scale; + int axisSelector; +} axisConfig; + +// Inputs +layout( location = 0 ) in vec3 inPosition; +layout( location = 1 ) in vec4 inPackedTangentsLegacy; + +// output +layout ( location = 0 ) out vec3 outColor; + + +void main() +{ + vec4 position = vec4(inPosition, 1.0); + if( gl_VertexIndex == 1 ) + { + TangentSpace space = unpackTangentSpaceLegacy( inPackedTangentsLegacy ); + vec3 axis = vec3(0.0); + if( axisConfig.axisSelector == 0 ) + { + axis = space.normal; + } + else if( axisConfig.axisSelector == 1 ) + { + axis = space.tangent; + } + else if( axisConfig.axisSelector == 2 ) + { + axis = space.binormal; + } + position.xyz += axisConfig.scale * axis; + } + + gl_Position = perframe.projectionMatrix * perframe.viewMatrix * position; + + outColor = axisConfig.color; +} \ No newline at end of file diff --git a/src/viewer/assets/shaders/tangentaxis.frag b/src/viewer/assets/shaders/tangentaxis.frag new file mode 100644 index 0000000..b2d9d55 --- /dev/null +++ b/src/viewer/assets/shaders/tangentaxis.frag @@ -0,0 +1,11 @@ +#version 450 + +// inputs +layout( location = 0 ) in vec3 inFragColor; + +// Outputs +layout( location = 0 ) out vec4 outFragColor; + +void main() { + outFragColor = vec4( inFragColor, 1.0 ); +} \ No newline at end of file diff --git a/src/viewer/assets/shaders/tangentaxis.vert b/src/viewer/assets/shaders/tangentaxis.vert new file mode 100644 index 0000000..048c1ce --- /dev/null +++ b/src/viewer/assets/shaders/tangentaxis.vert @@ -0,0 +1,37 @@ +#version 450 + +// Constants +layout( binding = 0 ) uniform PerFrame +{ + mat4 projectionMatrix; + mat4 viewMatrix; + mat4 modelMatrix; +} perframe; + + +layout( binding = 1 ) uniform AxisConfig +{ + vec3 color; + float scale; + int axisSelector; +} axisConfig; + +// Inputs +layout( location = 0 ) in vec3 inPosition; // per instance +layout( location = 1 ) in vec3 inTangent; // per instance + +// Output +layout ( location = 0 ) out vec3 outColor; + +void main() +{ + vec4 position = vec4(inPosition, 1.0); + + if( gl_VertexIndex == 1 ) + { + position.xyz += axisConfig.scale * inTangent; + } + + gl_Position = perframe.projectionMatrix * perframe.viewMatrix * position ; + outColor = axisConfig.color; +} \ No newline at end of file diff --git a/src/viewer/rendering/models/axis.cpp b/src/viewer/rendering/models/axis.cpp index a69ffa9..16d011b 100644 --- a/src/viewer/rendering/models/axis.cpp +++ b/src/viewer/rendering/models/axis.cpp @@ -22,6 +22,22 @@ const std::array AXIS_MESH = { AxisVertex{ { 0.0f, 0.0f, 1.0f }, BLUE }, }; +const std::array NORMAL_MESH = { + AxisVertex{ { 0.0f, 0.0f, 0.0f }, GREEN }, + AxisVertex{ { 0.0f, 1.0f, 0.0f }, GREEN }, +}; + + +const std::array TANGENT_MESH = { + AxisVertex{ { 0.0f, 0.0f, 0.0f }, RED }, + AxisVertex{ { 1.0f, 0.0f, 0.0f }, RED }, +}; + +const std::array BITANGENT_MESH = { + AxisVertex{ { 0.0f, 0.0f, 0.0f }, BLUE }, + AxisVertex{ { 0.0f, 0.0f, 1.0f }, BLUE }, +}; + PrimitiveRenderable Create( std::shared_ptr renderer ) { auto effect = PrimitiveEffects::CreateAxisEffect( renderer ); @@ -32,4 +48,58 @@ PrimitiveRenderable Create( std::shared_ptr renderer ) return model; } +PrimitiveRenderable CreateNormal( std::shared_ptr renderer, const cmf::Mesh& mesh ) +{ + // the bufferdata is the output of the geoprepass and will be handled later + return PrimitiveRenderable( renderer, PrimitiveEffects::CreateUnpackedAxisEffect( renderer, cmf::Usage::Normal, mesh ) ); +} + +PrimitiveRenderable CreateTangent( std::shared_ptr renderer, const cmf::Mesh& mesh ) +{ + // the bufferdata is the output of the geoprepass and will be handled later + return PrimitiveRenderable( renderer, PrimitiveEffects::CreateUnpackedAxisEffect( renderer, cmf::Usage::Tangent, mesh ) ); +} + +PrimitiveRenderable CreateBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh ) +{ + // the bufferdata is the output of the geoprepass and will be handled later + return PrimitiveRenderable( renderer, PrimitiveEffects::CreateUnpackedAxisEffect( renderer, cmf::Usage::Binormal, mesh ) ); +} + +PrimitiveRenderable CreatePackedNormal( std::shared_ptr renderer, const cmf::Mesh& mesh ) +{ + // the bufferdata is the output of the geoprepass and will be handled later + return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedAxisEffect( renderer, cmf::Usage::Normal, mesh ) ); +} + +PrimitiveRenderable CreatePackedTangent( std::shared_ptr renderer, const cmf::Mesh& mesh ) +{ + // the bufferdata is the output of the geoprepass and will be handled later + return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedAxisEffect( renderer, cmf::Usage::Tangent, mesh ) ); +} + +PrimitiveRenderable CreatePackedBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh ) +{ + // the bufferdata is the output of the geoprepass and will be handled later + return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedAxisEffect( renderer, cmf::Usage::Binormal, mesh ) ); +} + +PrimitiveRenderable CreatePackedLegacyNormal( std::shared_ptr renderer, const cmf::Mesh& mesh ) +{ + // the bufferdata is the output of the geoprepass and will be handled later + return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedLegacyAxisEffect( renderer, cmf::Usage::Normal, mesh ) ); +} + +PrimitiveRenderable CreatePackedLegacyTangent( std::shared_ptr renderer, const cmf::Mesh& mesh ) +{ + // the bufferdata is the output of the geoprepass and will be handled later + return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedLegacyAxisEffect( renderer, cmf::Usage::Tangent, mesh ) ); +} + +PrimitiveRenderable CreatePackedLegacyBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh ) +{ + // the bufferdata is the output of the geoprepass and will be handled later + return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedLegacyAxisEffect( renderer, cmf::Usage::Binormal, mesh ) ); +} + } diff --git a/src/viewer/rendering/models/axis.h b/src/viewer/rendering/models/axis.h index 6c9332a..63bb4b0 100644 --- a/src/viewer/rendering/models/axis.h +++ b/src/viewer/rendering/models/axis.h @@ -7,5 +7,16 @@ namespace Axis { + PrimitiveRenderable Create( std::shared_ptr renderer ); + +PrimitiveRenderable CreateNormal( std::shared_ptr renderer, const cmf::Mesh& mesh ); +PrimitiveRenderable CreateTangent( std::shared_ptr renderer, const cmf::Mesh& mesh ); +PrimitiveRenderable CreateBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh ); +PrimitiveRenderable CreatePackedNormal( std::shared_ptr renderer, const cmf::Mesh& mesh ); +PrimitiveRenderable CreatePackedTangent( std::shared_ptr renderer, const cmf::Mesh& mesh ); +PrimitiveRenderable CreatePackedBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh ); +PrimitiveRenderable CreatePackedLegacyNormal( std::shared_ptr renderer, const cmf::Mesh& mesh ); +PrimitiveRenderable CreatePackedLegacyTangent( std::shared_ptr renderer, const cmf::Mesh& mesh ); +PrimitiveRenderable CreatePackedLegacyBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh ); }; \ No newline at end of file diff --git a/src/viewer/rendering/models/primitiveEffects.cpp b/src/viewer/rendering/models/primitiveEffects.cpp index 396c7a0..3d785b3 100644 --- a/src/viewer/rendering/models/primitiveEffects.cpp +++ b/src/viewer/rendering/models/primitiveEffects.cpp @@ -1,4 +1,8 @@ #include "primitiveEffects.h" +#include + +const float AXIS_LENGTH_SCALE = 0.005f; +const float AXIS_LENGTH_MIN_SIZE = 0.001f; GraphicsEffect PrimitiveEffects::CreateFlatColorEffect( std::shared_ptr renderer, ColorInfo colorInfo, GraphicsEffect::Config config, std::vector vertexToBoneMapping ) { @@ -45,4 +49,86 @@ GraphicsEffect PrimitiveEffects::CreateAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ) +{ + auto effectConfig = GraphicsEffect::Config(); + PrimitiveEffects::AxisConfig axisConfig; + + uint32_t stride = 0; + // find the position and normal elements in the vertex declaration, + for( const auto& element : mesh.decl ) + { + stride += cmf::GetVertexElementSize( element ); + effectConfig.availableVertexElements.push_back( element ); + } + effectConfig.lineWidth = 2.0f; + effectConfig.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + effectConfig.stride = stride; + effectConfig.vertexInputRate = VK_VERTEX_INPUT_RATE_INSTANCE; + + assert( usage == cmf::Usage::Normal || usage == cmf::Usage::Tangent || usage == cmf::Usage::Binormal ); + switch( usage ) + { + case cmf::Usage::Normal: + axisConfig.color = Vector3( 0, 1, 0 ); + axisConfig.axisIndex = 0; + break; + case cmf::Usage::Tangent: + axisConfig.color = Vector3( 1, 0, 0 ); + axisConfig.axisIndex = 1; + break; + case cmf::Usage::Binormal: + axisConfig.color = Vector3( 0, 0, 1 ); + axisConfig.axisIndex = 2; + break; + default: + throw std::runtime_error( "Unsupported usage for CreateUnpackedAxisEffect" ); + } + + axisConfig.scale = std::max( AXIS_LENGTH_MIN_SIZE, CcpMath::Sphere( mesh.bounds ).radius * AXIS_LENGTH_SCALE ); + + auto effect = GraphicsEffect( renderer ); + effect.RegisterUniformData( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 0 ); + // this is what axis to draw, the shader will decode the packed tangent and use the sign to determine if it's normal, tangent or bitangent + effect.RegisterUniformData( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 1, axisConfig ); + effect.SetConfig( effectConfig ); + return effect; +} + +GraphicsEffect PrimitiveEffects::CreateUnpackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ) +{ + std::string shaderName; + switch( usage ) + { + case cmf::Usage::Normal: + shaderName = "normalaxis"; + break; + case cmf::Usage::Tangent: + shaderName = "tangentaxis"; + break; + case cmf::Usage::Binormal: + shaderName = "binormalaxis"; + break; + default: + throw std::runtime_error( "Unsupported usage for CreateUnpackedAxisEffect" ); + } + auto effect = CreateAxisEffect( renderer, usage, mesh ); + effect.SetShaderName( shaderName ); + return effect; +} + +GraphicsEffect PrimitiveEffects::CreatePackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ) +{ + auto effect = CreateAxisEffect( renderer, usage, mesh ); + effect.SetShaderName( "packedtangentaxis" ); + return effect; +} + +GraphicsEffect PrimitiveEffects::CreatePackedLegacyAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ) +{ + auto effect = CreateAxisEffect( renderer, usage, mesh ); + effect.SetShaderName( "packedtangentaxislegacy" ); + return effect; } \ No newline at end of file diff --git a/src/viewer/rendering/models/primitiveEffects.h b/src/viewer/rendering/models/primitiveEffects.h index 6792fe9..ab9c377 100644 --- a/src/viewer/rendering/models/primitiveEffects.h +++ b/src/viewer/rendering/models/primitiveEffects.h @@ -1,6 +1,7 @@ #pragma once #include "../vulkan/graphicseffect.h" +#include namespace PrimitiveEffects { @@ -13,6 +14,13 @@ struct VertexUBO Vector4 boneInfo; // x = boneCount, y = how many bones transforms are used per instance }; +struct AxisConfig +{ + Vector3 color; + float scale; + uint32_t axisIndex; // 0 for normal, 1 for tangent, 2 for bitangent +}; + struct ColorInfo { Vector4 unselected; @@ -21,4 +29,8 @@ struct ColorInfo GraphicsEffect CreateFlatColorEffect( std::shared_ptr renderer, ColorInfo colorInfo, GraphicsEffect::Config config, std::vector vertexToBoneMapping ); GraphicsEffect CreateAxisEffect( std::shared_ptr renderer ); +GraphicsEffect CreateUnpackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ); +GraphicsEffect CreatePackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ); +GraphicsEffect CreatePackedLegacyAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ); + }; \ No newline at end of file diff --git a/src/viewer/rendering/renderable/mesh.cpp b/src/viewer/rendering/renderable/mesh.cpp index 88076d1..6a4144b 100644 --- a/src/viewer/rendering/renderable/mesh.cpp +++ b/src/viewer/rendering/renderable/mesh.cpp @@ -1,6 +1,7 @@ #include "mesh.h" #include "../models/boundingBox.h" +#include "../models/axis.h" #include "../renderer.h" #include "../vulkan/vulkanerrors.h" #include "../models/primitiveEffects.h" @@ -18,8 +19,34 @@ MeshRenderable::MeshRenderable( std::shared_ptr data, const cmf::Mes for( const auto& vertexElement : m_cmfMesh.decl ) { m_availableVertexElements.push_back( vertexElement ); + if( vertexElement.usage == cmf::Usage::Normal ) + { + m_normalAxisRenderable = std::make_unique( Axis::CreateNormal( renderer, m_cmfMesh ) ); + } + else if( vertexElement.usage == cmf::Usage::Tangent ) + { + m_tangentAxisRenderable = std::make_unique( Axis::CreateTangent( renderer, m_cmfMesh ) ); + } + else if( vertexElement.usage == cmf::Usage::Binormal ) + { + m_binormalAxisRenderable = std::make_unique( Axis::CreateBinormal( renderer, m_cmfMesh ) ); + } + else if( vertexElement.usage == cmf::Usage::PackedTangent ) + { + m_normalAxisRenderable = std::make_unique( Axis::CreatePackedNormal( renderer, m_cmfMesh ) ); + m_tangentAxisRenderable = std::make_unique( Axis::CreatePackedTangent( renderer, m_cmfMesh ) ); + m_binormalAxisRenderable = std::make_unique( Axis::CreatePackedBinormal( renderer, m_cmfMesh ) ); + } + else if( vertexElement.usage == cmf::Usage::PackedTangentLegacy ) + { + m_normalAxisRenderable = std::make_unique( Axis::CreatePackedNormal( renderer, m_cmfMesh ) ); + m_tangentAxisRenderable = std::make_unique( Axis::CreatePackedTangent( renderer, m_cmfMesh ) ); + m_binormalAxisRenderable = std::make_unique( Axis::CreatePackedBinormal( renderer, m_cmfMesh ) ); + } } + m_boundingSphere = CcpMath::Sphere( m_cmfMesh.bounds ); + m_boundingBoxTransform = ScalingMatrix( m_cmfMesh.bounds.Size() ) * TranslationMatrix( m_cmfMesh.bounds.Center() ); m_stride = m_cmfMesh.lods[0].vb.stride; @@ -99,6 +126,19 @@ void MeshRenderable::Initialize( AppState& appState ) sizeof( uint16_t ) ); m_audioOcclusionRenderable.Initialize(); } + + if( m_normalAxisRenderable ) + { + m_normalAxisRenderable->Initialize(); + } + if( m_tangentAxisRenderable ) + { + m_tangentAxisRenderable->Initialize(); + } + if( m_binormalAxisRenderable ) + { + m_binormalAxisRenderable->Initialize(); + } m_initialized = true; } @@ -197,7 +237,7 @@ void MeshRenderable::Update( AppState& appState, const Camera& camera ) void MeshRenderable::Render( GraphicsCommandBuffer& commandBuffer, const AppState& appState, const Camera& camera ) { - auto viewProj = VertexUboData{ camera.GetProjection(), camera.GetView() }; + auto viewProj = GraphicsEffect::VertexUboData{ camera.GetProjection(), camera.GetView() }; auto vertexBuffer = m_prepass.GetVertexBuffer(); const Buffer indexBuffer = m_prepass.GetIndexBuffer(); @@ -241,6 +281,7 @@ void MeshRenderable::Render( GraphicsCommandBuffer& commandBuffer, const AppStat void MeshRenderable::RenderDebug( GraphicsCommandBuffer& commandBuffer, const AppState& appState, const Camera& camera ) { + auto viewProj = GraphicsEffect::VertexUboData{ camera.GetProjection(), camera.GetView() }; if( m_showBoundingBox ) { auto vertexData = PrimitiveEffects::VertexUBO{ camera.GetProjection(), camera.GetView(), m_boundingBoxTransform, Vector4() }; @@ -250,10 +291,26 @@ void MeshRenderable::RenderDebug( GraphicsCommandBuffer& commandBuffer, const Ap if( m_audioOcclusion && !m_cmfMesh.audioOcclusionMesh.vertices.empty() && !m_cmfMesh.audioOcclusionMesh.indices.empty() ) { - auto viewProj = VertexUboData{ camera.GetProjection(), camera.GetView() }; m_audioOcclusionRenderable.SetUniformData( 0, viewProj ); m_audioOcclusionRenderable.Render( commandBuffer ); } + const auto& meshState = appState.modelState.meshes[m_meshIndex].GetValue(); + + if( m_tangentAxisRenderable && meshState.showVertexTangents.GetValue() ) + { + m_tangentAxisRenderable->SetUniformData( 0, viewProj ); + m_tangentAxisRenderable->Render( commandBuffer, &( m_prepass.GetVertexBuffer() ), nullptr, 2, cmf::GetStreamElementCount(m_cmfMesh.lods[m_currentLod].vb) ); + } + if( m_normalAxisRenderable && meshState.showVertexNormals.GetValue() ) + { + m_normalAxisRenderable->SetUniformData( 0, viewProj ); + m_normalAxisRenderable->Render( commandBuffer, &( m_prepass.GetVertexBuffer() ), nullptr, 2, cmf::GetStreamElementCount( m_cmfMesh.lods[m_currentLod].vb ) ); + } + if( m_binormalAxisRenderable && meshState.showVertexBinormals.GetValue() ) + { + m_binormalAxisRenderable->SetUniformData( 0, viewProj ); + m_binormalAxisRenderable->Render( commandBuffer, &( m_prepass.GetVertexBuffer() ), nullptr, 2, cmf::GetStreamElementCount( m_cmfMesh.lods[m_currentLod].vb ) ); + } } void MeshRenderable::PrepareMesh( ComputeCommandBuffer& commandBuffer ) @@ -304,7 +361,7 @@ VkResult MeshRenderable::SetRenderingMode( std::string shaderName, VkPolygonMode m_modelEffect.SetConfig( config ); if( !m_modelEffect.IsInitialized() ) { - m_modelEffect.RegisterUniformData( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 0 ); + m_modelEffect.RegisterUniformData( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 0 ); m_modelEffect.Initialize(); } @@ -325,7 +382,7 @@ VkResult MeshRenderable::SetRenderingMode( std::string shaderName, VkPolygonMode m_wireframeEffect.SetShaderName( "wireframeoverlay" ); m_wireframeEffect.SetConfig( wireframeConfig ); - m_wireframeEffect.RegisterUniformData( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 0 ); + m_wireframeEffect.RegisterUniformData( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 0 ); m_wireframeEffect.Initialize(); } return VK_SUCCESS; @@ -351,7 +408,7 @@ GraphicsEffect MeshRenderable::GetAudioOcclusionEffect( std::shared_ptr( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 0 ); + effect.RegisterUniformData( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 0 ); return effect; } diff --git a/src/viewer/rendering/renderable/mesh.h b/src/viewer/rendering/renderable/mesh.h index a0ec560..605443b 100644 --- a/src/viewer/rendering/renderable/mesh.h +++ b/src/viewer/rendering/renderable/mesh.h @@ -38,12 +38,6 @@ class MeshRenderable }; GraphicsEffect GetAudioOcclusionEffect( std::shared_ptr renderer, const cmf::Mesh& cmfMesh ); - struct VertexUboData - { - Matrix proj; - Matrix view; - }; - std::vector m_availableVertexElements; std::shared_ptr m_renderer; @@ -79,6 +73,10 @@ class MeshRenderable PrimitiveRenderable m_boundingBox; Matrix m_boundingBoxTransform{}; + std::unique_ptr m_normalAxisRenderable; + std::unique_ptr m_tangentAxisRenderable; + std::unique_ptr m_binormalAxisRenderable; + // audio occlusion PrimitiveRenderable m_audioOcclusionRenderable; }; \ No newline at end of file diff --git a/src/viewer/rendering/renderable/primitive.cpp b/src/viewer/rendering/renderable/primitive.cpp index ec0e78a..3f55eb8 100644 --- a/src/viewer/rendering/renderable/primitive.cpp +++ b/src/viewer/rendering/renderable/primitive.cpp @@ -50,40 +50,48 @@ VkResult PrimitiveRenderable::Initialize() { VkCommandBuffer copyCmd; - RETURN_ERROR( m_renderer->CreateCopyCommandBuffer( ©Cmd ) ); - - m_vertexBuffer = BufferBuilder::Build( m_renderer.get(), m_data, m_vertexBufferSize, BufferType::Vertex, m_vertexStride ); - m_vertexBuffer->CopyFromStaging( copyCmd ); - m_elements = m_vertexBufferSize / m_vertexStride; - if( m_indexData ) + if( m_data != nullptr ) { - m_indexBuffer = BufferBuilder::Build( m_renderer.get(), m_indexData, m_indexBufferSize, BufferType::Index, m_indexStride ); - m_indexBuffer->CopyFromStaging( copyCmd ); - m_elements = m_indexBufferSize / m_indexStride; - } + RETURN_ERROR( m_renderer->CreateCopyCommandBuffer( ©Cmd ) ); - RETURN_ERROR( m_renderer->EndCopyCommandBuffer( copyCmd ) ); - m_vertexBuffer->ReleaseStaging( m_renderer.get() ); - if( m_indexData ) - { - m_indexBuffer->ReleaseStaging( m_renderer.get() ); + m_vertexBuffer = BufferBuilder::Build( m_renderer.get(), m_data, m_vertexBufferSize, BufferType::Vertex, m_vertexStride ); + m_vertexBuffer->CopyFromStaging( copyCmd ); + m_elements = m_vertexBufferSize / m_vertexStride; + if( m_indexData ) + { + m_indexBuffer = BufferBuilder::Build( m_renderer.get(), m_indexData, m_indexBufferSize, BufferType::Index, m_indexStride ); + m_indexBuffer->CopyFromStaging( copyCmd ); + m_elements = m_indexBufferSize / m_indexStride; + } + + RETURN_ERROR( m_renderer->EndCopyCommandBuffer( copyCmd ) ); + m_vertexBuffer->ReleaseStaging( m_renderer.get() ); + if( m_indexData ) + { + m_indexBuffer->ReleaseStaging( m_renderer.get() ); + } } RETURN_ERROR( m_effect.Initialize() ); return VK_SUCCESS; } -void PrimitiveRenderable::Render( GraphicsCommandBuffer& commandBuffer, uint32_t instanceCount ) +void PrimitiveRenderable::Render( GraphicsCommandBuffer& commandBuffer, const Buffer* vertexBuffer, const Buffer* indexBuffer, uint32_t elements, uint32_t instanceCount ) { commandBuffer.BindEffect( m_effect ); - commandBuffer.BindVertexBuffer( m_vertexBuffer->GetGpuBuffer() ); - if( m_indexBuffer ) + commandBuffer.BindVertexBuffer( vertexBuffer->GetGpuBuffer() ); + if( indexBuffer ) { - commandBuffer.BindIndexBuffer( *m_indexBuffer ); - commandBuffer.DrawIndexed( 0, m_elements, instanceCount ); + commandBuffer.BindIndexBuffer( *indexBuffer ); + commandBuffer.DrawIndexed( 0, elements, instanceCount ); } else { - commandBuffer.Draw( 0, m_elements, instanceCount ); + commandBuffer.Draw( 0, elements, instanceCount ); } } + +void PrimitiveRenderable::Render( GraphicsCommandBuffer& commandBuffer, uint32_t instanceCount ) +{ + Render( commandBuffer, m_vertexBuffer, m_indexBuffer, m_elements, instanceCount ); +} diff --git a/src/viewer/rendering/renderable/primitive.h b/src/viewer/rendering/renderable/primitive.h index 45254a9..f8443cd 100644 --- a/src/viewer/rendering/renderable/primitive.h +++ b/src/viewer/rendering/renderable/primitive.h @@ -11,6 +11,7 @@ class PrimitiveRenderable PrimitiveRenderable( std::shared_ptr renderer, GraphicsEffect&& effect ); ~PrimitiveRenderable(); + void SetBuffer( const Buffer* buffer ); void SetBufferData( const uint8_t* data, uint32_t size, uint32_t stride ); void SetIndexData( const uint8_t* data, uint32_t size, uint32_t stride ); GraphicsEffect& GetEffect(); @@ -23,6 +24,7 @@ class PrimitiveRenderable VkResult Initialize(); void Render( GraphicsCommandBuffer& commandBuffer, uint32_t instanceCount = 1 ); + void Render( GraphicsCommandBuffer& commandBuffer, const Buffer* vertexBuffer, const Buffer* indexBuffer, uint32_t elements, uint32_t instanceCount ); private: std::shared_ptr m_renderer{ nullptr }; diff --git a/src/viewer/rendering/uiRenderer.cpp b/src/viewer/rendering/uiRenderer.cpp index 2335d76..0c2716b 100644 --- a/src/viewer/rendering/uiRenderer.cpp +++ b/src/viewer/rendering/uiRenderer.cpp @@ -424,7 +424,6 @@ void UIRenderer::SetupGeneralView( AppState& appState ) ImGui::Text( "%u", m_uiState.modelStates.indexCount ); ImGui::TableNextRow(); - ImGui::TableNextColumn(); ImGui::Text( "Meshes" ); ImGui::TableNextColumn(); @@ -487,6 +486,50 @@ void UIRenderer::SetupGeneralView( AppState& appState ) ImGui::TableNextRow(); ImGui::EndDisabled(); + ImGui::TableNextColumn(); + ImGui::Text( "Normals" ); + ImGui::SetItemTooltip( "Toggles the normals for all meshes" ); + ImGui::TableNextColumn(); + bool normals = std::all_of( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { + return state.GetValue().showVertexNormals.GetValue(); + } ) && + appState.modelState.meshes.size() > 0; + OnChange( ImGui::Checkbox( "##vertexnormals", &normals ), [&appState, &normals]() { + std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [normals]( State& state ) { + state.GetValue().showVertexNormals.SetValue( normals ); + } ); + } ); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text( "Tangents" ); + ImGui::SetItemTooltip( "Toggles the tangents for all meshes" ); + ImGui::TableNextColumn(); + bool tangents = std::all_of( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { + return state.GetValue().showVertexTangents.GetValue(); + } ) && + appState.modelState.meshes.size() > 0; + OnChange( ImGui::Checkbox( "##vertextangents", &tangents ), [&appState, &tangents]() { + std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [tangents]( State& state ) { + state.GetValue().showVertexTangents.SetValue( tangents ); + } ); + } ); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text( "Bitangents" ); + ImGui::SetItemTooltip( "Toggles the bitangents for all meshes" ); + ImGui::TableNextColumn(); + bool bitangents = std::all_of( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { + return state.GetValue().showVertexBinormals.GetValue(); + } ) && + appState.modelState.meshes.size() > 0; + OnChange( ImGui::Checkbox( "##vertexbitangents", &bitangents ), [&appState, &bitangents]() { + std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [bitangents]( State& state ) { + state.GetValue().showVertexBinormals.SetValue( bitangents ); + } ); + } ); + ImGui::EndTable(); } } @@ -594,17 +637,43 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) ImGui::EndDisabled(); ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text( "Normals" ); + ImGui::SetItemTooltip( "Toggles the normals" ); + ImGui::TableNextColumn(); + bool normals = mesh.showVertexNormals; + OnChange( ImGui::Checkbox( "##vertexnormals", &normals ), [&appState, &mesh, &normals]() { + appState.modelState.meshes[mesh.meshIndex].GetValue().showVertexNormals.SetValue( normals ); + } ); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text( "Tangents" ); + ImGui::SetItemTooltip( "Toggles the tangents" ); + ImGui::TableNextColumn(); + bool tangents = mesh.showVertexTangents; + OnChange( ImGui::Checkbox( "##vertextangents", &tangents ), [&appState, &mesh, &tangents]() { + appState.modelState.meshes[mesh.meshIndex].GetValue().showVertexTangents.SetValue( tangents ); + } ); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text( "Bitangents" ); + ImGui::SetItemTooltip( "Toggles the bitangents" ); + ImGui::TableNextColumn(); + bool bitangents = mesh.showVertexBinormals; + OnChange( ImGui::Checkbox( "##vertexbitangents", &bitangents ), [&appState, &mesh, &bitangents]() { + appState.modelState.meshes[mesh.meshIndex].GetValue().showVertexBinormals.SetValue( bitangents ); + } ); ImGui::EndTable(); - if( !mesh.morphTargets.empty() ) + + std::string header = "Morph Targets (" + std::to_string( mesh.morphTargets.size() ) + ")"; + if( ImGui::CollapsingHeader( header.c_str() ) ) { - if( ImGui::CollapsingHeader( "Morph Targets" ) ) + uint32_t index = 0; + for( const auto& morphTarget : mesh.morphTargets ) { - uint32_t index = 0; - for( const auto& morphTarget : mesh.morphTargets ) - { - SetupMorphTarget( morphTarget, mesh.meshIndex, appState ); - } + SetupMorphTarget( morphTarget, mesh.meshIndex, appState ); } } } @@ -1117,6 +1186,12 @@ void UIRenderer::UpdateUiState( AppState& appState ) meshState.audioOcclusionMesh = meshAppState.audioOcclusionMesh.GetValue(); meshState.hasAudioOcclusionMesh = !mesh.audioOcclusionMesh.vertices.empty() && !mesh.audioOcclusionMesh.indices.empty(); meshState.boundingBox = meshAppState.renderBoundingBox.GetValue(); + meshState.showVertexNormals = meshAppState.showVertexNormals.GetValue(); + meshState.showVertexBinormals = meshAppState.showVertexBinormals.GetValue(); + meshState.showVertexTangents = meshAppState.showVertexTangents.GetValue(); + + bool showVertexTangents{ false }; + bool showVertexBinormals{ false }; maxLod = std::max( maxLod, mesh.lods.size() ); meshState.vertexCount = 0; diff --git a/src/viewer/rendering/uiRenderer.h b/src/viewer/rendering/uiRenderer.h index 79c4441..8389d7e 100644 --- a/src/viewer/rendering/uiRenderer.h +++ b/src/viewer/rendering/uiRenderer.h @@ -68,6 +68,9 @@ class UIRenderer bool wireframeOverlay{ false }; bool audioOcclusionMesh{ false }; bool hasAudioOcclusionMesh{ false }; + bool showVertexNormals{ false }; + bool showVertexTangents{ false }; + bool showVertexBinormals{ false }; }; struct ModelUiState diff --git a/src/viewer/rendering/vulkan/graphicseffect.h b/src/viewer/rendering/vulkan/graphicseffect.h index ea93aff..0089972 100644 --- a/src/viewer/rendering/vulkan/graphicseffect.h +++ b/src/viewer/rendering/vulkan/graphicseffect.h @@ -7,6 +7,12 @@ class GraphicsEffect : public Effect public: GraphicsEffect( std::shared_ptr renderer ); + struct VertexUboData + { + Matrix proj; + Matrix view; + }; + struct Config { VkPrimitiveTopology topology{ VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST }; @@ -16,6 +22,7 @@ class GraphicsEffect : public Effect VkCullModeFlags cullMode{ VK_CULL_MODE_BACK_BIT }; bool blend{ false }; size_t stride{ 0 }; + VkVertexInputRate vertexInputRate{ VK_VERTEX_INPUT_RATE_VERTEX }; std::vector availableVertexElements{}; }; diff --git a/src/viewer/rendering/vulkan/shadercache.cpp b/src/viewer/rendering/vulkan/shadercache.cpp index 4e615b5..c8beed0 100644 --- a/src/viewer/rendering/vulkan/shadercache.cpp +++ b/src/viewer/rendering/vulkan/shadercache.cpp @@ -168,7 +168,7 @@ VkResult ShaderCache::CreateGraphicsPipeline( const Renderer* renderer, std::str VkVertexInputBindingDescription bindingDecl{}; bindingDecl.binding = 0; // expected to be 0 since we only support one vertex buffer - bindingDecl.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + bindingDecl.inputRate = config.vertexInputRate; bindingDecl.stride = (uint32_t)config.stride; VkPipelineVertexInputStateCreateInfo vertexInputState{}; diff --git a/src/viewer/scripts/shaderCacheCreator.py b/src/viewer/scripts/shaderCacheCreator.py index 38f9c8a..c879d61 100644 --- a/src/viewer/scripts/shaderCacheCreator.py +++ b/src/viewer/scripts/shaderCacheCreator.py @@ -61,6 +61,8 @@ def enumerate_files( sourceDir, binaryDir, output): shaders[1] = filename elif shaderType == 'comp': shaders[2] = filename + else: + continueu shaderGroups[name] = shaders From 73746ddc4ec68bc5ae4be3d3f2791575d973778c Mon Sep 17 00:00:00 2001 From: ccpnobody <55136070+ccpnobody@users.noreply.github.com> Date: Mon, 8 Jun 2026 14:46:04 +0000 Subject: [PATCH 05/21] Fixing normal, tangent and binormal visualization for legacy packed tangent meshes --- src/viewer/rendering/models/primitiveEffects.cpp | 2 +- src/viewer/rendering/renderable/mesh.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/viewer/rendering/models/primitiveEffects.cpp b/src/viewer/rendering/models/primitiveEffects.cpp index 3d785b3..e868413 100644 --- a/src/viewer/rendering/models/primitiveEffects.cpp +++ b/src/viewer/rendering/models/primitiveEffects.cpp @@ -129,6 +129,6 @@ GraphicsEffect PrimitiveEffects::CreatePackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ) { auto effect = CreateAxisEffect( renderer, usage, mesh ); - effect.SetShaderName( "packedtangentaxislegacy" ); + effect.SetShaderName( "packedtangentlegacyaxis" ); return effect; } \ No newline at end of file diff --git a/src/viewer/rendering/renderable/mesh.cpp b/src/viewer/rendering/renderable/mesh.cpp index 6a4144b..dd84927 100644 --- a/src/viewer/rendering/renderable/mesh.cpp +++ b/src/viewer/rendering/renderable/mesh.cpp @@ -39,9 +39,9 @@ MeshRenderable::MeshRenderable( std::shared_ptr data, const cmf::Mes } else if( vertexElement.usage == cmf::Usage::PackedTangentLegacy ) { - m_normalAxisRenderable = std::make_unique( Axis::CreatePackedNormal( renderer, m_cmfMesh ) ); - m_tangentAxisRenderable = std::make_unique( Axis::CreatePackedTangent( renderer, m_cmfMesh ) ); - m_binormalAxisRenderable = std::make_unique( Axis::CreatePackedBinormal( renderer, m_cmfMesh ) ); + m_normalAxisRenderable = std::make_unique( Axis::CreatePackedLegacyNormal( renderer, m_cmfMesh ) ); + m_tangentAxisRenderable = std::make_unique( Axis::CreatePackedLegacyTangent( renderer, m_cmfMesh ) ); + m_binormalAxisRenderable = std::make_unique( Axis::CreatePackedLegacyBinormal( renderer, m_cmfMesh ) ); } } From 1355dfa7c4f8995da830191d74d6e66e3d494bba Mon Sep 17 00:00:00 2001 From: ccpnobody <55136070+ccpnobody@users.noreply.github.com> Date: Mon, 8 Jun 2026 14:46:35 +0000 Subject: [PATCH 06/21] Adding a display flag for the model which turns off display flag for all meshes --- src/viewer/rendering/uiRenderer.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/viewer/rendering/uiRenderer.cpp b/src/viewer/rendering/uiRenderer.cpp index 0c2716b..d0288a2 100644 --- a/src/viewer/rendering/uiRenderer.cpp +++ b/src/viewer/rendering/uiRenderer.cpp @@ -444,6 +444,20 @@ void UIRenderer::SetupGeneralView( AppState& appState ) SetupCombo( "##visualiationMode", m_uiState.visualizationShaderComboBox, appState.modelState.visualizationShader ); ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text( "Display" ); + ImGui::TableNextColumn(); + bool display = std::all_of( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { + return state.GetValue().display.GetValue(); + } ) && + appState.modelState.meshes.size() > 0; + OnChange( ImGui::Checkbox( "##displaycheckbox", &display ), [&appState, &display]() { + std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [display]( State& state ) { + state.GetValue().display.SetValue( display ); + } ); + } ); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); ImGui::Text( "Bounding Box" ); ImGui::TableNextColumn(); From 67f8ffcb2cbfd001cf1eae028484c3f0ca4e8636 Mon Sep 17 00:00:00 2001 From: ccpnobody <55136070+ccpnobody@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:20:01 +0000 Subject: [PATCH 07/21] Refactor orientation gizmo & shader fixes Rename and refactor orientation/gizmo primitives and effects, clean up shaders and misc fixes based on pr code review: - Rename Axis::Create -> Axis::CreateOrientationPrimitive and update callers (orientationGizmoRenderer, animationState). - Introduce CreateOrientationEffect and refactor PrimitiveEffects: move CreateAxisEffect implementation, add CreateOrientationEffect, use constexpr axis constants, and rework CreateFlatColorEffect placement. - Remove unused modelMatrix from tangent/normal/binormal vertex shaders. - Add callback invocations for showVertexNormals, showVertexTangents, showVertexBinormals in app state. - Improve SFINAE utility formatting in appState_template_impl.h. - Add padding to AxisConfig and update primitive header (remove SetBuffer API) and implementation (assert vertexBuffer non-null). - Move DEFAULT_SHADER_ORDER into anonymous namespace and minor formatting/spacing cleanups. - Fix typo in shaderCacheCreator.py (continueu -> continue) and tighten CMake shader-extension string comparisons. Also include small fixes for rendering call sites and mesh debug rendering argument formatting. --- src/viewer/CMakeLists.txt | 2 +- src/viewer/appState.h | 3 + src/viewer/appState_template_impl.h | 12 ++- src/viewer/assets/shaders/binormalaxis.vert | 1 - src/viewer/assets/shaders/normalaxis.vert | 1 - src/viewer/assets/shaders/tangentaxis.vert | 1 - src/viewer/rendering/models/axis.cpp | 20 +--- src/viewer/rendering/models/axis.h | 2 +- .../rendering/models/primitiveEffects.cpp | 99 ++++++++++--------- .../rendering/models/primitiveEffects.h | 3 +- .../rendering/orientationGizmoRenderer.cpp | 2 +- .../rendering/renderable/animationState.cpp | 2 +- src/viewer/rendering/renderable/mesh.cpp | 2 +- src/viewer/rendering/renderable/primitive.cpp | 2 + src/viewer/rendering/renderable/primitive.h | 1 - src/viewer/rendering/sceneRenderer.cpp | 4 +- src/viewer/rendering/uiRenderer.cpp | 8 +- src/viewer/scripts/shaderCacheCreator.py | 2 +- 18 files changed, 79 insertions(+), 88 deletions(-) diff --git a/src/viewer/CMakeLists.txt b/src/viewer/CMakeLists.txt index fa18e7d..d25aba6 100644 --- a/src/viewer/CMakeLists.txt +++ b/src/viewer/CMakeLists.txt @@ -87,7 +87,7 @@ foreach( SHADER_PATH ${SHADERS} ) if( NOT EXISTS ${GENERATED_SHADER_DIR} ) file( MAKE_DIRECTORY ${GENERATED_SHADER_DIR} ) endif() - if( ${SHADER_EXTENSION} STREQUAL ".comp" OR ${SHADER_EXTENSION} STREQUAL ".vert" OR ${SHADER_EXTENSION} STREQUAL ".frag" ) + if( "${SHADER_EXTENSION}" STREQUAL ".comp" OR "${SHADER_EXTENSION}" STREQUAL ".vert" OR "${SHADER_EXTENSION}" STREQUAL ".frag" ) add_custom_command( OUTPUT "${GENERATED_SHADER_DIR}/${SHADER_NAME}.spv.h" DEPENDS ${SHADER_PATH} diff --git a/src/viewer/appState.h b/src/viewer/appState.h index ee5eb67..15d0032 100644 --- a/src/viewer/appState.h +++ b/src/viewer/appState.h @@ -123,6 +123,9 @@ struct MeshState renderBoundingBox.CallCallbacks( appState ); activeLod.CallCallbacks( appState ); meshScreenSize.CallCallbacks( appState ); + showVertexNormals.CallCallbacks( appState ); + showVertexTangents.CallCallbacks( appState ); + showVertexBinormals.CallCallbacks( appState ); } }; diff --git a/src/viewer/appState_template_impl.h b/src/viewer/appState_template_impl.h index 2cb6a9e..75743fd 100644 --- a/src/viewer/appState_template_impl.h +++ b/src/viewer/appState_template_impl.h @@ -52,11 +52,15 @@ T& State::GetValue() namespace detail { - template - struct has_call_callbacks : std::false_type {}; +template +struct has_call_callbacks : std::false_type +{ +}; - template - struct has_call_callbacks().CallCallbacks( std::declval() ) )>> : std::true_type {}; +template +struct has_call_callbacks().CallCallbacks( std::declval() ) )>> : std::true_type +{ +}; } template diff --git a/src/viewer/assets/shaders/binormalaxis.vert b/src/viewer/assets/shaders/binormalaxis.vert index c291f0b..c7a03a2 100644 --- a/src/viewer/assets/shaders/binormalaxis.vert +++ b/src/viewer/assets/shaders/binormalaxis.vert @@ -5,7 +5,6 @@ layout( binding = 0 ) uniform PerFrame { mat4 projectionMatrix; mat4 viewMatrix; - mat4 modelMatrix; } perframe; diff --git a/src/viewer/assets/shaders/normalaxis.vert b/src/viewer/assets/shaders/normalaxis.vert index 81b959a..e37b1ea 100644 --- a/src/viewer/assets/shaders/normalaxis.vert +++ b/src/viewer/assets/shaders/normalaxis.vert @@ -5,7 +5,6 @@ layout( binding = 0 ) uniform PerFrame { mat4 projectionMatrix; mat4 viewMatrix; - mat4 modelMatrix; } perframe; diff --git a/src/viewer/assets/shaders/tangentaxis.vert b/src/viewer/assets/shaders/tangentaxis.vert index 048c1ce..e8ef516 100644 --- a/src/viewer/assets/shaders/tangentaxis.vert +++ b/src/viewer/assets/shaders/tangentaxis.vert @@ -5,7 +5,6 @@ layout( binding = 0 ) uniform PerFrame { mat4 projectionMatrix; mat4 viewMatrix; - mat4 modelMatrix; } perframe; diff --git a/src/viewer/rendering/models/axis.cpp b/src/viewer/rendering/models/axis.cpp index 16d011b..3296ea6 100644 --- a/src/viewer/rendering/models/axis.cpp +++ b/src/viewer/rendering/models/axis.cpp @@ -22,25 +22,9 @@ const std::array AXIS_MESH = { AxisVertex{ { 0.0f, 0.0f, 1.0f }, BLUE }, }; -const std::array NORMAL_MESH = { - AxisVertex{ { 0.0f, 0.0f, 0.0f }, GREEN }, - AxisVertex{ { 0.0f, 1.0f, 0.0f }, GREEN }, -}; - - -const std::array TANGENT_MESH = { - AxisVertex{ { 0.0f, 0.0f, 0.0f }, RED }, - AxisVertex{ { 1.0f, 0.0f, 0.0f }, RED }, -}; - -const std::array BITANGENT_MESH = { - AxisVertex{ { 0.0f, 0.0f, 0.0f }, BLUE }, - AxisVertex{ { 0.0f, 0.0f, 1.0f }, BLUE }, -}; - -PrimitiveRenderable Create( std::shared_ptr renderer ) +PrimitiveRenderable CreateOrientationPrimitive( std::shared_ptr renderer ) { - auto effect = PrimitiveEffects::CreateAxisEffect( renderer ); + auto effect = PrimitiveEffects::CreateOrientationEffect( renderer ); auto model = PrimitiveRenderable( renderer, std::move( effect ) ); model.SetBufferData( reinterpret_cast( AXIS_MESH.data() ), (uint32_t)AXIS_MESH.size() * sizeof( AxisVertex ), sizeof( AxisVertex ) ); diff --git a/src/viewer/rendering/models/axis.h b/src/viewer/rendering/models/axis.h index 63bb4b0..6dd9575 100644 --- a/src/viewer/rendering/models/axis.h +++ b/src/viewer/rendering/models/axis.h @@ -8,7 +8,7 @@ namespace Axis { -PrimitiveRenderable Create( std::shared_ptr renderer ); +PrimitiveRenderable CreateOrientationPrimitive( std::shared_ptr renderer ); PrimitiveRenderable CreateNormal( std::shared_ptr renderer, const cmf::Mesh& mesh ); PrimitiveRenderable CreateTangent( std::shared_ptr renderer, const cmf::Mesh& mesh ); diff --git a/src/viewer/rendering/models/primitiveEffects.cpp b/src/viewer/rendering/models/primitiveEffects.cpp index e868413..4213ef6 100644 --- a/src/viewer/rendering/models/primitiveEffects.cpp +++ b/src/viewer/rendering/models/primitiveEffects.cpp @@ -1,55 +1,10 @@ #include "primitiveEffects.h" #include -const float AXIS_LENGTH_SCALE = 0.005f; -const float AXIS_LENGTH_MIN_SIZE = 0.001f; - -GraphicsEffect PrimitiveEffects::CreateFlatColorEffect( std::shared_ptr renderer, ColorInfo colorInfo, GraphicsEffect::Config config, std::vector vertexToBoneMapping ) +namespace { - config.availableVertexElements = { - { cmf::Usage::Position, - 0, - cmf::ElementType::Float32, - 4, - 0 } - }; - config.stride = sizeof( Vector4 ); - auto effect = GraphicsEffect( renderer ); - effect.RegisterUniformData( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 0 ); - effect.RegisterStorageBuffer( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 1, 0xFF ); // bone transforms - effect.RegisterStorageBuffer( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 2, vertexToBoneMapping.data(), vertexToBoneMapping.size() ); // vertex to bone mapping - effect.RegisterStorageBuffer( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 3, 0xFF ); // selected bones - effect.RegisterUniformData( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 4, colorInfo ); - effect.SetConfig( config ); - effect.SetShaderName( "flatcolor" ); - return effect; -} - -GraphicsEffect PrimitiveEffects::CreateAxisEffect( std::shared_ptr renderer ) -{ - auto config = GraphicsEffect::Config(); - config.availableVertexElements = { - { cmf::Usage::Position, - 0, - cmf::ElementType::Float32, - 3, - 0 }, - { cmf::Usage::Color, - 0, - cmf::ElementType::Float32, - 3, - sizeof( Vector3 ) }, - }; - config.lineWidth = 2.0f; - config.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; - config.stride = sizeof( Vector3 ) * 2; - auto effect = GraphicsEffect( renderer ); - effect.RegisterUniformData( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 0 ); - effect.RegisterStorageBuffer( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 1, 0xFF ); - effect.SetConfig( config ); - effect.SetShaderName( "orientationgizmo" ); - return effect; -} +constexpr float AXIS_LENGTH_SCALE = 0.005f; +constexpr float AXIS_LENGTH_MIN_SIZE = 0.001f; GraphicsEffect CreateAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ) { @@ -96,6 +51,54 @@ GraphicsEffect CreateAxisEffect( std::shared_ptr renderer, const effect.SetConfig( effectConfig ); return effect; } +} + +GraphicsEffect PrimitiveEffects::CreateFlatColorEffect( std::shared_ptr renderer, ColorInfo colorInfo, GraphicsEffect::Config config, std::vector vertexToBoneMapping ) +{ + config.availableVertexElements = { + { cmf::Usage::Position, + 0, + cmf::ElementType::Float32, + 4, + 0 } + }; + config.stride = sizeof( Vector4 ); + auto effect = GraphicsEffect( renderer ); + effect.RegisterUniformData( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 0 ); + effect.RegisterStorageBuffer( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 1, 0xFF ); // bone transforms + effect.RegisterStorageBuffer( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 2, vertexToBoneMapping.data(), vertexToBoneMapping.size() ); // vertex to bone mapping + effect.RegisterStorageBuffer( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 3, 0xFF ); // selected bones + effect.RegisterUniformData( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 4, colorInfo ); + effect.SetConfig( config ); + effect.SetShaderName( "flatcolor" ); + return effect; +} + +GraphicsEffect PrimitiveEffects::CreateOrientationEffect( std::shared_ptr renderer ) +{ + auto config = GraphicsEffect::Config(); + config.availableVertexElements = { + { cmf::Usage::Position, + 0, + cmf::ElementType::Float32, + 3, + 0 }, + { cmf::Usage::Color, + 0, + cmf::ElementType::Float32, + 3, + sizeof( Vector3 ) }, + }; + config.lineWidth = 2.0f; + config.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + config.stride = sizeof( Vector3 ) * 2; + auto effect = GraphicsEffect( renderer ); + effect.RegisterUniformData( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 0 ); + effect.RegisterStorageBuffer( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 1, 0xFF ); + effect.SetConfig( config ); + effect.SetShaderName( "orientationgizmo" ); + return effect; +} GraphicsEffect PrimitiveEffects::CreateUnpackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ) { diff --git a/src/viewer/rendering/models/primitiveEffects.h b/src/viewer/rendering/models/primitiveEffects.h index ab9c377..a623e82 100644 --- a/src/viewer/rendering/models/primitiveEffects.h +++ b/src/viewer/rendering/models/primitiveEffects.h @@ -17,6 +17,7 @@ struct VertexUBO struct AxisConfig { Vector3 color; + float _padding; float scale; uint32_t axisIndex; // 0 for normal, 1 for tangent, 2 for bitangent }; @@ -28,7 +29,7 @@ struct ColorInfo }; GraphicsEffect CreateFlatColorEffect( std::shared_ptr renderer, ColorInfo colorInfo, GraphicsEffect::Config config, std::vector vertexToBoneMapping ); -GraphicsEffect CreateAxisEffect( std::shared_ptr renderer ); +GraphicsEffect CreateOrientationEffect( std::shared_ptr renderer ); GraphicsEffect CreateUnpackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ); GraphicsEffect CreatePackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ); GraphicsEffect CreatePackedLegacyAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ); diff --git a/src/viewer/rendering/orientationGizmoRenderer.cpp b/src/viewer/rendering/orientationGizmoRenderer.cpp index 311b57b..60c3f70 100644 --- a/src/viewer/rendering/orientationGizmoRenderer.cpp +++ b/src/viewer/rendering/orientationGizmoRenderer.cpp @@ -6,7 +6,7 @@ OrientationGizmoRenderer::OrientationGizmoRenderer( std::shared_ptr renderer ) : m_renderer( renderer ), m_graphicsCommandBuffer( renderer.get() ), - m_axis( Axis::Create( renderer ) ) + m_axis( Axis::CreateOrientationPrimitive( renderer ) ) { m_graphicsCommandBuffer.SetClearDepth( 1.0f ); } diff --git a/src/viewer/rendering/renderable/animationState.cpp b/src/viewer/rendering/renderable/animationState.cpp index ab32c81..a6c8ec4 100644 --- a/src/viewer/rendering/renderable/animationState.cpp +++ b/src/viewer/rendering/renderable/animationState.cpp @@ -11,7 +11,7 @@ AnimationState::AnimationState( uint8_t m_skeletonIndex, const cmf::Skeleton& sk m_skeletonIndex( m_skeletonIndex ), m_boneRenderable( std::make_unique( Bones::CreateBone( renderer, skeleton ) ) ), m_jointRenderable( std::make_unique( Bones::CreateJoint( renderer, skeleton ) ) ), - m_axisRenderable( std::make_unique( Axis::Create( renderer ) ) ), + m_axisRenderable( std::make_unique( Axis::CreateOrientationPrimitive( renderer ) ) ), m_skeleton( skeleton ) { ClearBoneMapping(); diff --git a/src/viewer/rendering/renderable/mesh.cpp b/src/viewer/rendering/renderable/mesh.cpp index dd84927..44268e3 100644 --- a/src/viewer/rendering/renderable/mesh.cpp +++ b/src/viewer/rendering/renderable/mesh.cpp @@ -299,7 +299,7 @@ void MeshRenderable::RenderDebug( GraphicsCommandBuffer& commandBuffer, const Ap if( m_tangentAxisRenderable && meshState.showVertexTangents.GetValue() ) { m_tangentAxisRenderable->SetUniformData( 0, viewProj ); - m_tangentAxisRenderable->Render( commandBuffer, &( m_prepass.GetVertexBuffer() ), nullptr, 2, cmf::GetStreamElementCount(m_cmfMesh.lods[m_currentLod].vb) ); + m_tangentAxisRenderable->Render( commandBuffer, &( m_prepass.GetVertexBuffer() ), nullptr, 2, cmf::GetStreamElementCount( m_cmfMesh.lods[m_currentLod].vb ) ); } if( m_normalAxisRenderable && meshState.showVertexNormals.GetValue() ) { diff --git a/src/viewer/rendering/renderable/primitive.cpp b/src/viewer/rendering/renderable/primitive.cpp index 3f55eb8..8b1b9e8 100644 --- a/src/viewer/rendering/renderable/primitive.cpp +++ b/src/viewer/rendering/renderable/primitive.cpp @@ -78,6 +78,8 @@ VkResult PrimitiveRenderable::Initialize() void PrimitiveRenderable::Render( GraphicsCommandBuffer& commandBuffer, const Buffer* vertexBuffer, const Buffer* indexBuffer, uint32_t elements, uint32_t instanceCount ) { + assert( vertexBuffer != nullptr ); + commandBuffer.BindEffect( m_effect ); commandBuffer.BindVertexBuffer( vertexBuffer->GetGpuBuffer() ); if( indexBuffer ) diff --git a/src/viewer/rendering/renderable/primitive.h b/src/viewer/rendering/renderable/primitive.h index f8443cd..7187b4a 100644 --- a/src/viewer/rendering/renderable/primitive.h +++ b/src/viewer/rendering/renderable/primitive.h @@ -11,7 +11,6 @@ class PrimitiveRenderable PrimitiveRenderable( std::shared_ptr renderer, GraphicsEffect&& effect ); ~PrimitiveRenderable(); - void SetBuffer( const Buffer* buffer ); void SetBufferData( const uint8_t* data, uint32_t size, uint32_t stride ); void SetIndexData( const uint8_t* data, uint32_t size, uint32_t stride ); GraphicsEffect& GetEffect(); diff --git a/src/viewer/rendering/sceneRenderer.cpp b/src/viewer/rendering/sceneRenderer.cpp index 6d91d6f..048934c 100644 --- a/src/viewer/rendering/sceneRenderer.cpp +++ b/src/viewer/rendering/sceneRenderer.cpp @@ -4,13 +4,15 @@ #include "vulkan/vulkanerrors.h" #include "vulkan/vulkanenums.h" -const std::vector DEFAULT_SHADER_ORDER +namespace { +const std::vector DEFAULT_SHADER_ORDER{ "Normal", "Packed Normal", "Packed Normal Legacy", "Face Normal" }; +} SceneRenderer::SceneRenderer( std::shared_ptr renderer ) : m_renderer( renderer ), diff --git a/src/viewer/rendering/uiRenderer.cpp b/src/viewer/rendering/uiRenderer.cpp index d0288a2..d3028a3 100644 --- a/src/viewer/rendering/uiRenderer.cpp +++ b/src/viewer/rendering/uiRenderer.cpp @@ -448,8 +448,8 @@ void UIRenderer::SetupGeneralView( AppState& appState ) ImGui::Text( "Display" ); ImGui::TableNextColumn(); bool display = std::all_of( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { - return state.GetValue().display.GetValue(); - } ) && + return state.GetValue().display.GetValue(); + } ) && appState.modelState.meshes.size() > 0; OnChange( ImGui::Checkbox( "##displaycheckbox", &display ), [&appState, &display]() { std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [display]( State& state ) { @@ -684,7 +684,6 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) std::string header = "Morph Targets (" + std::to_string( mesh.morphTargets.size() ) + ")"; if( ImGui::CollapsingHeader( header.c_str() ) ) { - uint32_t index = 0; for( const auto& morphTarget : mesh.morphTargets ) { SetupMorphTarget( morphTarget, mesh.meshIndex, appState ); @@ -1204,9 +1203,6 @@ void UIRenderer::UpdateUiState( AppState& appState ) meshState.showVertexBinormals = meshAppState.showVertexBinormals.GetValue(); meshState.showVertexTangents = meshAppState.showVertexTangents.GetValue(); - bool showVertexTangents{ false }; - bool showVertexBinormals{ false }; - maxLod = std::max( maxLod, mesh.lods.size() ); meshState.vertexCount = 0; meshState.indexCount = 0; diff --git a/src/viewer/scripts/shaderCacheCreator.py b/src/viewer/scripts/shaderCacheCreator.py index c879d61..8c3ebfd 100644 --- a/src/viewer/scripts/shaderCacheCreator.py +++ b/src/viewer/scripts/shaderCacheCreator.py @@ -62,7 +62,7 @@ def enumerate_files( sourceDir, binaryDir, output): elif shaderType == 'comp': shaders[2] = filename else: - continueu + continue shaderGroups[name] = shaders From 9c800ed461ba1b57dae813943ecd6099802c4e56 Mon Sep 17 00:00:00 2001 From: ccpnobody <55136070+ccpnobody@users.noreply.github.com> Date: Tue, 9 Jun 2026 09:08:36 +0000 Subject: [PATCH 08/21] Disable normal/tangent/bitangent UI Disable per-mesh and global UI controls for normals, tangents and bitangents when meshes lack those vertex attributes. Adds hasNormals/hasTangents/hasBinormals flags to MeshUiState and populates them in UpdateUiState by inspecting the vertex declaration; wraps the corresponding ImGui controls with BeginDisabled/EndDisabled. Also minor cleanup in primitive effects: value-initialize AxisConfig and remove a padding field from the struct. --- .../rendering/models/primitiveEffects.cpp | 2 +- .../rendering/models/primitiveEffects.h | 1 - src/viewer/rendering/uiRenderer.cpp | 33 +++++++++++++++++++ src/viewer/rendering/uiRenderer.h | 3 ++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/viewer/rendering/models/primitiveEffects.cpp b/src/viewer/rendering/models/primitiveEffects.cpp index 4213ef6..1019ed7 100644 --- a/src/viewer/rendering/models/primitiveEffects.cpp +++ b/src/viewer/rendering/models/primitiveEffects.cpp @@ -9,7 +9,7 @@ constexpr float AXIS_LENGTH_MIN_SIZE = 0.001f; GraphicsEffect CreateAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ) { auto effectConfig = GraphicsEffect::Config(); - PrimitiveEffects::AxisConfig axisConfig; + PrimitiveEffects::AxisConfig axisConfig{}; uint32_t stride = 0; // find the position and normal elements in the vertex declaration, diff --git a/src/viewer/rendering/models/primitiveEffects.h b/src/viewer/rendering/models/primitiveEffects.h index a623e82..6c7d188 100644 --- a/src/viewer/rendering/models/primitiveEffects.h +++ b/src/viewer/rendering/models/primitiveEffects.h @@ -17,7 +17,6 @@ struct VertexUBO struct AxisConfig { Vector3 color; - float _padding; float scale; uint32_t axisIndex; // 0 for normal, 1 for tangent, 2 for bitangent }; diff --git a/src/viewer/rendering/uiRenderer.cpp b/src/viewer/rendering/uiRenderer.cpp index d3028a3..d8c9e43 100644 --- a/src/viewer/rendering/uiRenderer.cpp +++ b/src/viewer/rendering/uiRenderer.cpp @@ -500,6 +500,10 @@ void UIRenderer::SetupGeneralView( AppState& appState ) ImGui::TableNextRow(); ImGui::EndDisabled(); + bool hasNormals = std::any_of( m_uiState.modelStates.meshes.begin(), m_uiState.modelStates.meshes.end(), []( const MeshUiState& state ) { + return state.hasNormals; + } ); + ImGui::BeginDisabled( !hasNormals ); ImGui::TableNextColumn(); ImGui::Text( "Normals" ); ImGui::SetItemTooltip( "Toggles the normals for all meshes" ); @@ -513,7 +517,13 @@ void UIRenderer::SetupGeneralView( AppState& appState ) state.GetValue().showVertexNormals.SetValue( normals ); } ); } ); + ImGui::EndDisabled(); + + bool hasTangents = std::any_of( m_uiState.modelStates.meshes.begin(), m_uiState.modelStates.meshes.end(), []( const MeshUiState& state ) { + return state.hasTangents; + } ); + ImGui::BeginDisabled( !hasTangents ); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text( "Tangents" ); @@ -528,7 +538,13 @@ void UIRenderer::SetupGeneralView( AppState& appState ) state.GetValue().showVertexTangents.SetValue( tangents ); } ); } ); + ImGui::EndDisabled(); + + bool hasBinormals = std::any_of( m_uiState.modelStates.meshes.begin(), m_uiState.modelStates.meshes.end(), []( const MeshUiState& state ) { + return state.hasBinormals; + } ); + ImGui::BeginDisabled( !hasBinormals ); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text( "Bitangents" ); @@ -543,6 +559,7 @@ void UIRenderer::SetupGeneralView( AppState& appState ) state.GetValue().showVertexBinormals.SetValue( bitangents ); } ); } ); + ImGui::EndDisabled(); ImGui::EndTable(); } @@ -650,6 +667,7 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) } ); ImGui::EndDisabled(); + ImGui::BeginDisabled( !mesh.hasNormals ); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text( "Normals" ); @@ -659,7 +677,9 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) OnChange( ImGui::Checkbox( "##vertexnormals", &normals ), [&appState, &mesh, &normals]() { appState.modelState.meshes[mesh.meshIndex].GetValue().showVertexNormals.SetValue( normals ); } ); + ImGui::EndDisabled(); + ImGui::BeginDisabled( !mesh.hasTangents ); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text( "Tangents" ); @@ -670,6 +690,9 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) appState.modelState.meshes[mesh.meshIndex].GetValue().showVertexTangents.SetValue( tangents ); } ); + ImGui::EndDisabled(); + + ImGui::BeginDisabled( !mesh.hasBinormals ); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text( "Bitangents" ); @@ -679,6 +702,7 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) OnChange( ImGui::Checkbox( "##vertexbitangents", &bitangents ), [&appState, &mesh, &bitangents]() { appState.modelState.meshes[mesh.meshIndex].GetValue().showVertexBinormals.SetValue( bitangents ); } ); + ImGui::EndDisabled(); ImGui::EndTable(); std::string header = "Morph Targets (" + std::to_string( mesh.morphTargets.size() ) + ")"; @@ -1202,6 +1226,15 @@ void UIRenderer::UpdateUiState( AppState& appState ) meshState.showVertexNormals = meshAppState.showVertexNormals.GetValue(); meshState.showVertexBinormals = meshAppState.showVertexBinormals.GetValue(); meshState.showVertexTangents = meshAppState.showVertexTangents.GetValue(); + meshState.hasNormals = std::any_of( mesh.decl.begin(), mesh.decl.end(), []( const cmf::VertexElement& element ) { + return element.usage == cmf::Usage::Normal || element.usage == cmf::Usage::PackedTangent || element.usage == cmf::Usage::PackedTangentLegacy; + } ); + meshState.hasBinormals = std::any_of( mesh.decl.begin(), mesh.decl.end(), []( const cmf::VertexElement& element ) { + return element.usage == cmf::Usage::Binormal || element.usage == cmf::Usage::PackedTangent || element.usage == cmf::Usage::PackedTangentLegacy; + } ); + meshState.hasTangents = std::any_of( mesh.decl.begin(), mesh.decl.end(), []( const cmf::VertexElement& element ) { + return element.usage == cmf::Usage::Tangent || element.usage == cmf::Usage::PackedTangent || element.usage == cmf::Usage::PackedTangentLegacy; + } ); maxLod = std::max( maxLod, mesh.lods.size() ); meshState.vertexCount = 0; diff --git a/src/viewer/rendering/uiRenderer.h b/src/viewer/rendering/uiRenderer.h index 8389d7e..791a8d0 100644 --- a/src/viewer/rendering/uiRenderer.h +++ b/src/viewer/rendering/uiRenderer.h @@ -71,6 +71,9 @@ class UIRenderer bool showVertexNormals{ false }; bool showVertexTangents{ false }; bool showVertexBinormals{ false }; + bool hasNormals{ false }; + bool hasTangents{ false }; + bool hasBinormals{ false }; }; struct ModelUiState From c3995b92cb8fee0400e8b403c006447a23108947 Mon Sep 17 00:00:00 2001 From: ccpnobody <55136070+ccpnobody@users.noreply.github.com> Date: Tue, 9 Jun 2026 09:44:36 +0000 Subject: [PATCH 09/21] Compute camera near/far from bounding sphere Changed the near and far clip calculations based on how far the boundingsphere center is in the view space, instead of euclidian eye to center space --- src/viewer/rendering/camera.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/viewer/rendering/camera.cpp b/src/viewer/rendering/camera.cpp index 88cece3..b7080e4 100644 --- a/src/viewer/rendering/camera.cpp +++ b/src/viewer/rendering/camera.cpp @@ -99,12 +99,17 @@ void Camera::HandleMouseStateChanged( MouseState& mouseState ) Matrix Camera::GetProjection() const { - float distToModel = Length( m_at - m_boundingSphere.center ); + const float ABSOLUTE_MIN_NEAR_PLANE = 0.01f; + const float boundingDepth = m_boundingSphere.radius * 2.0f; + const float centerDepth = -TransformCoord( m_boundingSphere.center, GetView() ).z; + const float nearPlane = std::max( ABSOLUTE_MIN_NEAR_PLANE, centerDepth - boundingDepth ); + const float farPlane = std::max( nearPlane + ABSOLUTE_MIN_NEAR_PLANE, centerDepth + boundingDepth ); + return PerspectiveFovMatrix( m_fov, m_screenSize.x / m_screenSize.y, - std::max( 0.01f, std::max( m_boundingSphere.radius / 10000.0f, m_zoom - distToModel - m_boundingSphere.radius ) ), - distToModel + m_zoom + m_boundingSphere.radius ); + nearPlane, + farPlane ); } Matrix Camera::GetRotation() const From c40d6188e8f781a2069cf055046121a87f709f7c Mon Sep 17 00:00:00 2001 From: ccpnobody <55136070+ccpnobody@users.noreply.github.com> Date: Fri, 12 Jun 2026 20:54:06 +0000 Subject: [PATCH 10/21] Support per-usage vertex attributes Add support for multiple vertex-usage indices and shader input declarations. Introduces GraphicsEffectTypes and ShaderInputDeclaration handling, updates GraphicsEffect APIs and many call sites to accept a usageIndex or shader input declaration. StateCollection gained templated AddState overloads and empty(), model/mesh code now tracks per-usage axis renderables (vectors) and uses State> for per-index toggles. Shaders were annotated for multi_usage, shader cache and UI were updated to expose available shaders with their declarations and to present per-attribute checkboxes. Overall this enables proper handling of multiple normal/tangent/bitangent streams. --- src/viewer/appState.h | 23 +- src/viewer/appState_template_impl.h | 12 +- src/viewer/assets/shaders/binormalaxis.vert | 6 +- src/viewer/assets/shaders/model_color.vert | 3 +- src/viewer/assets/shaders/model_normal.vert | 3 +- .../assets/shaders/model_packed_normal.vert | 3 +- .../shaders/model_packed_normal_legacy.vert | 4 +- src/viewer/assets/shaders/normalaxis.vert | 6 +- .../assets/shaders/packedtangentaxis.vert | 5 +- .../shaders/packedtangentlegacyaxis.vert | 5 +- src/viewer/assets/shaders/tangentaxis.vert | 6 +- src/viewer/rendering/models/axis.cpp | 40 ++- src/viewer/rendering/models/axis.h | 18 +- src/viewer/rendering/models/bones.cpp | 14 +- src/viewer/rendering/models/boundingBox.cpp | 2 +- .../rendering/models/primitiveEffects.cpp | 73 +++-- .../rendering/models/primitiveEffects.h | 10 +- src/viewer/rendering/renderable/mesh.cpp | 152 ++++++---- src/viewer/rendering/renderable/mesh.h | 8 +- src/viewer/rendering/sceneRenderer.cpp | 33 +-- src/viewer/rendering/uiRenderer.cpp | 154 +++++----- src/viewer/rendering/uiRenderer.h | 13 +- .../rendering/vulkan/graphicseffect.cpp | 4 +- src/viewer/rendering/vulkan/graphicseffect.h | 18 +- .../rendering/vulkan/graphicseffecttypes.h | 51 ++++ src/viewer/rendering/vulkan/shadercache.cpp | 92 +++--- src/viewer/rendering/vulkan/shadercache.h | 10 +- src/viewer/scripts/shaderCacheCreator.py | 267 ++++++++++-------- 28 files changed, 614 insertions(+), 421 deletions(-) create mode 100644 src/viewer/rendering/vulkan/graphicseffecttypes.h diff --git a/src/viewer/appState.h b/src/viewer/appState.h index 15d0032..c884d6a 100644 --- a/src/viewer/appState.h +++ b/src/viewer/appState.h @@ -3,6 +3,7 @@ #include #include "data/cmfcontent.h" +#include "rendering/vulkan/graphicseffecttypes.h" //forwards declaration struct AppState; @@ -56,8 +57,12 @@ class StateCollection StateCollection( T initialValue ); size_t AddState(); size_t AddState( T initialValue ); - size_t AddState( std::function configurator ); - size_t AddState( T initialValue, std::function configurator ); + + template + size_t AddState( Callable configurator ); + + template + size_t AddState( T initialValue, Callable configurator ); void Clear(); void CallCallbacks( AppState& appState ); @@ -67,6 +72,7 @@ class StateCollection void RemoveAt( size_t index ); size_t size() const; + bool empty() const; // Non-const iterators iterator begin(); @@ -104,13 +110,14 @@ enum class CameraTrigger struct MeshState { State display{ true }; + /// the pair is StateCollection> morphs{ { 0.0f, true } }; State wireframeOverlay{ false }; State audioOcclusionMesh{ false }; State renderBoundingBox{ false }; - State showVertexNormals{ false }; - State showVertexTangents{ false }; - State showVertexBinormals{ false }; + StateCollection> showVertexNormals{ { 0, false } }; + StateCollection> showVertexTangents{ { 0, false } }; + StateCollection> showVertexBinormals{ { 0, false } }; State activeLod{ 0 }; State meshScreenSize{ 0.0f }; @@ -136,8 +143,8 @@ struct ModelState StateCollection meshes{ {} }; - State visualizationShader{ "" }; - State> availableShaders{ {} }; + State> activeShader{ {} }; + State>> availableShaders{ {} }; State polygonMode{ VK_POLYGON_MODE_FILL }; State currentAnimation{ "" }; @@ -155,7 +162,7 @@ struct ModelState { selectedLod.CallCallbacks( appState ); meshes.CallCallbacks( appState ); - visualizationShader.CallCallbacks( appState ); + activeShader.CallCallbacks( appState ); availableShaders.CallCallbacks( appState ); polygonMode.CallCallbacks( appState ); currentAnimation.CallCallbacks( appState ); diff --git a/src/viewer/appState_template_impl.h b/src/viewer/appState_template_impl.h index 75743fd..6d409e8 100644 --- a/src/viewer/appState_template_impl.h +++ b/src/viewer/appState_template_impl.h @@ -115,7 +115,8 @@ size_t StateCollection::AddState( T initialValue ) } template -size_t StateCollection::AddState( std::function configurator ) +template +size_t StateCollection::AddState( Callable configurator ) { State state( m_initialValue ); m_states.push_back( state ); @@ -125,7 +126,8 @@ size_t StateCollection::AddState( std::function configurator ) } template -size_t StateCollection::AddState( T initialValue, std::function configurator ) +template +size_t StateCollection::AddState( T initialValue, Callable configurator ) { State state( initialValue ); m_states.push_back( state ); @@ -188,6 +190,12 @@ size_t StateCollection::size() const return m_states.size(); } +template +bool StateCollection::empty() const +{ + return m_states.empty(); +} + // Non-const iterators template typename StateCollection::iterator StateCollection::begin() diff --git a/src/viewer/assets/shaders/binormalaxis.vert b/src/viewer/assets/shaders/binormalaxis.vert index c7a03a2..cc8b337 100644 --- a/src/viewer/assets/shaders/binormalaxis.vert +++ b/src/viewer/assets/shaders/binormalaxis.vert @@ -1,4 +1,5 @@ #version 450 +#pragma multi_usage inBinormal // Constants layout( binding = 0 ) uniform PerFrame @@ -16,8 +17,9 @@ layout( binding = 1 ) uniform AxisConfig } axisConfig; // Inputs -layout( location = 0 ) in vec3 inPosition; // per instance -layout( location = 1 ) in vec3 inBinormal; // per instance +// inputs are per instance, not per vertex +layout( location = 0 ) in vec3 inPosition; +layout( location = 1 ) in vec3 inBinormal; // Output layout ( location = 0 ) out vec3 outColor; diff --git a/src/viewer/assets/shaders/model_color.vert b/src/viewer/assets/shaders/model_color.vert index 4128f94..27453d7 100644 --- a/src/viewer/assets/shaders/model_color.vert +++ b/src/viewer/assets/shaders/model_color.vert @@ -1,4 +1,5 @@ #version 450 +#pragma multi_usage inColor // Constants layout( binding = 0 ) uniform PerFrame @@ -10,7 +11,7 @@ layout( binding = 0 ) uniform PerFrame // Inputs layout( location = 0 ) in vec3 inPosition; -layout( location = 1 ) in vec3 inColor; +layout( location = 1 ) in vec3 inColor; // Outputs; layout( location = 0 ) out vec3 color; diff --git a/src/viewer/assets/shaders/model_normal.vert b/src/viewer/assets/shaders/model_normal.vert index dc47af7..627106e 100644 --- a/src/viewer/assets/shaders/model_normal.vert +++ b/src/viewer/assets/shaders/model_normal.vert @@ -9,8 +9,9 @@ layout( binding = 0 ) uniform PerFrame // Inputs +// inputs are per instance, not per vertex layout( location = 0 ) in vec3 inPosition; -layout( location = 1 ) in vec3 inNormal; +layout( location = 1 ) in vec3 inNormal; // shadercachecreator_multi_usage_index // Outputs layout( location = 0 ) out vec3 viewPosition; diff --git a/src/viewer/assets/shaders/model_packed_normal.vert b/src/viewer/assets/shaders/model_packed_normal.vert index 4ed960a..f6e67c9 100644 --- a/src/viewer/assets/shaders/model_packed_normal.vert +++ b/src/viewer/assets/shaders/model_packed_normal.vert @@ -1,4 +1,5 @@ #version 450 +#pragma multi_usage inPackedTangents #include "packed_tangent.inc" @@ -12,7 +13,7 @@ layout( binding = 0 ) uniform PerFrame // Inputs layout( location = 0 ) in vec3 inPosition; -layout( location = 1 ) in vec4 inPackedTangents; +layout( location = 1 ) in vec4 inPackedTangents; // Outputs layout( location = 0 ) out vec3 viewPosition; diff --git a/src/viewer/assets/shaders/model_packed_normal_legacy.vert b/src/viewer/assets/shaders/model_packed_normal_legacy.vert index 262ee95..0d15ced 100644 --- a/src/viewer/assets/shaders/model_packed_normal_legacy.vert +++ b/src/viewer/assets/shaders/model_packed_normal_legacy.vert @@ -1,4 +1,6 @@ #version 450 +#pragma multi_usage inPackedTangentsLegacy + #include "packed_tangent.inc" // Constants @@ -11,7 +13,7 @@ layout( binding = 0 ) uniform PerFrame // Inputs layout( location = 0 ) in vec3 inPosition; -layout( location = 1 ) in vec4 inPackedTangentsLegacy; +layout( location = 1 ) in vec4 inPackedTangentsLegacy; // Outputs layout( location = 0 ) out vec3 viewPosition; diff --git a/src/viewer/assets/shaders/normalaxis.vert b/src/viewer/assets/shaders/normalaxis.vert index e37b1ea..6eb0712 100644 --- a/src/viewer/assets/shaders/normalaxis.vert +++ b/src/viewer/assets/shaders/normalaxis.vert @@ -1,4 +1,5 @@ #version 450 +#pragma multi_usage inNormal // Constants layout( binding = 0 ) uniform PerFrame @@ -16,8 +17,9 @@ layout( binding = 1 ) uniform AxisConfig } axisConfig; // Inputs -layout( location = 0 ) in vec3 inPosition; // per instance -layout( location = 1 ) in vec3 inNormal; // per instance +// inputs are per instance, not per vertex +layout( location = 0 ) in vec3 inPosition; +layout( location = 1 ) in vec3 inNormal; // Output layout ( location = 0 ) out vec3 outColor; diff --git a/src/viewer/assets/shaders/packedtangentaxis.vert b/src/viewer/assets/shaders/packedtangentaxis.vert index 1bae169..92f51b2 100644 --- a/src/viewer/assets/shaders/packedtangentaxis.vert +++ b/src/viewer/assets/shaders/packedtangentaxis.vert @@ -1,4 +1,6 @@ #version 450 +#pragma multi_usage inPackedTangents + #include "packed_tangent.inc" // Constants @@ -16,8 +18,9 @@ layout( binding = 1 ) uniform AxisConfig } axisConfig; // Inputs +// inputs are per instance, not per vertex layout( location = 0 ) in vec3 inPosition; -layout( location = 1 ) in vec4 inPackedTangents; +layout( location = 1 ) in vec4 inPackedTangents; // output layout ( location = 0 ) out vec3 outColor; diff --git a/src/viewer/assets/shaders/packedtangentlegacyaxis.vert b/src/viewer/assets/shaders/packedtangentlegacyaxis.vert index 7875794..ffe889d 100644 --- a/src/viewer/assets/shaders/packedtangentlegacyaxis.vert +++ b/src/viewer/assets/shaders/packedtangentlegacyaxis.vert @@ -1,4 +1,6 @@ #version 450 +#pragma multi_usage inPackedTangentsLegacy + #include "packed_tangent.inc" // Constants @@ -16,8 +18,9 @@ layout( binding = 1 ) uniform AxisConfig } axisConfig; // Inputs +// inputs are per instance, not per vertex layout( location = 0 ) in vec3 inPosition; -layout( location = 1 ) in vec4 inPackedTangentsLegacy; +layout( location = 1 ) in vec4 inPackedTangentsLegacy; // output layout ( location = 0 ) out vec3 outColor; diff --git a/src/viewer/assets/shaders/tangentaxis.vert b/src/viewer/assets/shaders/tangentaxis.vert index e8ef516..411a9a4 100644 --- a/src/viewer/assets/shaders/tangentaxis.vert +++ b/src/viewer/assets/shaders/tangentaxis.vert @@ -1,4 +1,5 @@ #version 450 +#pragma multi_usage inTangent // Constants layout( binding = 0 ) uniform PerFrame @@ -16,8 +17,9 @@ layout( binding = 1 ) uniform AxisConfig } axisConfig; // Inputs -layout( location = 0 ) in vec3 inPosition; // per instance -layout( location = 1 ) in vec3 inTangent; // per instance +// inputs are per instance, not per vertex +layout( location = 0 ) in vec3 inPosition; +layout( location = 1 ) in vec3 inTangent; // Output layout ( location = 0 ) out vec3 outColor; diff --git a/src/viewer/rendering/models/axis.cpp b/src/viewer/rendering/models/axis.cpp index 3296ea6..cb77abc 100644 --- a/src/viewer/rendering/models/axis.cpp +++ b/src/viewer/rendering/models/axis.cpp @@ -24,66 +24,64 @@ const std::array AXIS_MESH = { PrimitiveRenderable CreateOrientationPrimitive( std::shared_ptr renderer ) { - auto effect = PrimitiveEffects::CreateOrientationEffect( renderer ); - - auto model = PrimitiveRenderable( renderer, std::move( effect ) ); + auto model = PrimitiveRenderable( renderer, PrimitiveEffects::CreateOrientationEffect( renderer ) ); model.SetBufferData( reinterpret_cast( AXIS_MESH.data() ), (uint32_t)AXIS_MESH.size() * sizeof( AxisVertex ), sizeof( AxisVertex ) ); return model; } -PrimitiveRenderable CreateNormal( std::shared_ptr renderer, const cmf::Mesh& mesh ) +PrimitiveRenderable CreateNormal( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ) { // the bufferdata is the output of the geoprepass and will be handled later - return PrimitiveRenderable( renderer, PrimitiveEffects::CreateUnpackedAxisEffect( renderer, cmf::Usage::Normal, mesh ) ); + return PrimitiveRenderable( renderer, PrimitiveEffects::CreateUnpackedAxisEffect( renderer, cmf::Usage::Normal, usageIndex, mesh ) ); } -PrimitiveRenderable CreateTangent( std::shared_ptr renderer, const cmf::Mesh& mesh ) +PrimitiveRenderable CreateTangent( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ) { // the bufferdata is the output of the geoprepass and will be handled later - return PrimitiveRenderable( renderer, PrimitiveEffects::CreateUnpackedAxisEffect( renderer, cmf::Usage::Tangent, mesh ) ); + return PrimitiveRenderable( renderer, PrimitiveEffects::CreateUnpackedAxisEffect( renderer, cmf::Usage::Tangent, usageIndex, mesh ) ); } -PrimitiveRenderable CreateBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh ) +PrimitiveRenderable CreateBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ) { // the bufferdata is the output of the geoprepass and will be handled later - return PrimitiveRenderable( renderer, PrimitiveEffects::CreateUnpackedAxisEffect( renderer, cmf::Usage::Binormal, mesh ) ); + return PrimitiveRenderable( renderer, PrimitiveEffects::CreateUnpackedAxisEffect( renderer, cmf::Usage::Binormal, usageIndex, mesh ) ); } -PrimitiveRenderable CreatePackedNormal( std::shared_ptr renderer, const cmf::Mesh& mesh ) +PrimitiveRenderable CreatePackedNormal( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ) { // the bufferdata is the output of the geoprepass and will be handled later - return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedAxisEffect( renderer, cmf::Usage::Normal, mesh ) ); + return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedAxisEffect( renderer, cmf::Usage::Normal, usageIndex, mesh ) ); } -PrimitiveRenderable CreatePackedTangent( std::shared_ptr renderer, const cmf::Mesh& mesh ) +PrimitiveRenderable CreatePackedTangent( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ) { // the bufferdata is the output of the geoprepass and will be handled later - return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedAxisEffect( renderer, cmf::Usage::Tangent, mesh ) ); + return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedAxisEffect( renderer, cmf::Usage::Tangent, usageIndex,mesh ) ); } -PrimitiveRenderable CreatePackedBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh ) +PrimitiveRenderable CreatePackedBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ) { // the bufferdata is the output of the geoprepass and will be handled later - return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedAxisEffect( renderer, cmf::Usage::Binormal, mesh ) ); + return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedAxisEffect( renderer, cmf::Usage::Binormal, usageIndex, mesh ) ); } -PrimitiveRenderable CreatePackedLegacyNormal( std::shared_ptr renderer, const cmf::Mesh& mesh ) +PrimitiveRenderable CreatePackedLegacyNormal( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ) { // the bufferdata is the output of the geoprepass and will be handled later - return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedLegacyAxisEffect( renderer, cmf::Usage::Normal, mesh ) ); + return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedLegacyAxisEffect( renderer, cmf::Usage::Normal, usageIndex, mesh ) ); } -PrimitiveRenderable CreatePackedLegacyTangent( std::shared_ptr renderer, const cmf::Mesh& mesh ) +PrimitiveRenderable CreatePackedLegacyTangent( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ) { // the bufferdata is the output of the geoprepass and will be handled later - return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedLegacyAxisEffect( renderer, cmf::Usage::Tangent, mesh ) ); + return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedLegacyAxisEffect( renderer, cmf::Usage::Tangent, usageIndex, mesh ) ); } -PrimitiveRenderable CreatePackedLegacyBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh ) +PrimitiveRenderable CreatePackedLegacyBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ) { // the bufferdata is the output of the geoprepass and will be handled later - return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedLegacyAxisEffect( renderer, cmf::Usage::Binormal, mesh ) ); + return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedLegacyAxisEffect( renderer, cmf::Usage::Binormal, usageIndex, mesh ) ); } } diff --git a/src/viewer/rendering/models/axis.h b/src/viewer/rendering/models/axis.h index 6dd9575..aea2c57 100644 --- a/src/viewer/rendering/models/axis.h +++ b/src/viewer/rendering/models/axis.h @@ -10,13 +10,13 @@ namespace Axis PrimitiveRenderable CreateOrientationPrimitive( std::shared_ptr renderer ); -PrimitiveRenderable CreateNormal( std::shared_ptr renderer, const cmf::Mesh& mesh ); -PrimitiveRenderable CreateTangent( std::shared_ptr renderer, const cmf::Mesh& mesh ); -PrimitiveRenderable CreateBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh ); -PrimitiveRenderable CreatePackedNormal( std::shared_ptr renderer, const cmf::Mesh& mesh ); -PrimitiveRenderable CreatePackedTangent( std::shared_ptr renderer, const cmf::Mesh& mesh ); -PrimitiveRenderable CreatePackedBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh ); -PrimitiveRenderable CreatePackedLegacyNormal( std::shared_ptr renderer, const cmf::Mesh& mesh ); -PrimitiveRenderable CreatePackedLegacyTangent( std::shared_ptr renderer, const cmf::Mesh& mesh ); -PrimitiveRenderable CreatePackedLegacyBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh ); +PrimitiveRenderable CreateNormal( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ); +PrimitiveRenderable CreateTangent( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ); +PrimitiveRenderable CreateBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ); +PrimitiveRenderable CreatePackedNormal( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ); +PrimitiveRenderable CreatePackedTangent( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ); +PrimitiveRenderable CreatePackedBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ); +PrimitiveRenderable CreatePackedLegacyNormal( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ); +PrimitiveRenderable CreatePackedLegacyTangent( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ); +PrimitiveRenderable CreatePackedLegacyBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ); }; \ No newline at end of file diff --git a/src/viewer/rendering/models/bones.cpp b/src/viewer/rendering/models/bones.cpp index 445f3df..13b2f83 100644 --- a/src/viewer/rendering/models/bones.cpp +++ b/src/viewer/rendering/models/bones.cpp @@ -6,9 +6,9 @@ namespace Bones PrimitiveRenderable CreateJoint( std::shared_ptr renderer, const cmf::Skeleton& skeleton, Vector3 color, Vector3 selectedColor ) { - GraphicsEffect::Config config{}; + GraphicsEffectTypes::Config config{}; config.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - config.stride = sizeof( Vector4 ); + auto colorInfo = PrimitiveEffects::ColorInfo{ Vector4( color, 1.0f ), @@ -30,17 +30,9 @@ PrimitiveRenderable CreateJoint( std::shared_ptr renderer, const PrimitiveRenderable CreateBone( std::shared_ptr renderer, const cmf::Skeleton& skeleton, Vector3 color, Vector3 selectedColor ) { - GraphicsEffect::Config config{}; - config.availableVertexElements = { - { cmf::Usage::Position, - 0, - cmf::ElementType::Float32, - 4, - 0 } - }; + GraphicsEffectTypes::Config config{}; config.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; - config.stride = sizeof( Vector4 ); config.lineWidth = 1.5f; auto colorInfo = PrimitiveEffects::ColorInfo{ diff --git a/src/viewer/rendering/models/boundingBox.cpp b/src/viewer/rendering/models/boundingBox.cpp index 85f6f1c..20254ba 100644 --- a/src/viewer/rendering/models/boundingBox.cpp +++ b/src/viewer/rendering/models/boundingBox.cpp @@ -19,7 +19,7 @@ const std::array BOX_INDICES = { 0, 1, 0, 2, 0, 4, 3, 1, 3, 2, 3, PrimitiveRenderable Create( std::shared_ptr renderer, Vector3 color ) { - GraphicsEffect::Config config{}; + GraphicsEffectTypes::Config config{}; config.lineWidth = 2.0f; config.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; diff --git a/src/viewer/rendering/models/primitiveEffects.cpp b/src/viewer/rendering/models/primitiveEffects.cpp index 1019ed7..cf75a86 100644 --- a/src/viewer/rendering/models/primitiveEffects.cpp +++ b/src/viewer/rendering/models/primitiveEffects.cpp @@ -1,27 +1,52 @@ #include "primitiveEffects.h" #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace { constexpr float AXIS_LENGTH_SCALE = 0.005f; constexpr float AXIS_LENGTH_MIN_SIZE = 0.001f; -GraphicsEffect CreateAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ) +GraphicsEffect CreateAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, uint32_t usageIndex, const cmf::Mesh& mesh ) { - auto effectConfig = GraphicsEffect::Config(); + auto effectConfig = GraphicsEffectTypes::Config(); PrimitiveEffects::AxisConfig axisConfig{}; + auto inputDeclaration = GraphicsEffectTypes::ShaderInputDeclaration(); + inputDeclaration.vertexInputRate = VK_VERTEX_INPUT_RATE_INSTANCE; - uint32_t stride = 0; - // find the position and normal elements in the vertex declaration, for( const auto& element : mesh.decl ) { - stride += cmf::GetVertexElementSize( element ); - effectConfig.availableVertexElements.push_back( element ); + inputDeclaration.stride += cmf::GetVertexElementSize( element ); + if( element.usage == usage || element.usage == cmf::Usage::PackedTangent || element.usage == cmf::Usage::PackedTangentLegacy ) + { + if( element.usageIndex == usageIndex ) + { + inputDeclaration.vertexDeclarations.push_back( element ); + } + } + else + { + inputDeclaration.vertexDeclarations.push_back( element ); + } } + effectConfig.inputDeclaration = inputDeclaration; effectConfig.lineWidth = 2.0f; effectConfig.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; - effectConfig.stride = stride; - effectConfig.vertexInputRate = VK_VERTEX_INPUT_RATE_INSTANCE; assert( usage == cmf::Usage::Normal || usage == cmf::Usage::Tangent || usage == cmf::Usage::Binormal ); switch( usage ) @@ -53,16 +78,13 @@ GraphicsEffect CreateAxisEffect( std::shared_ptr renderer, const } } -GraphicsEffect PrimitiveEffects::CreateFlatColorEffect( std::shared_ptr renderer, ColorInfo colorInfo, GraphicsEffect::Config config, std::vector vertexToBoneMapping ) +GraphicsEffect PrimitiveEffects::CreateFlatColorEffect( std::shared_ptr renderer, ColorInfo colorInfo, GraphicsEffectTypes::Config config, std::vector vertexToBoneMapping ) { - config.availableVertexElements = { - { cmf::Usage::Position, - 0, - cmf::ElementType::Float32, - 4, - 0 } + config.inputDeclaration.stride = sizeof( Vector4 ); + config.inputDeclaration.vertexDeclarations = { + cmf::VertexElement{ cmf::Usage::Position, 0, cmf::ElementType::Float32, 4, 0 } }; - config.stride = sizeof( Vector4 ); + auto effect = GraphicsEffect( renderer ); effect.RegisterUniformData( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 0 ); effect.RegisterStorageBuffer( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 1, 0xFF ); // bone transforms @@ -76,8 +98,8 @@ GraphicsEffect PrimitiveEffects::CreateFlatColorEffect( std::shared_ptr renderer ) { - auto config = GraphicsEffect::Config(); - config.availableVertexElements = { + auto config = GraphicsEffectTypes::Config(); + config.inputDeclaration.vertexDeclarations = { { cmf::Usage::Position, 0, cmf::ElementType::Float32, @@ -89,9 +111,10 @@ GraphicsEffect PrimitiveEffects::CreateOrientationEffect( std::shared_ptr( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 0 ); effect.RegisterStorageBuffer( VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, 1, 0xFF ); @@ -100,7 +123,7 @@ GraphicsEffect PrimitiveEffects::CreateOrientationEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ) +GraphicsEffect PrimitiveEffects::CreateUnpackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, uint32_t usageIndex, const cmf::Mesh& mesh ) { std::string shaderName; switch( usage ) @@ -117,21 +140,21 @@ GraphicsEffect PrimitiveEffects::CreateUnpackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ) +GraphicsEffect PrimitiveEffects::CreatePackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, uint32_t usageIndex, const cmf::Mesh& mesh ) { - auto effect = CreateAxisEffect( renderer, usage, mesh ); + auto effect = CreateAxisEffect( renderer, usage, usageIndex, mesh ); effect.SetShaderName( "packedtangentaxis" ); return effect; } -GraphicsEffect PrimitiveEffects::CreatePackedLegacyAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ) +GraphicsEffect PrimitiveEffects::CreatePackedLegacyAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, uint32_t usageIndex, const cmf::Mesh& mesh ) { - auto effect = CreateAxisEffect( renderer, usage, mesh ); + auto effect = CreateAxisEffect( renderer, usage, usageIndex, mesh ); effect.SetShaderName( "packedtangentlegacyaxis" ); return effect; } \ No newline at end of file diff --git a/src/viewer/rendering/models/primitiveEffects.h b/src/viewer/rendering/models/primitiveEffects.h index 6c7d188..db39391 100644 --- a/src/viewer/rendering/models/primitiveEffects.h +++ b/src/viewer/rendering/models/primitiveEffects.h @@ -1,7 +1,7 @@ #pragma once #include "../vulkan/graphicseffect.h" -#include +#include "cmf/cmf.h" namespace PrimitiveEffects { @@ -27,10 +27,10 @@ struct ColorInfo Vector4 selected; }; -GraphicsEffect CreateFlatColorEffect( std::shared_ptr renderer, ColorInfo colorInfo, GraphicsEffect::Config config, std::vector vertexToBoneMapping ); +GraphicsEffect CreateFlatColorEffect( std::shared_ptr renderer, ColorInfo colorInfo, GraphicsEffectTypes::Config config, std::vector vertexToBoneMapping ); GraphicsEffect CreateOrientationEffect( std::shared_ptr renderer ); -GraphicsEffect CreateUnpackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ); -GraphicsEffect CreatePackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ); -GraphicsEffect CreatePackedLegacyAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, const cmf::Mesh& mesh ); +GraphicsEffect CreateUnpackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, uint32_t usageIndex, const cmf::Mesh& mesh ); +GraphicsEffect CreatePackedAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, uint32_t usageIndex, const cmf::Mesh& mesh ); +GraphicsEffect CreatePackedLegacyAxisEffect( std::shared_ptr renderer, const cmf::Usage usage, uint32_t usageIndex, const cmf::Mesh& mesh ); }; \ No newline at end of file diff --git a/src/viewer/rendering/renderable/mesh.cpp b/src/viewer/rendering/renderable/mesh.cpp index 44268e3..7c604fa 100644 --- a/src/viewer/rendering/renderable/mesh.cpp +++ b/src/viewer/rendering/renderable/mesh.cpp @@ -16,35 +16,6 @@ MeshRenderable::MeshRenderable( std::shared_ptr data, const cmf::Mes m_audioOcclusionRenderable( renderer, GetAudioOcclusionEffect( renderer, cmfMesh ) ), m_boundingBox( BoundingBox::Create( renderer, Vector3( 0.5, 0.5, 0.0 ) ) ) { - for( const auto& vertexElement : m_cmfMesh.decl ) - { - m_availableVertexElements.push_back( vertexElement ); - if( vertexElement.usage == cmf::Usage::Normal ) - { - m_normalAxisRenderable = std::make_unique( Axis::CreateNormal( renderer, m_cmfMesh ) ); - } - else if( vertexElement.usage == cmf::Usage::Tangent ) - { - m_tangentAxisRenderable = std::make_unique( Axis::CreateTangent( renderer, m_cmfMesh ) ); - } - else if( vertexElement.usage == cmf::Usage::Binormal ) - { - m_binormalAxisRenderable = std::make_unique( Axis::CreateBinormal( renderer, m_cmfMesh ) ); - } - else if( vertexElement.usage == cmf::Usage::PackedTangent ) - { - m_normalAxisRenderable = std::make_unique( Axis::CreatePackedNormal( renderer, m_cmfMesh ) ); - m_tangentAxisRenderable = std::make_unique( Axis::CreatePackedTangent( renderer, m_cmfMesh ) ); - m_binormalAxisRenderable = std::make_unique( Axis::CreatePackedBinormal( renderer, m_cmfMesh ) ); - } - else if( vertexElement.usage == cmf::Usage::PackedTangentLegacy ) - { - m_normalAxisRenderable = std::make_unique( Axis::CreatePackedLegacyNormal( renderer, m_cmfMesh ) ); - m_tangentAxisRenderable = std::make_unique( Axis::CreatePackedLegacyTangent( renderer, m_cmfMesh ) ); - m_binormalAxisRenderable = std::make_unique( Axis::CreatePackedLegacyBinormal( renderer, m_cmfMesh ) ); - } - } - m_boundingSphere = CcpMath::Sphere( m_cmfMesh.bounds ); m_boundingBoxTransform = ScalingMatrix( m_cmfMesh.bounds.Size() ) * TranslationMatrix( m_cmfMesh.bounds.Center() ); @@ -72,11 +43,14 @@ void MeshRenderable::Initialize( AppState& appState ) m_prepass.Initialize( appState ); appState.modelState.polygonMode.RegisterCallback( [this]( VkPolygonMode mode, AppState& appState ) { - SetRenderingMode( appState.modelState.visualizationShader.GetValue(), mode ); + auto [name, declaration] = appState.modelState.activeShader.GetValue(); + SetRenderingMode( name, declaration, mode ); } ); - appState.modelState.visualizationShader.RegisterCallback( [this]( std::string shaderName, AppState& appState ) { - SetRenderingMode( shaderName, appState.modelState.polygonMode.GetValue() ); + appState.modelState.activeShader.RegisterCallback( [this]( std::pair shaderInputDeclaration, AppState& appState ) { + auto [name, declaration] = shaderInputDeclaration; + + SetRenderingMode( name, declaration, appState.modelState.polygonMode.GetValue() ); } ); m_meshIndex = appState.modelState.meshes.AddState( [this]( MeshState& meshState ) { @@ -103,6 +77,45 @@ void MeshRenderable::Initialize( AppState& appState ) m_prepass.SetMorphWeight( static_cast( i ), morph.second ? morph.first : 0.0f ); } ); } + for( const auto& vertexElement : m_cmfMesh.decl ) + { + m_availableVertexElements.push_back( vertexElement ); + if( vertexElement.usage == cmf::Usage::Normal ) + { + meshState.showVertexNormals.AddState( { vertexElement.usageIndex, false } ); + m_normalAxisRenderables.push_back( Axis::CreateNormal( m_renderer, m_cmfMesh, vertexElement.usageIndex ) ); + } + else if( vertexElement.usage == cmf::Usage::Tangent ) + { + meshState.showVertexTangents.AddState( { vertexElement.usageIndex, false } ); + m_tangentAxisRenderables.push_back( Axis::CreateTangent( m_renderer, m_cmfMesh, vertexElement.usageIndex ) ); + } + else if( vertexElement.usage == cmf::Usage::Binormal ) + { + meshState.showVertexBinormals.AddState( { vertexElement.usageIndex, false } ); + m_binormalAxisRenderables.push_back( Axis::CreateBinormal( m_renderer, m_cmfMesh, vertexElement.usageIndex ) ); + } + else if( vertexElement.usage == cmf::Usage::PackedTangent ) + { + meshState.showVertexNormals.AddState( { vertexElement.usageIndex, false } ); + meshState.showVertexTangents.AddState( { vertexElement.usageIndex, false } ); + meshState.showVertexBinormals.AddState( { vertexElement.usageIndex, false } ); + + m_normalAxisRenderables.push_back( Axis::CreatePackedNormal( m_renderer, m_cmfMesh, vertexElement.usageIndex ) ); + m_tangentAxisRenderables.push_back( Axis::CreatePackedTangent( m_renderer, m_cmfMesh, vertexElement.usageIndex ) ); + m_binormalAxisRenderables.push_back( Axis::CreatePackedBinormal( m_renderer, m_cmfMesh, vertexElement.usageIndex ) ); + } + else if( vertexElement.usage == cmf::Usage::PackedTangentLegacy ) + { + meshState.showVertexNormals.AddState( { vertexElement.usageIndex, false } ); + meshState.showVertexTangents.AddState( { vertexElement.usageIndex, false } ); + meshState.showVertexBinormals.AddState( { vertexElement.usageIndex, false } ); + + m_normalAxisRenderables.push_back( Axis::CreatePackedLegacyNormal( m_renderer, m_cmfMesh, vertexElement.usageIndex ) ); + m_tangentAxisRenderables.push_back( Axis::CreatePackedLegacyTangent( m_renderer, m_cmfMesh, vertexElement.usageIndex ) ); + m_binormalAxisRenderables.push_back( Axis::CreatePackedLegacyBinormal( m_renderer, m_cmfMesh, vertexElement.usageIndex ) ); + } + } } ); appState.modelState.selectedLod.RegisterCallback( [this]( int32_t lodIndex, AppState& appState ) { @@ -127,17 +140,17 @@ void MeshRenderable::Initialize( AppState& appState ) m_audioOcclusionRenderable.Initialize(); } - if( m_normalAxisRenderable ) + for( auto& normalAxisRenderable : m_normalAxisRenderables ) { - m_normalAxisRenderable->Initialize(); + normalAxisRenderable.Initialize(); } - if( m_tangentAxisRenderable ) + for( auto& tangentAxisRenderable : m_tangentAxisRenderables ) { - m_tangentAxisRenderable->Initialize(); + tangentAxisRenderable.Initialize(); } - if( m_binormalAxisRenderable ) + for( auto& binormalAxisRenderable : m_binormalAxisRenderables ) { - m_binormalAxisRenderable->Initialize(); + binormalAxisRenderable.Initialize(); } m_initialized = true; } @@ -296,21 +309,42 @@ void MeshRenderable::RenderDebug( GraphicsCommandBuffer& commandBuffer, const Ap } const auto& meshState = appState.modelState.meshes[m_meshIndex].GetValue(); - if( m_tangentAxisRenderable && meshState.showVertexTangents.GetValue() ) + auto streamElementCount = cmf::GetStreamElementCount( m_cmfMesh.lods[m_currentLod].vb ); + const auto* buffer = &( m_prepass.GetVertexBuffer() ); + uint32_t index = 0; + for( const auto& normalState : meshState.showVertexNormals ) { - m_tangentAxisRenderable->SetUniformData( 0, viewProj ); - m_tangentAxisRenderable->Render( commandBuffer, &( m_prepass.GetVertexBuffer() ), nullptr, 2, cmf::GetStreamElementCount( m_cmfMesh.lods[m_currentLod].vb ) ); + if( normalState.GetValue().second ) + { + // since the state and renderables are created in the same order based on the vertex elements, + // we can use the index to get the corresponding renderable for the normal/tangent/binormal we want to render + m_normalAxisRenderables[index].SetUniformData( 0, viewProj ); + m_normalAxisRenderables[index].Render( commandBuffer, buffer, nullptr, 2, streamElementCount ); + } + ++index; } - if( m_normalAxisRenderable && meshState.showVertexNormals.GetValue() ) + index = 0; + for( const auto& tangentState : meshState.showVertexTangents ) { - m_normalAxisRenderable->SetUniformData( 0, viewProj ); - m_normalAxisRenderable->Render( commandBuffer, &( m_prepass.GetVertexBuffer() ), nullptr, 2, cmf::GetStreamElementCount( m_cmfMesh.lods[m_currentLod].vb ) ); + if( tangentState.GetValue().second ) + { + m_tangentAxisRenderables[index].SetUniformData( 0, viewProj ); + m_tangentAxisRenderables[index].Render( commandBuffer, buffer, nullptr, 2, streamElementCount ); + } + ++index; } - if( m_binormalAxisRenderable && meshState.showVertexBinormals.GetValue() ) + + index = 0; + for( const auto& binormalState : meshState.showVertexBinormals ) { - m_binormalAxisRenderable->SetUniformData( 0, viewProj ); - m_binormalAxisRenderable->Render( commandBuffer, &( m_prepass.GetVertexBuffer() ), nullptr, 2, cmf::GetStreamElementCount( m_cmfMesh.lods[m_currentLod].vb ) ); + if( binormalState.GetValue().second ) + { + m_binormalAxisRenderables[index].SetUniformData( 0, viewProj ); + m_binormalAxisRenderables[index].Render( commandBuffer, buffer, nullptr, 2, streamElementCount ); + } + ++index; } + } void MeshRenderable::PrepareMesh( ComputeCommandBuffer& commandBuffer ) @@ -341,7 +375,7 @@ void MeshRenderable::DrawIndexed( GraphicsCommandBuffer& commandBuffer ) } } -VkResult MeshRenderable::SetRenderingMode( std::string shaderName, VkPolygonMode polygonMode ) +VkResult MeshRenderable::SetRenderingMode( std::string shaderName, GraphicsEffectTypes::ShaderInputDeclaration shaderInputDeclaration, VkPolygonMode polygonMode ) { auto logicalDevice = m_renderer->GetDevice()->GetLogicalDevice(); @@ -350,12 +384,12 @@ VkResult MeshRenderable::SetRenderingMode( std::string shaderName, VkPolygonMode CR_RETURN( vkDeviceWaitIdle( logicalDevice ) ); - auto config = GraphicsEffect::Config(); + auto config = GraphicsEffectTypes::Config(); config.topology = m_topology; config.polygonMode = polygonMode; config.cullMode = ( polygonMode == VK_POLYGON_MODE_FILL ) ? VK_CULL_MODE_BACK_BIT : VK_CULL_MODE_NONE; - config.availableVertexElements = m_availableVertexElements; - config.stride = m_stride; + config.inputDeclaration.vertexDeclarations = shaderInputDeclaration.vertexDeclarations; + config.inputDeclaration.stride = m_stride; m_modelEffect.SetShaderName( m_shaderName ); m_modelEffect.SetConfig( config ); @@ -367,7 +401,7 @@ VkResult MeshRenderable::SetRenderingMode( std::string shaderName, VkPolygonMode if( !m_wireframeEffect.IsInitialized() ) { - auto wireframeConfig = GraphicsEffect::Config(); + auto wireframeConfig = GraphicsEffectTypes::Config(); wireframeConfig.topology = m_topology; // use fill mode even though we are rendering wireframe // The reason is when we rasterize the lines we will get issues with the depth buffer where some lines @@ -377,8 +411,12 @@ VkResult MeshRenderable::SetRenderingMode( std::string shaderName, VkPolygonMode wireframeConfig.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; wireframeConfig.cullMode = VK_CULL_MODE_NONE; wireframeConfig.blend = true; - wireframeConfig.availableVertexElements = m_availableVertexElements; - wireframeConfig.stride = m_stride; + wireframeConfig.inputDeclaration.stride = m_stride; + wireframeConfig.inputDeclaration.vertexDeclarations = { { cmf::Usage::Position, + 0, + cmf::ElementType::Float32, + 3, + 0 } }; m_wireframeEffect.SetShaderName( "wireframeoverlay" ); m_wireframeEffect.SetConfig( wireframeConfig ); @@ -390,13 +428,13 @@ VkResult MeshRenderable::SetRenderingMode( std::string shaderName, VkPolygonMode GraphicsEffect MeshRenderable::GetAudioOcclusionEffect( std::shared_ptr renderer, const cmf::Mesh& cmfMesh ) { - auto config = GraphicsEffect::Config(); + auto config = GraphicsEffectTypes::Config(); config.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; config.polygonMode = VK_POLYGON_MODE_FILL; config.cullMode = VK_CULL_MODE_NONE; config.blend = false; - config.stride = sizeof( Vector3 ); - config.availableVertexElements = { + config.inputDeclaration.stride = sizeof( Vector3 ); + config.inputDeclaration.vertexDeclarations = { cmf::VertexElement{ cmf::Usage::Position, 0, diff --git a/src/viewer/rendering/renderable/mesh.h b/src/viewer/rendering/renderable/mesh.h index 605443b..d1a0ff0 100644 --- a/src/viewer/rendering/renderable/mesh.h +++ b/src/viewer/rendering/renderable/mesh.h @@ -20,7 +20,7 @@ class MeshRenderable void Render( GraphicsCommandBuffer& commandBuffer, const AppState& appState, const Camera& camera ); void RenderDebug( GraphicsCommandBuffer& commandBuffer, const AppState& appState, const Camera& camera ); void PrepareMesh( ComputeCommandBuffer& computeCommandBuffer ); - VkResult SetRenderingMode( std::string shaderName, VkPolygonMode polygonMode ); + VkResult SetRenderingMode( std::string shaderName, GraphicsEffectTypes::ShaderInputDeclaration shaderInputDeclaration, VkPolygonMode polygonMode ); void UpdateMeshCurves( float animationTime, const cmf::Animation* animation, AppState& appState ); void SetSkeletonPose( const std::array& boneTransforms ); @@ -73,9 +73,9 @@ class MeshRenderable PrimitiveRenderable m_boundingBox; Matrix m_boundingBoxTransform{}; - std::unique_ptr m_normalAxisRenderable; - std::unique_ptr m_tangentAxisRenderable; - std::unique_ptr m_binormalAxisRenderable; + std::vector m_normalAxisRenderables; + std::vector m_tangentAxisRenderables; + std::vector m_binormalAxisRenderables; // audio occlusion PrimitiveRenderable m_audioOcclusionRenderable; diff --git a/src/viewer/rendering/sceneRenderer.cpp b/src/viewer/rendering/sceneRenderer.cpp index 048934c..f2a84db 100644 --- a/src/viewer/rendering/sceneRenderer.cpp +++ b/src/viewer/rendering/sceneRenderer.cpp @@ -3,6 +3,7 @@ #include "vulkan/shadercache.h" #include "vulkan/vulkanerrors.h" #include "vulkan/vulkanenums.h" +#include "vulkan/graphicseffecttypes.h" namespace { @@ -114,7 +115,7 @@ void SceneRenderer::SetData( std::shared_ptr data, AppState& appStat } // reset the visualization shader if it is not set or not applicable for the current model - std::string currentShaderName = appState.modelState.visualizationShader.GetValue(); + auto [currentShaderName, currentShaderDeclaration] = appState.modelState.activeShader.GetValue(); std::vector availableVertexElements; for( const auto& mesh : data->m_cmfData->meshes ) @@ -122,31 +123,31 @@ void SceneRenderer::SetData( std::shared_ptr data, AppState& appStat // add all of the declarations, there may be situations where one mesh has more declarations than another inside the same model availableVertexElements.insert( availableVertexElements.end(), mesh.decl.begin(), mesh.decl.end() ); } - - auto shaderNames = ShaderCache::GetAvailableShaderNames( availableVertexElements ); - auto foundItem = std::find_if( shaderNames.begin(), shaderNames.end(), [&]( auto name ) { - return name == currentShaderName; + + std::vector> shaders = ShaderCache::GetAvailableShaders( availableVertexElements ); + auto foundItem = std::find_if( shaders.begin(), shaders.end(), [&]( const auto& shaderNameAndDeclarations ) { + return shaderNameAndDeclarations.first == currentShaderName; } ); - - if( foundItem == shaderNames.end() && shaderNames.size() > 0 ) + if( foundItem == shaders.end() && shaders.size() > 0 ) { // pick the shader that best matches our default shader order - auto defaultShaderIt = std::find_if( DEFAULT_SHADER_ORDER.begin(), DEFAULT_SHADER_ORDER.end(), [&]( auto defaultShaderName ) { - return std::find_if( shaderNames.begin(), shaderNames.end(), [&]( auto shaderName ) { - return shaderName == defaultShaderName; - } ) != shaderNames.end(); + auto defaultShaderIt = std::find_if( shaders.begin(), shaders.end(), [&]( auto shaderNameAndDeclarations ) { + return std::find_if( DEFAULT_SHADER_ORDER.begin(), DEFAULT_SHADER_ORDER.end(), [&]( auto defaultShaderName ) { + return shaderNameAndDeclarations.first == defaultShaderName; + } ) != DEFAULT_SHADER_ORDER.end(); } ); - if( defaultShaderIt != DEFAULT_SHADER_ORDER.end() ) + + if( defaultShaderIt != shaders.end() ) { - appState.modelState.visualizationShader.SetValue( *defaultShaderIt ); + appState.modelState.activeShader.SetValue( *defaultShaderIt ); } else { - appState.modelState.visualizationShader.SetValue( shaderNames[0] ); + appState.modelState.activeShader.SetValue( shaders[0] ); } } - appState.modelState.availableShaders.SetValue( shaderNames ); - + appState.modelState.availableShaders.SetValue( shaders ); + m_model.reset( new ModelRenderable( data, m_renderer ) ); m_model->Initialize( appState ); } diff --git a/src/viewer/rendering/uiRenderer.cpp b/src/viewer/rendering/uiRenderer.cpp index d8c9e43..054bd23 100644 --- a/src/viewer/rendering/uiRenderer.cpp +++ b/src/viewer/rendering/uiRenderer.cpp @@ -1,5 +1,6 @@ #include "uiRenderer.h" +#include #include #include #include @@ -9,6 +10,7 @@ #include "appState.h" #include "vulkan/vulkanerrors.h" + const float MENU_BAR_HEIGHT = 18.0f; const float ANIMATION_PLAYER_HEIGHT = 36.0f; const float BUTTON_SIZE = 18.0f; @@ -253,7 +255,7 @@ void UIRenderer::CMFInfoWindow( AppState& appState ) void UIRenderer::MeshDetailsWindow( AppState& appState ) { bool open = true; - + ImGui::ShowDemoWindow( &open ); float width = (float)appState.windowSize.GetValue().first; float height = (float)appState.windowSize.GetValue().second; @@ -441,7 +443,7 @@ void UIRenderer::SetupGeneralView( AppState& appState ) ImGui::Text( "Visualization" ); ImGui::TableNextColumn(); ImGui::SetNextItemWidth( ImGui::GetContentRegionAvail().x ); - SetupCombo( "##visualiationMode", m_uiState.visualizationShaderComboBox, appState.modelState.visualizationShader ); + SetupCombo( "##visualiationMode", m_uiState.visualizationShaderComboBox, appState.modelState.activeShader ); ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -499,7 +501,7 @@ void UIRenderer::SetupGeneralView( AppState& appState ) } ); ImGui::TableNextRow(); ImGui::EndDisabled(); - + /* bool hasNormals = std::any_of( m_uiState.modelStates.meshes.begin(), m_uiState.modelStates.meshes.end(), []( const MeshUiState& state ) { return state.hasNormals; } ); @@ -560,7 +562,7 @@ void UIRenderer::SetupGeneralView( AppState& appState ) } ); } ); ImGui::EndDisabled(); - + */ ImGui::EndTable(); } } @@ -582,6 +584,71 @@ void UIRenderer::SetupMeshListView( const ModelUiState& modelState, AppState& ap } } +void UIRenderer::SetupVertexAxisRows( MeshState& meshAppState ) +{ + auto usageIndexChecker = []( const State>& pair1, const State>& pair2 ) { + return pair1.GetValue().first < pair2.GetValue().first; + }; + + auto maxNormalIndexState = std::max_element( meshAppState.showVertexNormals.begin(), meshAppState.showVertexNormals.end(), usageIndexChecker ); + auto maxTangentIndexState = std::max_element( meshAppState.showVertexTangents.begin(), meshAppState.showVertexTangents.end(), usageIndexChecker ); + auto maxBitangentIndexState = std::max_element( meshAppState.showVertexBinormals.begin(), meshAppState.showVertexBinormals.end(), usageIndexChecker ); + + auto maxNormalIndex = maxNormalIndexState != meshAppState.showVertexNormals.end() ? maxNormalIndexState->GetValue().first : -1; + auto maxTangentIndex = maxTangentIndexState != meshAppState.showVertexTangents.end() ? maxTangentIndexState->GetValue().first : -1; + auto maxBitangentIndex = maxBitangentIndexState != meshAppState.showVertexBinormals.end() ? maxBitangentIndexState->GetValue().first : -1; + + auto maxIndex = std::max( { maxNormalIndex, maxTangentIndex, maxBitangentIndex } ) + 1; + + SetupVertexAxisRow( meshAppState.showVertexNormals, "Normals", maxIndex ); + SetupVertexAxisRow( meshAppState.showVertexTangents, "Tangents", maxIndex ); + SetupVertexAxisRow( meshAppState.showVertexBinormals, "Bitangents", maxIndex ); +} + +void UIRenderer::SetupVertexAxisRow( StateCollection>& vertexAxisStates, const char* label, int columnCount ) +{ + // table with the first column being the label and the rest being checkboxes for each attribute index + if( vertexAxisStates.empty() ) + { + ImGui::BeginDisabled(); + } + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text( label ); + ImGui::TableNextColumn(); + + if( columnCount >= 0 ) + { + for( int32_t i = 0; i < columnCount; ++i ) + { + // find the states that have the index of the column (f.ex there is nothing stopping a mesh having one color attribute that is using usageindex at 16) + auto foundElement = std::find_if( vertexAxisStates.begin(), vertexAxisStates.end(), [i]( const State>& state ) { + return state.GetValue().first == i; + } ); + + if( foundElement != vertexAxisStates.end() ) + { + bool changed = ImGui::Checkbox( ( std::string( "##" ) + label + std::to_string( i ) ).c_str(), &foundElement->GetValue().second ); + + OnChange( changed, [foundElement]() { + foundElement->SetValue( { foundElement->GetValue().first, foundElement->GetValue().second } ); + } ); + + ImGui::SetItemTooltip( "%s %d", label, foundElement->GetValue().first ); + ImGui::SameLine(); + } + else + { + ImGui::SameLine( 18.0f ); + } + } + ImGui::NewLine(); + } + if( vertexAxisStates.empty() ) + { + ImGui::BeginDisabled(); + } +} void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) { @@ -589,6 +656,7 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) { if( ImGui::BeginTable( "##table", 2 ) ) { + auto& meshAppState = appState.modelState.meshes[mesh.meshIndex].GetValue(); ImGui::TableSetupColumn( "", ImGuiTableColumnFlags_WidthFixed ); ImGui::TableSetupColumn( "", ImGuiTableColumnFlags_WidthStretch ); ImGui::TableNextRow(); @@ -630,8 +698,8 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) ImGui::TableNextColumn(); bool display = mesh.display; - OnChange( ImGui::Checkbox( "##displaycheckbox", &display ), [&appState, &mesh, &display]() { - appState.modelState.meshes[mesh.meshIndex].GetValue().display.SetValue( display ); + OnChange( ImGui::Checkbox( "##displaycheckbox", &display ), [&meshAppState, &display]() { + meshAppState.display.SetValue( display ); } ); ImGui::TableNextRow(); @@ -641,8 +709,8 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) ImGui::TableNextColumn(); bool boundingBox = mesh.boundingBox; - OnChange( ImGui::Checkbox( "##boundingboxcheckbox", &boundingBox ), [&appState, &mesh, &boundingBox]() { - appState.modelState.meshes[mesh.meshIndex].GetValue().renderBoundingBox.SetValue( boundingBox ); + OnChange( ImGui::Checkbox( "##boundingboxcheckbox", &boundingBox ), [&meshAppState, &boundingBox]() { + meshAppState.renderBoundingBox.SetValue( boundingBox ); } ); ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -651,8 +719,8 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) ImGui::TableNextColumn(); bool wireframeOverlay = mesh.wireframeOverlay; - OnChange( ImGui::Checkbox( "##wireframeoverlaycheckbox", &wireframeOverlay ), [&appState, &mesh, &wireframeOverlay]() { - appState.modelState.meshes[mesh.meshIndex].GetValue().wireframeOverlay.SetValue( wireframeOverlay ); + OnChange( ImGui::Checkbox( "##wireframeoverlaycheckbox", &wireframeOverlay ), [&meshAppState, &wireframeOverlay]() { + meshAppState.wireframeOverlay.SetValue( wireframeOverlay ); } ); ImGui::TableNextRow(); @@ -662,47 +730,13 @@ void UIRenderer::SetupMeshView( const MeshUiState& mesh, AppState& appState ) ImGui::SetItemTooltip( "Toggles the audio occlusion mesh rendering for the \"%s\" mesh", mesh.name.c_str() ); ImGui::TableNextColumn(); bool audioOcclusion = mesh.audioOcclusionMesh; - OnChange( ImGui::Checkbox( "##audioocclusionmeshcheckbox", &audioOcclusion ), [&appState, &mesh, &audioOcclusion]() { - appState.modelState.meshes[mesh.meshIndex].GetValue().audioOcclusionMesh.SetValue( audioOcclusion ); - } ); - ImGui::EndDisabled(); - - ImGui::BeginDisabled( !mesh.hasNormals ); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text( "Normals" ); - ImGui::SetItemTooltip( "Toggles the normals" ); - ImGui::TableNextColumn(); - bool normals = mesh.showVertexNormals; - OnChange( ImGui::Checkbox( "##vertexnormals", &normals ), [&appState, &mesh, &normals]() { - appState.modelState.meshes[mesh.meshIndex].GetValue().showVertexNormals.SetValue( normals ); + OnChange( ImGui::Checkbox( "##audioocclusionmeshcheckbox", &audioOcclusion ), [&meshAppState, &audioOcclusion]() { + meshAppState.audioOcclusionMesh.SetValue( audioOcclusion ); } ); ImGui::EndDisabled(); - ImGui::BeginDisabled( !mesh.hasTangents ); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text( "Tangents" ); - ImGui::SetItemTooltip( "Toggles the tangents" ); - ImGui::TableNextColumn(); - bool tangents = mesh.showVertexTangents; - OnChange( ImGui::Checkbox( "##vertextangents", &tangents ), [&appState, &mesh, &tangents]() { - appState.modelState.meshes[mesh.meshIndex].GetValue().showVertexTangents.SetValue( tangents ); - } ); - - ImGui::EndDisabled(); + SetupVertexAxisRows( meshAppState ); - ImGui::BeginDisabled( !mesh.hasBinormals ); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text( "Bitangents" ); - ImGui::SetItemTooltip( "Toggles the bitangents" ); - ImGui::TableNextColumn(); - bool bitangents = mesh.showVertexBinormals; - OnChange( ImGui::Checkbox( "##vertexbitangents", &bitangents ), [&appState, &mesh, &bitangents]() { - appState.modelState.meshes[mesh.meshIndex].GetValue().showVertexBinormals.SetValue( bitangents ); - } ); - ImGui::EndDisabled(); ImGui::EndTable(); std::string header = "Morph Targets (" + std::to_string( mesh.morphTargets.size() ) + ")"; @@ -1223,18 +1257,6 @@ void UIRenderer::UpdateUiState( AppState& appState ) meshState.audioOcclusionMesh = meshAppState.audioOcclusionMesh.GetValue(); meshState.hasAudioOcclusionMesh = !mesh.audioOcclusionMesh.vertices.empty() && !mesh.audioOcclusionMesh.indices.empty(); meshState.boundingBox = meshAppState.renderBoundingBox.GetValue(); - meshState.showVertexNormals = meshAppState.showVertexNormals.GetValue(); - meshState.showVertexBinormals = meshAppState.showVertexBinormals.GetValue(); - meshState.showVertexTangents = meshAppState.showVertexTangents.GetValue(); - meshState.hasNormals = std::any_of( mesh.decl.begin(), mesh.decl.end(), []( const cmf::VertexElement& element ) { - return element.usage == cmf::Usage::Normal || element.usage == cmf::Usage::PackedTangent || element.usage == cmf::Usage::PackedTangentLegacy; - } ); - meshState.hasBinormals = std::any_of( mesh.decl.begin(), mesh.decl.end(), []( const cmf::VertexElement& element ) { - return element.usage == cmf::Usage::Binormal || element.usage == cmf::Usage::PackedTangent || element.usage == cmf::Usage::PackedTangentLegacy; - } ); - meshState.hasTangents = std::any_of( mesh.decl.begin(), mesh.decl.end(), []( const cmf::VertexElement& element ) { - return element.usage == cmf::Usage::Tangent || element.usage == cmf::Usage::PackedTangent || element.usage == cmf::Usage::PackedTangentLegacy; - } ); maxLod = std::max( maxLod, mesh.lods.size() ); meshState.vertexCount = 0; @@ -1276,11 +1298,17 @@ void UIRenderer::UpdateUiState( AppState& appState ) } // visualization shader selection - for( const auto& shaderName : appState.modelState.availableShaders.GetValue() ) + for( const auto& shaderSetupInfo : appState.modelState.availableShaders.GetValue() ) { - m_uiState.visualizationShaderComboBox.items.push_back( { shaderName, shaderName } ); + std::string shaderName = shaderSetupInfo.first; + if( !shaderSetupInfo.second.shaderNameAddition.empty() ) + { + shaderName += " " + shaderSetupInfo.second.shaderNameAddition; + } + + m_uiState.visualizationShaderComboBox.items.push_back( { shaderName, shaderSetupInfo } ); } - m_uiState.visualizationShaderComboBox.SetSelectedItemByValue( appState.modelState.visualizationShader.GetValue() ); + m_uiState.visualizationShaderComboBox.SetSelectedItemByValue( appState.modelState.activeShader.GetValue() ); m_uiState.modelStates.lod.items.push_back( std::make_pair( "Auto", -1 ) ); @@ -1332,7 +1360,7 @@ void UIRenderer::UpdateUiState( AppState& appState ) m_uiState.filePath = "No file loaded"; // visualization shader selection - m_uiState.visualizationShaderComboBox.items.push_back( { "", "" } ); + m_uiState.visualizationShaderComboBox.items.push_back( { "", {} } ); } } diff --git a/src/viewer/rendering/uiRenderer.h b/src/viewer/rendering/uiRenderer.h index 791a8d0..0d3ade3 100644 --- a/src/viewer/rendering/uiRenderer.h +++ b/src/viewer/rendering/uiRenderer.h @@ -68,12 +68,9 @@ class UIRenderer bool wireframeOverlay{ false }; bool audioOcclusionMesh{ false }; bool hasAudioOcclusionMesh{ false }; - bool showVertexNormals{ false }; - bool showVertexTangents{ false }; - bool showVertexBinormals{ false }; - bool hasNormals{ false }; - bool hasTangents{ false }; - bool hasBinormals{ false }; + std::vector> showVertexNormals{ }; + std::vector> showVertexTangents{ }; + std::vector> showVertexBinormals{ }; }; struct ModelUiState @@ -105,7 +102,7 @@ class UIRenderer ModelUiState modelStates{}; std::vector skeletonOwners{}; CmfUiComboBox polygonModeComboBox; - CmfUiComboBox visualizationShaderComboBox; + CmfUiComboBox> visualizationShaderComboBox; bool boneDebug{ false }; bool jointDebug{ false }; bool jointAxisDebug{ false }; @@ -148,6 +145,8 @@ class UIRenderer void SetupGeneralView( AppState& appState ); void SetupMeshListView( const ModelUiState& modelState, AppState& appState ); void SetupMeshView( const MeshUiState& mesh, AppState& appState ); + void SetupVertexAxisRows( MeshState& meshAppState ); + void SetupVertexAxisRow( StateCollection>& vertexAxisStates, const char* label, int columnCount ); void SetupMorphTarget( const MorphTargetUiState& morphTarget, size_t meshIndex, AppState& appState ); void SetupSkeletonOwners( const std::vector& skeletonOwners, AppState& appState ); void SetupSkeletons( const std::vector& skeletonStates, AppState& appState ); diff --git a/src/viewer/rendering/vulkan/graphicseffect.cpp b/src/viewer/rendering/vulkan/graphicseffect.cpp index a39d907..d448b13 100644 --- a/src/viewer/rendering/vulkan/graphicseffect.cpp +++ b/src/viewer/rendering/vulkan/graphicseffect.cpp @@ -9,7 +9,7 @@ GraphicsEffect::GraphicsEffect( std::shared_ptr renderer ) : { } -void GraphicsEffect::SetConfig( GraphicsEffect::Config config ) +void GraphicsEffect::SetConfig( GraphicsEffectTypes::Config config ) { m_config = config; @@ -37,5 +37,5 @@ void GraphicsEffect::Bind( VkCommandBuffer commandBuffer, uint32_t currentFrameI size_t GraphicsEffect::GetStride() const { - return m_config.stride; + return m_config.inputDeclaration.stride; } diff --git a/src/viewer/rendering/vulkan/graphicseffect.h b/src/viewer/rendering/vulkan/graphicseffect.h index 0089972..006e7a3 100644 --- a/src/viewer/rendering/vulkan/graphicseffect.h +++ b/src/viewer/rendering/vulkan/graphicseffect.h @@ -1,6 +1,7 @@ #pragma once #include "../renderer.h" #include "effect.h" +#include "graphicseffecttypes.h" class GraphicsEffect : public Effect { @@ -13,20 +14,7 @@ class GraphicsEffect : public Effect Matrix view; }; - struct Config - { - VkPrimitiveTopology topology{ VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST }; - VkPolygonMode polygonMode{ VK_POLYGON_MODE_FILL }; - float lineWidth{ 1.0f }; - VkCompareOp depthCompareOp{ VK_COMPARE_OP_LESS }; - VkCullModeFlags cullMode{ VK_CULL_MODE_BACK_BIT }; - bool blend{ false }; - size_t stride{ 0 }; - VkVertexInputRate vertexInputRate{ VK_VERTEX_INPUT_RATE_VERTEX }; - std::vector availableVertexElements{}; - }; - - void SetConfig( GraphicsEffect::Config config ); + void SetConfig( GraphicsEffectTypes::Config config ); void Bind( VkCommandBuffer commandBuffer, uint32_t currentFrameIndex ) override; size_t GetStride() const; @@ -34,5 +22,5 @@ class GraphicsEffect : public Effect VkResult CreatePipeline() override; private: - Config m_config{}; + GraphicsEffectTypes::Config m_config{}; }; diff --git a/src/viewer/rendering/vulkan/graphicseffecttypes.h b/src/viewer/rendering/vulkan/graphicseffecttypes.h new file mode 100644 index 0000000..b5eac4b --- /dev/null +++ b/src/viewer/rendering/vulkan/graphicseffecttypes.h @@ -0,0 +1,51 @@ +#pragma once + +namespace GraphicsEffectTypes +{ + +struct ShaderInputDeclaration +{ + VkVertexInputRate vertexInputRate{ VK_VERTEX_INPUT_RATE_VERTEX }; + std::vector vertexDeclarations; + size_t stride{ 0 }; + // used for displaying additional info in the UI, f.ex Color 1, UV 2, etc. + std::string shaderNameAddition{ "" }; + + bool operator==( const ShaderInputDeclaration& other ) const + { + if( vertexInputRate != other.vertexInputRate || stride != other.stride || shaderNameAddition != other.shaderNameAddition ) + { + return false; + } + if( vertexDeclarations.size() != other.vertexDeclarations.size() ) + { + return false; + + } + for( size_t i = 0; i < vertexDeclarations.size(); i++ ) + { + if( vertexDeclarations[i].usage != other.vertexDeclarations[i].usage || + vertexDeclarations[i].usageIndex != other.vertexDeclarations[i].usageIndex || + vertexDeclarations[i].type != other.vertexDeclarations[i].type || + vertexDeclarations[i].elementCount != other.vertexDeclarations[i].elementCount || + vertexDeclarations[i].offset != other.vertexDeclarations[i].offset ) + { + return false; + } + } + return true; + } +}; + +struct Config +{ + VkPrimitiveTopology topology{ VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST }; + VkPolygonMode polygonMode{ VK_POLYGON_MODE_FILL }; + float lineWidth{ 1.0f }; + VkCompareOp depthCompareOp{ VK_COMPARE_OP_LESS }; + VkCullModeFlags cullMode{ VK_CULL_MODE_BACK_BIT }; + bool blend{ false }; + ShaderInputDeclaration inputDeclaration; +}; + +} \ No newline at end of file diff --git a/src/viewer/rendering/vulkan/shadercache.cpp b/src/viewer/rendering/vulkan/shadercache.cpp index c8beed0..9f82add 100644 --- a/src/viewer/rendering/vulkan/shadercache.cpp +++ b/src/viewer/rendering/vulkan/shadercache.cpp @@ -87,7 +87,7 @@ void ShaderCache::ReleaseShaders( const Renderer* renderer ) s_initialized = false; } -VkResult ShaderCache::CreateGraphicsPipeline( const Renderer* renderer, std::string shaderName, GraphicsEffect::Config config, VkPipelineLayout pipelineLayout, VkPipeline* outPipeline ) +VkResult ShaderCache::CreateGraphicsPipeline( const Renderer* renderer, std::string shaderName, GraphicsEffectTypes::Config config, VkPipelineLayout pipelineLayout, VkPipeline* outPipeline ) { assert( s_initialized ); @@ -164,12 +164,12 @@ VkResult ShaderCache::CreateGraphicsPipeline( const Renderer* renderer, std::str dynamicState.flags = 0; std::vector vertexDescriptions; - ShaderCache::GenerateVertexDescriptions( shaderName, config.availableVertexElements, &vertexDescriptions ); + ShaderCache::GenerateVertexDescriptions( shaderName, config.inputDeclaration.vertexDeclarations, &vertexDescriptions ); VkVertexInputBindingDescription bindingDecl{}; bindingDecl.binding = 0; // expected to be 0 since we only support one vertex buffer - bindingDecl.inputRate = config.vertexInputRate; - bindingDecl.stride = (uint32_t)config.stride; + bindingDecl.inputRate = config.inputDeclaration.vertexInputRate; + bindingDecl.stride = (uint32_t)config.inputDeclaration.stride; VkPipelineVertexInputStateCreateInfo vertexInputState{}; vertexInputState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; @@ -249,61 +249,73 @@ VkResult ShaderCache::CreateComputePipeline( const Renderer* renderer, std::stri return VK_SUCCESS; } -std::vector ShaderCache::GetAvailableShaderNames( const std::vector& availableVertexElements ) +std::vector> ShaderCache::GetAvailableShaders( const std::vector& availableVertexElements ) { - std::vector result; + std::vector> result; result.reserve( s_cache.size() ); - std::vector> elements; - elements.reserve( availableVertexElements.size() ); - std::transform( - availableVertexElements.begin(), - availableVertexElements.end(), - std::back_inserter( elements ), - []( const cmf::VertexElement& element ) { - return std::make_pair( element.usage, element.usageIndex ); - } ); - - std::for_each( s_cache.begin(), s_cache.end(), [&result, &elements]( const auto& keyValue ) { - ShaderContainer shaderContainer = keyValue.second; + std::for_each( s_cache.begin(), s_cache.end(), [&result, &availableVertexElements]( const auto& keyValue ) { + auto& [shaderName, shaderContainer] = keyValue; // only model shaders available if( !shaderContainer.isModelShader ) { return; } - // check if there are any inputs that the shader expects that is not in the cmf vertex declaration + // find all the elements that are used + std::vector elements; + std::vector multiUsageElements; + for( const auto& inputLayout : shaderContainer.inputLayout ) { - if( std::find( elements.begin(), elements.end(), std::make_pair( inputLayout.usage, inputLayout.usageIndex ) ) == elements.end() ) + std::vector foundElements; + std::copy_if( availableVertexElements.begin(), availableVertexElements.end(), std::back_inserter( foundElements ), [inputLayout]( const cmf::VertexElement& element ) { + return element.usage == inputLayout.usage; + } ); + + if( foundElements.empty() ) { + // couldn´t find any element for this usage, so this shader config is not compatible with the available vertex elements return; } + + if( inputLayout.multiUsageIndex ) + { + // this will gather all f.x Color usages, UV usages, etc. and create a shader config for each of them + multiUsageElements.insert( multiUsageElements.end(), foundElements.begin(), foundElements.end() ); + } + else + { + // just take the first one + elements.push_back( foundElements.front() ); + } } - result.push_back( keyValue.first ); + GraphicsEffectTypes::ShaderInputDeclaration shaderinputDeclaration{}; + shaderinputDeclaration.vertexDeclarations = elements; + + if( multiUsageElements.empty() ) + { + result.push_back( { shaderName, shaderinputDeclaration } ); + } + else + { + for( const auto& element : multiUsageElements ) + { + auto inputDecl = shaderinputDeclaration; + inputDecl.vertexDeclarations.push_back( element ); + + if( multiUsageElements.size() != 0 ) + { + inputDecl.shaderNameAddition = std::to_string( element.usageIndex ); + } + result.push_back( { shaderName, inputDecl } ); + } + } } ); return result; } -std::vector ShaderCache::GetShaderUsage( std::string shaderName ) -{ - std::vector usage{}; - auto entry = s_cache.find( shaderName ); - - if( entry == s_cache.end() ) - { - return usage; - } - auto shaderContainer = ( *entry ).second; - for( const auto& layout : shaderContainer.inputLayout ) - { - usage.push_back( layout.usage ); - } - return usage; -} - - void ShaderCache::GenerateVertexDescriptions( std::string shaderName, const std::vector& availableVertexElements, std::vector* outAttributeDescriptions ) { outAttributeDescriptions->clear(); @@ -318,7 +330,7 @@ void ShaderCache::GenerateVertexDescriptions( std::string shaderName, const std: for( const auto& layout : shaderContainer.inputLayout ) { auto foundElement = std::find_if( availableVertexElements.begin(), availableVertexElements.end(), [layout]( const cmf::VertexElement& element ) { - return element.usage == layout.usage && element.usageIndex == layout.usageIndex; + return element.usage == layout.usage; } ); if( foundElement != availableVertexElements.end() ) diff --git a/src/viewer/rendering/vulkan/shadercache.h b/src/viewer/rendering/vulkan/shadercache.h index 3888a85..e5dbe35 100644 --- a/src/viewer/rendering/vulkan/shadercache.h +++ b/src/viewer/rendering/vulkan/shadercache.h @@ -31,7 +31,9 @@ struct ShaderInputLayout { uint8_t location; cmf::Usage usage; - uint8_t usageIndex; + // Used if we can support mapping of multiple vertex elements that have the same usage. + // e.g multiple UV sets, multiple color sets, etc. + bool multiUsageIndex; }; struct ShaderContainer @@ -50,11 +52,9 @@ class ShaderCache static VkResult InitializeShaders( const Renderer* renderer ); static void ReleaseShaders( const Renderer* renderer ); - static VkResult CreateGraphicsPipeline( const Renderer* renderer, std::string shaderName, GraphicsEffect::Config config, VkPipelineLayout pipelineLayout, VkPipeline* outPipeline ); + static VkResult CreateGraphicsPipeline( const Renderer* renderer, std::string shaderName, GraphicsEffectTypes::Config config, VkPipelineLayout pipelineLayout, VkPipeline* outPipeline ); static VkResult CreateComputePipeline( const Renderer* renderer, std::string shaderName, VkPipelineLayout pipelineLayout, VkPipeline* outPipeline ); - static std::vector GetAvailableShaderNames( const std::vector& availableVertexElements ); - - static std::vector GetShaderUsage( std::string shaderName ); + static std::vector> GetAvailableShaders( const std::vector& availableVertexElements ); private: static void GenerateVertexDescriptions( std::string shaderName, const std::vector& availableVertexElements, std::vector* outAttributeDescriptions ); diff --git a/src/viewer/scripts/shaderCacheCreator.py b/src/viewer/scripts/shaderCacheCreator.py index 8c3ebfd..5fd6c95 100644 --- a/src/viewer/scripts/shaderCacheCreator.py +++ b/src/viewer/scripts/shaderCacheCreator.py @@ -1,133 +1,164 @@ -import sys import os import re +import sys -def enumerate_files( sourceDir, binaryDir, output): - shaderGroups = {} - vertexDeclarations = {} - - # matches the pattern: - # layout( location = 0 ) in vec3 inPosition; - # grabs the 0 and inPosition - pattern = r'layout\s*\(\s*location\s*=\s*(\d+)\s*\)\s*in\s+\w+\s+(\w+);' - - shaderUsageLookup = { - "inposition": "cmf::Usage::Position", - "innormal": "cmf::Usage::Normal", - "inbinormal": "cmf::Usage::Binormal", - "intangent": "cmf::Usage::Tangent", - "inpackedtangents": "cmf::Usage::PackedTangent", - "inpackedtangentslegacy": "cmf::Usage::PackedTangentLegacy", - "incolor": "cmf::Usage::Color", - "intexcoord": "cmf::Usage::TexCoord", - "inboneindices": "cmf::Usage::BoneIndices", - "inboneweights": "cmf::Usage::BoneWeights" - }; - - # find all the input vertex declarations of vertex shaders - for filename in os.listdir(sourceDir): - if not filename.endswith('.vert'): - continue - - parts = filename.split('.') - name = parts[0] - - with open(os.path.join(sourceDir, filename), 'r') as vertexShader: - inputDeclarations = [] - for line in vertexShader: - match = re.search(pattern, line) - if match: - variableName = str(match.group(2)).lower() - try: - usage = shaderUsageLookup[variableName] - except KeyError as e: - print("could not find '%s' in applicable vertex variable names %s" % ( variableName, shaderUsageLookup.keys() )) - raise e - - inputDeclarations.append( ( int(match.group(1)), usage )) - vertexDeclarations[name] = inputDeclarations - - for filename in os.listdir(binaryDir): - if not filename.endswith('.spv.h'): + +VERTEX_DECLARATION_PATTERN = re.compile( + r"^\s*layout\s*\(\s*location\s*=\s*(\d+)\s*\)\s*in\s+\w+\s+(\w+)\s*;" +) + +MULTI_USAGE_VERTEX_NAME = re.compile( + r"^\s*#pragma\s+multi_usage\s+(\w+)\s*$" +) + +SHADER_USAGE_LOOKUP = { + "inposition": "cmf::Usage::Position", + "innormal": "cmf::Usage::Normal", + "inbinormal": "cmf::Usage::Binormal", + "intangent": "cmf::Usage::Tangent", + "inpackedtangents": "cmf::Usage::PackedTangent", + "inpackedtangentslegacy": "cmf::Usage::PackedTangentLegacy", + "incolor": "cmf::Usage::Color", + "intexcoord": "cmf::Usage::TexCoord", + "inboneindices": "cmf::Usage::BoneIndices", + "inboneweights": "cmf::Usage::BoneWeights", +} + + +def shader_wrapper(shader_name): + return 'Shader( {\n #include "%s"\n } )' % shader_name + + +def parse_vertex_declarations(shader_path): + declarations = [] + multi_usage_names = [] + + with open(shader_path, "r") as vertex_shader: + for line in vertex_shader: + multi_usage_names_match = MULTI_USAGE_VERTEX_NAME.match(line) + if multi_usage_names_match: + multi_usage_names.append(multi_usage_names_match.group(1).lower()) + continue + + match = VERTEX_DECLARATION_PATTERN.match(line) + if not match: + continue + + print("match in file '%s': %s" % (os.path.basename(shader_path), match.groups())) + + variable_name = match.group(2).lower() + if variable_name not in SHADER_USAGE_LOOKUP: + raise KeyError( + "could not find '%s' in applicable vertex variable names %s" + % (variable_name, SHADER_USAGE_LOOKUP.keys()) + ) + + declarations.append((int(match.group(1)), SHADER_USAGE_LOOKUP[variable_name], variable_name in multi_usage_names)) + + if len(multi_usage_names) > 1: + print("ERROR: Shader '%s' has multi usage index elements: %s" % (os.path.basename(shader_path), multi_usage_names)) + print("Only zero or one elements can be multi index usage") + sys.exit(1) + + print("Parsed vertex declarations for shader '%s': %s" % (os.path.basename(shader_path), declarations)) + return declarations + + +def collect_vertex_declarations(source_dir): + vertex_declarations = {} + + for filename in sorted(os.listdir(source_dir)): + if filename.endswith(".vert"): + shader_name = filename.rsplit(".", 1)[0] + vertex_declarations[shader_name] = parse_vertex_declarations(os.path.join(source_dir, filename)) + + return vertex_declarations + + +def collect_shader_groups(binary_dir): + shader_groups = {} + + for filename in sorted(os.listdir(binary_dir)): + if not filename.endswith(".spv.h"): continue - parts = filename.split('.') - name = parts[0] - shaderType = parts[1] - shaders = shaderGroups.get(name, [None, None, None]) - if shaderType == 'vert': - shaders[0] = filename - elif shaderType == 'frag': - shaders[1] = filename - elif shaderType == 'comp': - shaders[2] = filename - else: + parts = filename.split(".") + if len(parts) < 3: continue - shaderGroups[name] = shaders + shader_name = parts[0] + shader_type = parts[1] + group = shader_groups.setdefault(shader_name, {"vert": None, "frag": None, "comp": None}) + + if shader_type in group: + group[shader_type] = filename + + return shader_groups + + +def display_name(shader_name): + if not shader_name.startswith("model_"): + return shader_name + + parts = shader_name[len("model_"):].split("_") + return " ".join(part[:1].upper() + part[1:] for part in parts if part) + - code = "" +def format_input_layout(shader_name, vertex_declarations): + entries = vertex_declarations.get(shader_name, []) + return ",\n".join("{%d, %s, %d}" % (location, usage, int(multi_usage)) for location, usage, multi_usage in entries) - for name, shaders in shaderGroups.items(): - - if (not shaders[0] or not shaders[1]) and not shaders[2]: - print("Warning: Shader group '%s' is missing a shader." % name) + +def format_shader_entry(shader_name, shader_group, vertex_declarations): + vert_shader = "std::nullopt" + frag_shader = "std::nullopt" + comp_shader = "std::nullopt" + + if shader_group["vert"]: + vert_shader = shader_wrapper(shader_group["vert"]) + if shader_group["frag"]: + frag_shader = shader_wrapper(shader_group["frag"]) + if shader_group["comp"]: + comp_shader = shader_wrapper(shader_group["comp"]) + + return ( + ' {"%s", { \n' + ' %s,\n' + ' %s,\n' + ' %s,\n' + ' %s,\n' + ' { %s }\n' + ' } \n' + ' },' + % ( + display_name(shader_name), + vert_shader, + frag_shader, + comp_shader, + str(shader_name.startswith("model_")).lower(), + format_input_layout(shader_name, vertex_declarations), + ) + ) + + +def enumerate_files(source_dir, binary_dir, output_file): + vertex_declarations = collect_vertex_declarations(source_dir) + shader_groups = collect_shader_groups(binary_dir) + + entries = [] + for shader_name in sorted(shader_groups.keys()): + shader_group = shader_groups[shader_name] + + if (not shader_group["vert"] or not shader_group["frag"]) and not shader_group["comp"]: + print("Warning: Shader group '%s' is missing a shader." % shader_name) continue - vertShader = "std::nullopt" - fragShader = "std::nullopt" - compShader = "std::nullopt" - if shaders[0]: - vertShader = """Shader( { - #include \"%s\" - } - )""" % ( shaders[0], ) - if shaders[1]: - fragShader = """Shader( { - #include \"%s\" - } )""" % shaders[1] - if shaders[2]: - compShader = """Shader( { - #include \"%s\" - } )""" % shaders[2] - - splitNames = name.replace("model_", "").split("_") - - shaderConfigs = [[(locationIndex, usage, 0) for (locationIndex, usage) in vertexDeclarations.get(name, [])]] - modelShader = name.startswith("model_") - - displayName = name - if modelShader: - displayName = " ".join(s[0].upper() + s[1:] for s in splitNames) - vertexElements = vertexDeclarations.get(name) - if len(vertexElements) == 2: - # recreate the config - shaderConfigs = [] - for i in range(0, 8): - config = [] - for index, usage in vertexDeclarations[name]: - if usage != "cmf::Usage::Position": - config.append( (index, usage, i) ) - else: - config.append( (index, usage, 0) ) - - shaderConfigs.append(config) - - for (i, shaderConfig) in enumerate(shaderConfigs): - code += """ - {\"%s\", { - %s, - %s, - %s, - %s, - { %s } - } - },""" % (displayName + ("" if i == 0 else " " + str(i)), vertShader, fragShader, compShader, str(modelShader).lower(), ",\n".join(["{%d, %s, %d}" % (locationIndex, usage, usageIndex) for (locationIndex, usage, usageIndex) in shaderConfig ])) - - code = code[:-1] - with open(output, 'w') as f: - f.write(code) + entries.append(format_shader_entry(shader_name, shader_group, vertex_declarations)) + code = "\n".join(entries) + + with open(output_file, "w") as f: + f.write(code) if __name__ == '__main__': if len(sys.argv) != 4: From 7cc7060d336fb6d759527963a1231c1657d2afdf Mon Sep 17 00:00:00 2001 From: ccpnobody <55136070+ccpnobody@users.noreply.github.com> Date: Mon, 15 Jun 2026 13:44:28 +0000 Subject: [PATCH 11/21] Add tri-state checkboxes for mesh attribute controls Implemented tri-state checkboxes in the UI for mesh display, wireframe, and audio occlusion controls, as well as per-attribute-index toggles for normals, tangents, and bitangents. Refactored related logic, improved tooltips, and added helper methods for managing tri-state states across multiple meshes. --- src/viewer/rendering/uiRenderer.cpp | 258 +++++++++++------- src/viewer/rendering/uiRenderer.h | 23 +- .../rendering/uiRenderer_template_impl.h | 46 ++++ 3 files changed, 229 insertions(+), 98 deletions(-) diff --git a/src/viewer/rendering/uiRenderer.cpp b/src/viewer/rendering/uiRenderer.cpp index 054bd23..9f81a5e 100644 --- a/src/viewer/rendering/uiRenderer.cpp +++ b/src/viewer/rendering/uiRenderer.cpp @@ -5,11 +5,43 @@ #include #include #include +#include #include #include "appState.h" #include "vulkan/vulkanerrors.h" +// taken from https://github.com/ocornut/imgui/issues/2644 +namespace ImGui +{ + +// threeway checkbox +bool CheckBoxTristate( const char* label, CheckBoxTriStateValue* v_tristate ) +{ + bool ret; + if( *v_tristate == CheckBoxTriStateValue::MIXED ) + { + ImGui::PushItemFlag( ImGuiItemFlags_MixedValue, true ); + bool b = false; + ret = ImGui::Checkbox( label, &b ); + if( ret ) + { + *v_tristate = CheckBoxTriStateValue::CHECKED; + } + ImGui::PopItemFlag(); + } + else + { + bool b = ( *v_tristate != CheckBoxTriStateValue::UNCHECKED ); + ret = ImGui::Checkbox( label, &b ); + if( ret ) + { + *v_tristate = (CheckBoxTriStateValue)(int)b; + } + } + return ret; +} +}; const float MENU_BAR_HEIGHT = 18.0f; const float ANIMATION_PLAYER_HEIGHT = 36.0f; @@ -255,7 +287,6 @@ void UIRenderer::CMFInfoWindow( AppState& appState ) void UIRenderer::MeshDetailsWindow( AppState& appState ) { bool open = true; - ImGui::ShowDemoWindow( &open ); float width = (float)appState.windowSize.GetValue().first; float height = (float)appState.windowSize.GetValue().second; @@ -449,13 +480,13 @@ void UIRenderer::SetupGeneralView( AppState& appState ) ImGui::TableNextColumn(); ImGui::Text( "Display" ); ImGui::TableNextColumn(); - bool display = std::all_of( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { - return state.GetValue().display.GetValue(); - } ) && - appState.modelState.meshes.size() > 0; - OnChange( ImGui::Checkbox( "##displaycheckbox", &display ), [&appState, &display]() { - std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [display]( State& state ) { - state.GetValue().display.SetValue( display ); + size_t checkedCount = std::count_if( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { + return state.GetValue().display.GetValue(); + } ); + ImGui::CheckBoxTriStateValue checked = ( checkedCount == 0 ) ? ImGui::CheckBoxTriStateValue::UNCHECKED : ( checkedCount == appState.modelState.meshes.size() ? ImGui::CheckBoxTriStateValue::CHECKED : ImGui::CheckBoxTriStateValue::MIXED ); + OnChange( ImGui::CheckBoxTristate( "##displaycheckbox", &checked ), [&appState, &checked]() { + std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [checked]( State& state ) { + state.GetValue().display.SetValue( checked == ImGui::CheckBoxTriStateValue::CHECKED ); } ); } ); ImGui::TableNextRow(); @@ -472,13 +503,13 @@ void UIRenderer::SetupGeneralView( AppState& appState ) ImGui::TableNextColumn(); ImGui::Text( "Wireframe Overlay" ); ImGui::TableNextColumn(); - bool wireframeOverlay = std::all_of( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { - return state.GetValue().wireframeOverlay.GetValue(); - } ) && - appState.modelState.meshes.size() > 0; - OnChange( ImGui::Checkbox( "##wireframecheckbox", &wireframeOverlay ), [&appState, &wireframeOverlay]() { - std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [wireframeOverlay]( State& state ) { - state.GetValue().wireframeOverlay.SetValue( wireframeOverlay ); + checkedCount = std::count_if( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { + return state.GetValue().wireframeOverlay.GetValue(); + } ); + checked = ( checkedCount == 0 ) ? ImGui::CheckBoxTriStateValue::UNCHECKED : ( checkedCount == appState.modelState.meshes.size() ? ImGui::CheckBoxTriStateValue::CHECKED : ImGui::CheckBoxTriStateValue::MIXED ); + OnChange( ImGui::CheckBoxTristate( "##wireframecheckbox", &checked ), [&appState, &checked]() { + std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [checked]( State& state ) { + state.GetValue().wireframeOverlay.SetValue( checked == ImGui::CheckBoxTriStateValue::CHECKED ); } ); } ); ImGui::TableNextRow(); @@ -490,79 +521,20 @@ void UIRenderer::SetupGeneralView( AppState& appState ) ImGui::TableNextColumn(); ImGui::Text( "Audio Occlusion Mesh" ); ImGui::TableNextColumn(); - bool audioOcclusion = std::all_of( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { - return state.GetValue().audioOcclusionMesh.GetValue(); - } ) && - appState.modelState.meshes.size() > 0; - OnChange( ImGui::Checkbox( "##audioocclusioncheckbox", &audioOcclusion ), [&appState, &audioOcclusion]() { - std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [audioOcclusion]( State& state ) { - state.GetValue().audioOcclusionMesh.SetValue( audioOcclusion ); - } ); + checkedCount = std::count_if( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { + return state.GetValue().audioOcclusionMesh.GetValue(); } ); - ImGui::TableNextRow(); - ImGui::EndDisabled(); - /* - bool hasNormals = std::any_of( m_uiState.modelStates.meshes.begin(), m_uiState.modelStates.meshes.end(), []( const MeshUiState& state ) { - return state.hasNormals; - } ); - ImGui::BeginDisabled( !hasNormals ); - ImGui::TableNextColumn(); - ImGui::Text( "Normals" ); - ImGui::SetItemTooltip( "Toggles the normals for all meshes" ); - ImGui::TableNextColumn(); - bool normals = std::all_of( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { - return state.GetValue().showVertexNormals.GetValue(); - } ) && - appState.modelState.meshes.size() > 0; - OnChange( ImGui::Checkbox( "##vertexnormals", &normals ), [&appState, &normals]() { - std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [normals]( State& state ) { - state.GetValue().showVertexNormals.SetValue( normals ); + checked = ( checkedCount == 0 ) ? ImGui::CheckBoxTriStateValue::UNCHECKED : ( checkedCount == appState.modelState.meshes.size() ? ImGui::CheckBoxTriStateValue::CHECKED : ImGui::CheckBoxTriStateValue::MIXED ); + OnChange( ImGui::CheckBoxTristate( "##audioocclusioncheckbox", &checked ), [&appState, &checked]() { + std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [checked]( State& state ) { + state.GetValue().audioOcclusionMesh.SetValue( checked == ImGui::CheckBoxTriStateValue::CHECKED ); } ); } ); - ImGui::EndDisabled(); - - - bool hasTangents = std::any_of( m_uiState.modelStates.meshes.begin(), m_uiState.modelStates.meshes.end(), []( const MeshUiState& state ) { - return state.hasTangents; - } ); - ImGui::BeginDisabled( !hasTangents ); ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text( "Tangents" ); - ImGui::SetItemTooltip( "Toggles the tangents for all meshes" ); - ImGui::TableNextColumn(); - bool tangents = std::all_of( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { - return state.GetValue().showVertexTangents.GetValue(); - } ) && - appState.modelState.meshes.size() > 0; - OnChange( ImGui::Checkbox( "##vertextangents", &tangents ), [&appState, &tangents]() { - std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [tangents]( State& state ) { - state.GetValue().showVertexTangents.SetValue( tangents ); - } ); - } ); ImGui::EndDisabled(); + SetupModelAxisRows( appState ); - bool hasBinormals = std::any_of( m_uiState.modelStates.meshes.begin(), m_uiState.modelStates.meshes.end(), []( const MeshUiState& state ) { - return state.hasBinormals; - } ); - ImGui::BeginDisabled( !hasBinormals ); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text( "Bitangents" ); - ImGui::SetItemTooltip( "Toggles the bitangents for all meshes" ); - ImGui::TableNextColumn(); - bool bitangents = std::all_of( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), []( const State& state ) { - return state.GetValue().showVertexBinormals.GetValue(); - } ) && - appState.modelState.meshes.size() > 0; - OnChange( ImGui::Checkbox( "##vertexbitangents", &bitangents ), [&appState, &bitangents]() { - std::for_each( appState.modelState.meshes.begin(), appState.modelState.meshes.end(), [bitangents]( State& state ) { - state.GetValue().showVertexBinormals.SetValue( bitangents ); - } ); - } ); - ImGui::EndDisabled(); - */ ImGui::EndTable(); } } @@ -584,6 +556,100 @@ void UIRenderer::SetupMeshListView( const ModelUiState& modelState, AppState& ap } } +void UIRenderer::SetupModelAxisRows( AppState& appState ) +{ + std::vector>> allNormalStates; + std::vector>> allTangentStates; + std::vector>> allBinormalStates; + + for( const auto& meshState : appState.modelState.meshes ) + { + allNormalStates.push_back( meshState.GetValue().showVertexNormals ); + allTangentStates.push_back( meshState.GetValue().showVertexTangents ); + allBinormalStates.push_back( meshState.GetValue().showVertexBinormals ); + } + + auto normalCheckboxes = GetAxisTriCheckboxStates( allNormalStates ); + auto tangentCheckboxes = GetAxisTriCheckboxStates( allTangentStates ); + auto binormalCheckboxes = GetAxisTriCheckboxStates( allBinormalStates ); + + SetupModelAxisRow( normalCheckboxes, std::string( "Normals" ), [&appState]( bool checked, uint32_t index ) { + for( auto& meshState : appState.modelState.meshes ) + { + auto& vertexNormalStates = meshState.GetValue().showVertexNormals; + auto foundState = std::find_if( vertexNormalStates.begin(), vertexNormalStates.end(), [index]( const State>& state ) { + return state.GetValue().first == index; + } ); + if( foundState != vertexNormalStates.end() ) + { + foundState->SetValue( { index, checked } ); + } + } + } ); + + SetupModelAxisRow( tangentCheckboxes, std::string( "Tangents" ), [&appState]( bool checked, uint32_t index ) { + for( auto& meshState : appState.modelState.meshes ) + { + auto& vertexTangentStates = meshState.GetValue().showVertexTangents; + auto foundState = std::find_if( vertexTangentStates.begin(), vertexTangentStates.end(), [index]( const State>& state ) { + return state.GetValue().first == index; + } ); + if( foundState != vertexTangentStates.end() ) + { + foundState->SetValue( { index, checked } ); + } + } + } ); + + SetupModelAxisRow( binormalCheckboxes, std::string( "Bitangents" ), [&appState]( bool checked, uint32_t index ) { + for( auto& meshState : appState.modelState.meshes ) + { + auto& vertexBinormalStates = meshState.GetValue().showVertexBinormals; + auto foundState = std::find_if( vertexBinormalStates.begin(), vertexBinormalStates.end(), [index]( const State>& state ) { + return state.GetValue().first == index; + } ); + if( foundState != vertexBinormalStates.end() ) + { + foundState->SetValue( { index, checked } ); + } + } + } ); +} + +std::vector> UIRenderer::GetAxisTriCheckboxStates( const std::vector>>& axisStates ) +{ + std::vector> combinedStates; + + for( const auto& meshState : axisStates ) + { + for( const auto& axis : meshState ) + { + auto [index, checked] = axis.GetValue(); + + auto foundState = std::find_if( combinedStates.begin(), combinedStates.end(), [index]( const std::pair& pair ) { + return pair.first == index; + } ); + + if( foundState == combinedStates.end() ) + { + combinedStates.push_back( { index, checked ? ImGui::CheckBoxTriStateValue::CHECKED : ImGui::CheckBoxTriStateValue::UNCHECKED } ); + } + else + { + if( foundState->second != ImGui::CheckBoxTriStateValue::MIXED ) + { + if( ( foundState->second == ImGui::CheckBoxTriStateValue::CHECKED && !checked ) || ( foundState->second == ImGui::CheckBoxTriStateValue::UNCHECKED && checked ) ) + { + foundState->second = ImGui::CheckBoxTriStateValue::MIXED; + } + } + } + } + } + + return combinedStates; +} + void UIRenderer::SetupVertexAxisRows( MeshState& meshAppState ) { auto usageIndexChecker = []( const State>& pair1, const State>& pair2 ) { @@ -594,20 +660,17 @@ void UIRenderer::SetupVertexAxisRows( MeshState& meshAppState ) auto maxTangentIndexState = std::max_element( meshAppState.showVertexTangents.begin(), meshAppState.showVertexTangents.end(), usageIndexChecker ); auto maxBitangentIndexState = std::max_element( meshAppState.showVertexBinormals.begin(), meshAppState.showVertexBinormals.end(), usageIndexChecker ); - auto maxNormalIndex = maxNormalIndexState != meshAppState.showVertexNormals.end() ? maxNormalIndexState->GetValue().first : -1; - auto maxTangentIndex = maxTangentIndexState != meshAppState.showVertexTangents.end() ? maxTangentIndexState->GetValue().first : -1; - auto maxBitangentIndex = maxBitangentIndexState != meshAppState.showVertexBinormals.end() ? maxBitangentIndexState->GetValue().first : -1; - - auto maxIndex = std::max( { maxNormalIndex, maxTangentIndex, maxBitangentIndex } ) + 1; + int32_t maxNormalIndex = maxNormalIndexState != meshAppState.showVertexNormals.end() ? maxNormalIndexState->GetValue().first : -1; + int32_t maxTangentIndex = maxTangentIndexState != meshAppState.showVertexTangents.end() ? maxTangentIndexState->GetValue().first : -1; + int32_t maxBitangentIndex = maxBitangentIndexState != meshAppState.showVertexBinormals.end() ? maxBitangentIndexState->GetValue().first : -1; - SetupVertexAxisRow( meshAppState.showVertexNormals, "Normals", maxIndex ); - SetupVertexAxisRow( meshAppState.showVertexTangents, "Tangents", maxIndex ); - SetupVertexAxisRow( meshAppState.showVertexBinormals, "Bitangents", maxIndex ); + SetupVertexAxisRow( meshAppState.showVertexNormals, "Normals", maxNormalIndex ); + SetupVertexAxisRow( meshAppState.showVertexTangents, "Tangents", maxTangentIndex ); + SetupVertexAxisRow( meshAppState.showVertexBinormals, "Bitangents", maxBitangentIndex ); } -void UIRenderer::SetupVertexAxisRow( StateCollection>& vertexAxisStates, const char* label, int columnCount ) +void UIRenderer::SetupVertexAxisRow( StateCollection>& vertexAxisStates, const char* label, int maxUsageIndex ) { - // table with the first column being the label and the rest being checkboxes for each attribute index if( vertexAxisStates.empty() ) { ImGui::BeginDisabled(); @@ -617,9 +680,9 @@ void UIRenderer::SetupVertexAxisRow( StateCollection>& ImGui::Text( label ); ImGui::TableNextColumn(); - if( columnCount >= 0 ) + if( maxUsageIndex >= 0 ) { - for( int32_t i = 0; i < columnCount; ++i ) + for( int32_t i = 0; i <= maxUsageIndex; ++i ) { // find the states that have the index of the column (f.ex there is nothing stopping a mesh having one color attribute that is using usageindex at 16) auto foundElement = std::find_if( vertexAxisStates.begin(), vertexAxisStates.end(), [i]( const State>& state ) { @@ -634,12 +697,17 @@ void UIRenderer::SetupVertexAxisRow( StateCollection>& foundElement->SetValue( { foundElement->GetValue().first, foundElement->GetValue().second } ); } ); - ImGui::SetItemTooltip( "%s %d", label, foundElement->GetValue().first ); + ImGui::SetItemTooltip( "Toggles debug visualization for %s with usage index %d", label, i ); ImGui::SameLine(); } else { - ImGui::SameLine( 18.0f ); + ImGui::BeginDisabled( true ); + bool value = false; + ImGui::Checkbox( ( std::string( "##" ) + label + std::to_string( i ) ).c_str(), &value ); + ImGui::SetItemTooltip( "Mesh doesn't have %s with usage index %d.", label, i ); + ImGui::SameLine(); + ImGui::EndDisabled(); } } ImGui::NewLine(); diff --git a/src/viewer/rendering/uiRenderer.h b/src/viewer/rendering/uiRenderer.h index 0d3ade3..39c5cf9 100644 --- a/src/viewer/rendering/uiRenderer.h +++ b/src/viewer/rendering/uiRenderer.h @@ -8,6 +8,17 @@ #include "rendering/renderer.h" #include "vulkan/commandbuffer.h" +namespace ImGui +{ +enum class CheckBoxTriStateValue +{ + UNCHECKED = 0, + CHECKED = 1, + MIXED = -1 +}; +bool CheckBoxTristate( const char* label, CheckBoxTriStateValue* v_tristate ); +} + // Handles rendering the UI class UIRenderer { @@ -68,9 +79,9 @@ class UIRenderer bool wireframeOverlay{ false }; bool audioOcclusionMesh{ false }; bool hasAudioOcclusionMesh{ false }; - std::vector> showVertexNormals{ }; - std::vector> showVertexTangents{ }; - std::vector> showVertexBinormals{ }; + std::vector> showVertexNormals{}; + std::vector> showVertexTangents{}; + std::vector> showVertexBinormals{}; }; struct ModelUiState @@ -145,6 +156,12 @@ class UIRenderer void SetupGeneralView( AppState& appState ); void SetupMeshListView( const ModelUiState& modelState, AppState& appState ); void SetupMeshView( const MeshUiState& mesh, AppState& appState ); + void SetupModelAxisRows( AppState& appState ); + + template + void SetupModelAxisRow( std::vector>& checkboxStates, const std::string& name, const Callable& changeCallback ); + + std::vector> GetAxisTriCheckboxStates( const std::vector>>& axisStates ); void SetupVertexAxisRows( MeshState& meshAppState ); void SetupVertexAxisRow( StateCollection>& vertexAxisStates, const char* label, int columnCount ); void SetupMorphTarget( const MorphTargetUiState& morphTarget, size_t meshIndex, AppState& appState ); diff --git a/src/viewer/rendering/uiRenderer_template_impl.h b/src/viewer/rendering/uiRenderer_template_impl.h index 01f3902..8947ac3 100644 --- a/src/viewer/rendering/uiRenderer_template_impl.h +++ b/src/viewer/rendering/uiRenderer_template_impl.h @@ -57,4 +57,50 @@ void UIRenderer::CmfUiComboBox::SetSelectedItemByValue( T value ) selectedItemName = items.front().first; selectedItemValue = items.front().second; } +} + +template +void UIRenderer::SetupModelAxisRow( std::vector>& checkboxStates, const std::string& name, const Callable& changeCallback ) +{ + int32_t maxIndex = checkboxStates.empty() ? -1 : std::max_element( checkboxStates.begin(), checkboxStates.end(), []( const std::pair& a, const std::pair& b ) { + return a.first < b.first; + } )->first; + + ImGui::BeginDisabled( maxIndex < 0 ); + ImGui::TableNextColumn(); + ImGui::Text( name.c_str() ); + ImGui::SetItemTooltip( "Toggles the %s visualization for all meshes", name.c_str() ); + ImGui::TableNextColumn(); + + if( maxIndex >= 0 ) + { + for( int32_t i = 0; i <= maxIndex; ++i ) + { + auto foundState = std::find_if( checkboxStates.begin(), checkboxStates.end(), [i]( const std::pair& pair ) { + return pair.first == (uint32_t)i; + } ); + + if( foundState == checkboxStates.end() ) + { + // add a disabled checkbox + ImGui::BeginDisabled( true ); + auto disabledValue = ImGui::CheckBoxTriStateValue::UNCHECKED; + ImGui::CheckBoxTristate( ( std::string( "##tricheckbox" ) + name + std::to_string( i ) ).c_str(), &disabledValue ); + + ImGui::SetItemTooltip( "No mesh has %s information for usage %d", name.c_str(), i ); + ImGui::SameLine(); + ImGui::EndDisabled(); + } + else + { + auto value = foundState->second; + OnChange( ImGui::CheckBoxTristate( ( std::string( "##tricheckbox" ) + name + std::to_string( i ) ).c_str(), &value ), [changeCallback, i, value]() { + changeCallback( value == ImGui::CheckBoxTriStateValue::CHECKED, i ); + } ); + ImGui::SetItemTooltip( "Toggles debug visualization for %s with usage index %d for all meshes (if available)", name.c_str(), i ); + ImGui::SameLine(); + } + } + } + ImGui::EndDisabled(); } \ No newline at end of file From 2284890a2651093ecdfdc03b57a803172e8828f0 Mon Sep 17 00:00:00 2001 From: ccpnobody <55136070+ccpnobody@users.noreply.github.com> Date: Mon, 15 Jun 2026 13:45:55 +0000 Subject: [PATCH 12/21] Formatting fixes --- src/viewer/rendering/models/primitiveEffects.cpp | 2 +- src/viewer/rendering/renderable/mesh.cpp | 3 +-- src/viewer/rendering/vulkan/graphicseffecttypes.h | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/viewer/rendering/models/primitiveEffects.cpp b/src/viewer/rendering/models/primitiveEffects.cpp index cf75a86..d7c50b4 100644 --- a/src/viewer/rendering/models/primitiveEffects.cpp +++ b/src/viewer/rendering/models/primitiveEffects.cpp @@ -41,7 +41,7 @@ GraphicsEffect CreateAxisEffect( std::shared_ptr renderer, const } else { - inputDeclaration.vertexDeclarations.push_back( element ); + inputDeclaration.vertexDeclarations.push_back( element ); } } effectConfig.inputDeclaration = inputDeclaration; diff --git a/src/viewer/rendering/renderable/mesh.cpp b/src/viewer/rendering/renderable/mesh.cpp index 7c604fa..bff2381 100644 --- a/src/viewer/rendering/renderable/mesh.cpp +++ b/src/viewer/rendering/renderable/mesh.cpp @@ -316,7 +316,7 @@ void MeshRenderable::RenderDebug( GraphicsCommandBuffer& commandBuffer, const Ap { if( normalState.GetValue().second ) { - // since the state and renderables are created in the same order based on the vertex elements, + // since the state and renderables are created in the same order based on the vertex elements, // we can use the index to get the corresponding renderable for the normal/tangent/binormal we want to render m_normalAxisRenderables[index].SetUniformData( 0, viewProj ); m_normalAxisRenderables[index].Render( commandBuffer, buffer, nullptr, 2, streamElementCount ); @@ -344,7 +344,6 @@ void MeshRenderable::RenderDebug( GraphicsCommandBuffer& commandBuffer, const Ap } ++index; } - } void MeshRenderable::PrepareMesh( ComputeCommandBuffer& commandBuffer ) diff --git a/src/viewer/rendering/vulkan/graphicseffecttypes.h b/src/viewer/rendering/vulkan/graphicseffecttypes.h index b5eac4b..d7aa25a 100644 --- a/src/viewer/rendering/vulkan/graphicseffecttypes.h +++ b/src/viewer/rendering/vulkan/graphicseffecttypes.h @@ -20,7 +20,6 @@ struct ShaderInputDeclaration if( vertexDeclarations.size() != other.vertexDeclarations.size() ) { return false; - } for( size_t i = 0; i < vertexDeclarations.size(); i++ ) { From 267fe3f3109bb1aea74684a43bd73daf9aae772f Mon Sep 17 00:00:00 2001 From: ccpnobody <55136070+ccpnobody@users.noreply.github.com> Date: Mon, 15 Jun 2026 13:50:28 +0000 Subject: [PATCH 13/21] more formatting fixes --- src/viewer/rendering/models/axis.cpp | 2 +- src/viewer/rendering/sceneRenderer.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/viewer/rendering/models/axis.cpp b/src/viewer/rendering/models/axis.cpp index cb77abc..1900702 100644 --- a/src/viewer/rendering/models/axis.cpp +++ b/src/viewer/rendering/models/axis.cpp @@ -57,7 +57,7 @@ PrimitiveRenderable CreatePackedNormal( std::shared_ptr renderer PrimitiveRenderable CreatePackedTangent( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ) { // the bufferdata is the output of the geoprepass and will be handled later - return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedAxisEffect( renderer, cmf::Usage::Tangent, usageIndex,mesh ) ); + return PrimitiveRenderable( renderer, PrimitiveEffects::CreatePackedAxisEffect( renderer, cmf::Usage::Tangent, usageIndex, mesh ) ); } PrimitiveRenderable CreatePackedBinormal( std::shared_ptr renderer, const cmf::Mesh& mesh, const uint32_t usageIndex ) diff --git a/src/viewer/rendering/sceneRenderer.cpp b/src/viewer/rendering/sceneRenderer.cpp index f2a84db..ff6a9cb 100644 --- a/src/viewer/rendering/sceneRenderer.cpp +++ b/src/viewer/rendering/sceneRenderer.cpp @@ -123,7 +123,7 @@ void SceneRenderer::SetData( std::shared_ptr data, AppState& appStat // add all of the declarations, there may be situations where one mesh has more declarations than another inside the same model availableVertexElements.insert( availableVertexElements.end(), mesh.decl.begin(), mesh.decl.end() ); } - + std::vector> shaders = ShaderCache::GetAvailableShaders( availableVertexElements ); auto foundItem = std::find_if( shaders.begin(), shaders.end(), [&]( const auto& shaderNameAndDeclarations ) { return shaderNameAndDeclarations.first == currentShaderName; @@ -147,7 +147,7 @@ void SceneRenderer::SetData( std::shared_ptr data, AppState& appStat } } appState.modelState.availableShaders.SetValue( shaders ); - + m_model.reset( new ModelRenderable( data, m_renderer ) ); m_model->Initialize( appState ); } From 1918c11c4b75eb8f40ee2689d1d0335bdd14860e Mon Sep 17 00:00:00 2001 From: ccpnobody <55136070+ccpnobody@users.noreply.github.com> Date: Mon, 15 Jun 2026 16:41:33 +0000 Subject: [PATCH 14/21] Fixing mac build errors --- src/viewer/rendering/sceneRenderer.cpp | 2 +- src/viewer/rendering/uiRenderer.cpp | 2 +- src/viewer/rendering/uiRenderer_template_impl.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/viewer/rendering/sceneRenderer.cpp b/src/viewer/rendering/sceneRenderer.cpp index ff6a9cb..43a51c7 100644 --- a/src/viewer/rendering/sceneRenderer.cpp +++ b/src/viewer/rendering/sceneRenderer.cpp @@ -125,7 +125,7 @@ void SceneRenderer::SetData( std::shared_ptr data, AppState& appStat } std::vector> shaders = ShaderCache::GetAvailableShaders( availableVertexElements ); - auto foundItem = std::find_if( shaders.begin(), shaders.end(), [&]( const auto& shaderNameAndDeclarations ) { + auto foundItem = std::find_if( shaders.begin(), shaders.end(), [¤tShaderName]( const auto& shaderNameAndDeclarations ) { return shaderNameAndDeclarations.first == currentShaderName; } ); if( foundItem == shaders.end() && shaders.size() > 0 ) diff --git a/src/viewer/rendering/uiRenderer.cpp b/src/viewer/rendering/uiRenderer.cpp index 9f81a5e..fe4e3c9 100644 --- a/src/viewer/rendering/uiRenderer.cpp +++ b/src/viewer/rendering/uiRenderer.cpp @@ -626,7 +626,7 @@ std::vector> UIRenderer::GetAx { auto [index, checked] = axis.GetValue(); - auto foundState = std::find_if( combinedStates.begin(), combinedStates.end(), [index]( const std::pair& pair ) { + auto foundState = std::find_if( combinedStates.begin(), combinedStates.end(), [&index]( const std::pair& pair ) { return pair.first == index; } ); diff --git a/src/viewer/rendering/uiRenderer_template_impl.h b/src/viewer/rendering/uiRenderer_template_impl.h index 8947ac3..a2b29c8 100644 --- a/src/viewer/rendering/uiRenderer_template_impl.h +++ b/src/viewer/rendering/uiRenderer_template_impl.h @@ -68,7 +68,7 @@ void UIRenderer::SetupModelAxisRow( std::vector Date: Wed, 24 Jun 2026 09:45:29 +0000 Subject: [PATCH 15/21] Add copyright comments --- src/viewer/assets/shaders/binormalaxis.frag | 2 ++ src/viewer/assets/shaders/binormalaxis.vert | 2 ++ src/viewer/assets/shaders/model_color.frag | 1 + src/viewer/assets/shaders/model_color.vert | 2 ++ src/viewer/assets/shaders/normalaxis.frag | 2 ++ src/viewer/assets/shaders/normalaxis.vert | 2 ++ src/viewer/assets/shaders/packed_tangent.inc | 2 ++ src/viewer/assets/shaders/packedtangentaxis.frag | 2 ++ src/viewer/assets/shaders/packedtangentaxis.vert | 2 ++ src/viewer/assets/shaders/packedtangentlegacyaxis.frag | 2 ++ src/viewer/assets/shaders/packedtangentlegacyaxis.vert | 2 ++ src/viewer/assets/shaders/tangentaxis.frag | 2 ++ src/viewer/assets/shaders/tangentaxis.vert | 2 ++ 13 files changed, 25 insertions(+) diff --git a/src/viewer/assets/shaders/binormalaxis.frag b/src/viewer/assets/shaders/binormalaxis.frag index b2d9d55..7a61cf8 100644 --- a/src/viewer/assets/shaders/binormalaxis.frag +++ b/src/viewer/assets/shaders/binormalaxis.frag @@ -1,3 +1,5 @@ +// Copyright (c) 2026 CCP ehf. + #version 450 // inputs diff --git a/src/viewer/assets/shaders/binormalaxis.vert b/src/viewer/assets/shaders/binormalaxis.vert index cc8b337..50cb428 100644 --- a/src/viewer/assets/shaders/binormalaxis.vert +++ b/src/viewer/assets/shaders/binormalaxis.vert @@ -1,3 +1,5 @@ +// Copyright (c) 2026 CCP ehf. + #version 450 #pragma multi_usage inBinormal diff --git a/src/viewer/assets/shaders/model_color.frag b/src/viewer/assets/shaders/model_color.frag index 40d7b35..46c7d21 100644 --- a/src/viewer/assets/shaders/model_color.frag +++ b/src/viewer/assets/shaders/model_color.frag @@ -1,3 +1,4 @@ +// Copyright (c) 2026 CCP ehf. #version 450 diff --git a/src/viewer/assets/shaders/model_color.vert b/src/viewer/assets/shaders/model_color.vert index 27453d7..5b07ff0 100644 --- a/src/viewer/assets/shaders/model_color.vert +++ b/src/viewer/assets/shaders/model_color.vert @@ -1,3 +1,5 @@ +// Copyright (c) 2026 CCP ehf. + #version 450 #pragma multi_usage inColor diff --git a/src/viewer/assets/shaders/normalaxis.frag b/src/viewer/assets/shaders/normalaxis.frag index b2d9d55..7a61cf8 100644 --- a/src/viewer/assets/shaders/normalaxis.frag +++ b/src/viewer/assets/shaders/normalaxis.frag @@ -1,3 +1,5 @@ +// Copyright (c) 2026 CCP ehf. + #version 450 // inputs diff --git a/src/viewer/assets/shaders/normalaxis.vert b/src/viewer/assets/shaders/normalaxis.vert index 6eb0712..928c175 100644 --- a/src/viewer/assets/shaders/normalaxis.vert +++ b/src/viewer/assets/shaders/normalaxis.vert @@ -1,3 +1,5 @@ +// Copyright (c) 2026 CCP ehf. + #version 450 #pragma multi_usage inNormal diff --git a/src/viewer/assets/shaders/packed_tangent.inc b/src/viewer/assets/shaders/packed_tangent.inc index 86ac37b..fa0ad94 100644 --- a/src/viewer/assets/shaders/packed_tangent.inc +++ b/src/viewer/assets/shaders/packed_tangent.inc @@ -1,3 +1,5 @@ +// Copyright (c) 2026 CCP ehf. + struct TangentSpace { vec3 tangent; vec3 binormal; diff --git a/src/viewer/assets/shaders/packedtangentaxis.frag b/src/viewer/assets/shaders/packedtangentaxis.frag index b2d9d55..7a61cf8 100644 --- a/src/viewer/assets/shaders/packedtangentaxis.frag +++ b/src/viewer/assets/shaders/packedtangentaxis.frag @@ -1,3 +1,5 @@ +// Copyright (c) 2026 CCP ehf. + #version 450 // inputs diff --git a/src/viewer/assets/shaders/packedtangentaxis.vert b/src/viewer/assets/shaders/packedtangentaxis.vert index 92f51b2..9d02476 100644 --- a/src/viewer/assets/shaders/packedtangentaxis.vert +++ b/src/viewer/assets/shaders/packedtangentaxis.vert @@ -1,3 +1,5 @@ +// Copyright (c) 2026 CCP ehf. + #version 450 #pragma multi_usage inPackedTangents diff --git a/src/viewer/assets/shaders/packedtangentlegacyaxis.frag b/src/viewer/assets/shaders/packedtangentlegacyaxis.frag index b2d9d55..7a61cf8 100644 --- a/src/viewer/assets/shaders/packedtangentlegacyaxis.frag +++ b/src/viewer/assets/shaders/packedtangentlegacyaxis.frag @@ -1,3 +1,5 @@ +// Copyright (c) 2026 CCP ehf. + #version 450 // inputs diff --git a/src/viewer/assets/shaders/packedtangentlegacyaxis.vert b/src/viewer/assets/shaders/packedtangentlegacyaxis.vert index ffe889d..c817ae9 100644 --- a/src/viewer/assets/shaders/packedtangentlegacyaxis.vert +++ b/src/viewer/assets/shaders/packedtangentlegacyaxis.vert @@ -1,3 +1,5 @@ +// Copyright (c) 2026 CCP ehf. + #version 450 #pragma multi_usage inPackedTangentsLegacy diff --git a/src/viewer/assets/shaders/tangentaxis.frag b/src/viewer/assets/shaders/tangentaxis.frag index b2d9d55..7a61cf8 100644 --- a/src/viewer/assets/shaders/tangentaxis.frag +++ b/src/viewer/assets/shaders/tangentaxis.frag @@ -1,3 +1,5 @@ +// Copyright (c) 2026 CCP ehf. + #version 450 // inputs diff --git a/src/viewer/assets/shaders/tangentaxis.vert b/src/viewer/assets/shaders/tangentaxis.vert index 411a9a4..9d1baf1 100644 --- a/src/viewer/assets/shaders/tangentaxis.vert +++ b/src/viewer/assets/shaders/tangentaxis.vert @@ -1,3 +1,5 @@ +// Copyright (c) 2026 CCP ehf. + #version 450 #pragma multi_usage inTangent From 1ee7569e64fd213b7be756e8ee2f014271ef59d4 Mon Sep 17 00:00:00 2001 From: Filipp Pavlov Date: Wed, 24 Jun 2026 09:49:01 +0000 Subject: [PATCH 16/21] Fix compile errors on clang --- src/viewer/rendering/uiRenderer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/viewer/rendering/uiRenderer.cpp b/src/viewer/rendering/uiRenderer.cpp index 5376ae2..cc4e5e8 100644 --- a/src/viewer/rendering/uiRenderer.cpp +++ b/src/viewer/rendering/uiRenderer.cpp @@ -466,8 +466,8 @@ std::vector> UIRenderer::GetAx { auto [index, checked] = axis.GetValue(); - auto foundState = std::find_if( combinedStates.begin(), combinedStates.end(), [&index]( const std::pair& pair ) { - return pair.first == index; + auto foundState = std::find_if( combinedStates.begin(), combinedStates.end(), [idx=index]( const std::pair& pair ) { + return pair.first == idx; } ); if( foundState == combinedStates.end() ) @@ -517,7 +517,7 @@ void UIRenderer::SetupVertexAxisRow( StateCollection>& } ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGui::Text( label ); + ImGui::TextUnformatted( label ); ImGui::TableNextColumn(); if( maxUsageIndex >= 0 ) From b45da2875377fb0a57480f4ef9602fca30f80feb Mon Sep 17 00:00:00 2001 From: Filipp Pavlov Date: Wed, 24 Jun 2026 09:55:24 +0000 Subject: [PATCH 17/21] Fix global normal/tangent/bitangent checkboxes not working --- src/viewer/rendering/uiRenderer_template_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/viewer/rendering/uiRenderer_template_impl.h b/src/viewer/rendering/uiRenderer_template_impl.h index 15f4380..4a6b865 100644 --- a/src/viewer/rendering/uiRenderer_template_impl.h +++ b/src/viewer/rendering/uiRenderer_template_impl.h @@ -79,7 +79,7 @@ void UIRenderer::SetupModelAxisRow( std::vectorsecond; OnChange( ImGui::CheckBoxTristate( ( std::string( "##tricheckbox" ) + name + std::to_string( i ) ).c_str(), &value ), [changeCallback, i, value]() { - changeCallback( value == ImGui::CheckBoxTriStateValue::CHECKED, i ); + changeCallback( value != ImGui::CheckBoxTriStateValue::CHECKED, i ); } ); ImGui::SetItemTooltip( "Toggles debug visualization for %s with usage index %d for all meshes (if available)", name.c_str(), i ); ImGui::SameLine(); From a5cc677e613fcba56ef9e20bf5228bc574e16c28 Mon Sep 17 00:00:00 2001 From: Filipp Pavlov Date: Wed, 24 Jun 2026 09:58:00 +0000 Subject: [PATCH 18/21] Format the file --- src/viewer/rendering/uiRenderer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/viewer/rendering/uiRenderer.cpp b/src/viewer/rendering/uiRenderer.cpp index cc4e5e8..4b51675 100644 --- a/src/viewer/rendering/uiRenderer.cpp +++ b/src/viewer/rendering/uiRenderer.cpp @@ -18,7 +18,7 @@ // ImGui is using a lot of variadic functions for text formatting, so we disable the cppcoreguidelines-pro-type-vararg lint for this file // NOLINTBEGIN(cppcoreguidelines-pro-type-vararg) -// +// // taken from https://github.com/ocornut/imgui/issues/2644 namespace ImGui { @@ -466,7 +466,7 @@ std::vector> UIRenderer::GetAx { auto [index, checked] = axis.GetValue(); - auto foundState = std::find_if( combinedStates.begin(), combinedStates.end(), [idx=index]( const std::pair& pair ) { + auto foundState = std::find_if( combinedStates.begin(), combinedStates.end(), [idx = index]( const std::pair& pair ) { return pair.first == idx; } ); From 3afc9d7e7f3f2ebbf4e965f3610b49ec5ce1b337 Mon Sep 17 00:00:00 2001 From: Filipp Pavlov Date: Wed, 24 Jun 2026 10:09:26 +0000 Subject: [PATCH 19/21] Fix compile error on clang --- src/viewer/rendering/sceneRenderer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/viewer/rendering/sceneRenderer.cpp b/src/viewer/rendering/sceneRenderer.cpp index 8402524..43f0ad0 100644 --- a/src/viewer/rendering/sceneRenderer.cpp +++ b/src/viewer/rendering/sceneRenderer.cpp @@ -127,8 +127,8 @@ void SceneRenderer::SetData( std::shared_ptr data, AppState& appStat } std::vector> shaders = ShaderCache::GetAvailableShaders( availableVertexElements ); - auto foundItem = std::find_if( shaders.begin(), shaders.end(), [¤tShaderName]( const auto& shaderNameAndDeclarations ) { - return shaderNameAndDeclarations.first == currentShaderName; + auto foundItem = std::find_if( shaders.begin(), shaders.end(), [name = currentShaderName]( const auto& shaderNameAndDeclarations ) { + return shaderNameAndDeclarations.first == name; } ); if( foundItem == shaders.end() && shaders.size() > 0 ) { From d8c7726b8a1e7b036e6f90f78ba607f7e60bfde2 Mon Sep 17 00:00:00 2001 From: Filipp Pavlov Date: Wed, 24 Jun 2026 11:53:59 +0000 Subject: [PATCH 20/21] Address PR comments --- src/viewer/assets/shaders/binormalaxis.frag | 3 ++- src/viewer/assets/shaders/binormalaxis.vert | 4 ++-- src/viewer/assets/shaders/model_color.frag | 2 +- src/viewer/assets/shaders/model_color.vert | 2 +- src/viewer/assets/shaders/model_packed_normal.vert | 4 ++-- .../assets/shaders/model_packed_normal_legacy.vert | 4 ++-- src/viewer/assets/shaders/normalaxis.frag | 3 ++- src/viewer/assets/shaders/normalaxis.vert | 8 ++++---- src/viewer/assets/shaders/packed_tangent.inc | 4 ++-- src/viewer/assets/shaders/packedtangentaxis.frag | 3 ++- src/viewer/assets/shaders/packedtangentaxis.vert | 4 ++-- src/viewer/assets/shaders/packedtangentlegacyaxis.frag | 3 ++- src/viewer/assets/shaders/packedtangentlegacyaxis.vert | 4 ++-- src/viewer/assets/shaders/tangentaxis.frag | 3 ++- src/viewer/assets/shaders/tangentaxis.vert | 10 +++++----- src/viewer/rendering/uiRenderer.cpp | 2 +- src/viewer/rendering/vulkan/shadercache.cpp | 2 +- 17 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/viewer/assets/shaders/binormalaxis.frag b/src/viewer/assets/shaders/binormalaxis.frag index 7a61cf8..9bcc4dc 100644 --- a/src/viewer/assets/shaders/binormalaxis.frag +++ b/src/viewer/assets/shaders/binormalaxis.frag @@ -8,6 +8,7 @@ layout( location = 0 ) in vec3 inFragColor; // Outputs layout( location = 0 ) out vec4 outFragColor; -void main() { +void main() +{ outFragColor = vec4( inFragColor, 1.0 ); } \ No newline at end of file diff --git a/src/viewer/assets/shaders/binormalaxis.vert b/src/viewer/assets/shaders/binormalaxis.vert index 50cb428..80c9f3d 100644 --- a/src/viewer/assets/shaders/binormalaxis.vert +++ b/src/viewer/assets/shaders/binormalaxis.vert @@ -28,13 +28,13 @@ layout ( location = 0 ) out vec3 outColor; void main() { - vec4 position = vec4(inPosition, 1.0); + vec4 position = vec4( inPosition, 1.0 ); if( gl_VertexIndex == 1 ) { position.xyz += axisConfig.scale * inBinormal; } - gl_Position = perframe.projectionMatrix * perframe.viewMatrix * position ; + gl_Position = perframe.projectionMatrix * perframe.viewMatrix * position; outColor = axisConfig.color; } \ No newline at end of file diff --git a/src/viewer/assets/shaders/model_color.frag b/src/viewer/assets/shaders/model_color.frag index 46c7d21..99e42b7 100644 --- a/src/viewer/assets/shaders/model_color.frag +++ b/src/viewer/assets/shaders/model_color.frag @@ -10,5 +10,5 @@ layout( location = 0 ) out vec4 outFragColor; void main() { - outFragColor = vec4( vec3( color ), 1.0 ); + outFragColor = vec4( color, 1.0 ); } \ No newline at end of file diff --git a/src/viewer/assets/shaders/model_color.vert b/src/viewer/assets/shaders/model_color.vert index 5b07ff0..c8496e2 100644 --- a/src/viewer/assets/shaders/model_color.vert +++ b/src/viewer/assets/shaders/model_color.vert @@ -22,5 +22,5 @@ layout( location = 0 ) out vec3 color; void main() { color = inColor; - gl_Position = perframe.projectionMatrix * perframe.viewMatrix *vec4( inPosition, 1.0 ); + gl_Position = perframe.projectionMatrix * perframe.viewMatrix * vec4( inPosition, 1.0 ); } \ No newline at end of file diff --git a/src/viewer/assets/shaders/model_packed_normal.vert b/src/viewer/assets/shaders/model_packed_normal.vert index 089dc5b..84c8493 100644 --- a/src/viewer/assets/shaders/model_packed_normal.vert +++ b/src/viewer/assets/shaders/model_packed_normal.vert @@ -15,7 +15,7 @@ layout( binding = 0 ) uniform PerFrame // Inputs layout( location = 0 ) in vec3 inPosition; -layout( location = 1 ) in vec4 inPackedTangents; +layout( location = 1 ) in vec4 inPackedTangents; // Outputs layout( location = 0 ) out vec3 viewPosition; @@ -26,6 +26,6 @@ void main() viewPosition = ( perframe.viewMatrix * vec4( inPosition, 1.0 ) ).xyz; gl_Position = perframe.projectionMatrix * vec4( viewPosition, 1.0 ); - TangentSpace space = unpackTangentSpace( inPackedTangents ); + TangentSpace space = UnpackTangentSpace( inPackedTangents ); normal = ( perframe.viewMatrix * vec4( space.normal, 0.0 ) ).xyz; } \ No newline at end of file diff --git a/src/viewer/assets/shaders/model_packed_normal_legacy.vert b/src/viewer/assets/shaders/model_packed_normal_legacy.vert index a2d5718..33fc43e 100644 --- a/src/viewer/assets/shaders/model_packed_normal_legacy.vert +++ b/src/viewer/assets/shaders/model_packed_normal_legacy.vert @@ -15,7 +15,7 @@ layout( binding = 0 ) uniform PerFrame // Inputs layout( location = 0 ) in vec3 inPosition; -layout( location = 1 ) in vec4 inPackedTangentsLegacy; +layout( location = 1 ) in vec4 inPackedTangentsLegacy; // Outputs layout( location = 0 ) out vec3 viewPosition; @@ -26,6 +26,6 @@ void main() viewPosition = ( perframe.viewMatrix * vec4( inPosition, 1.0 ) ).xyz; gl_Position = perframe.projectionMatrix * vec4( viewPosition, 1.0 ); - TangentSpace space = unpackTangentSpaceLegacy( inPackedTangentsLegacy ); + TangentSpace space = UnpackTangentSpaceLegacy( inPackedTangentsLegacy ); normal = ( perframe.viewMatrix * vec4( space.normal, 0.0 ) ).xyz; } \ No newline at end of file diff --git a/src/viewer/assets/shaders/normalaxis.frag b/src/viewer/assets/shaders/normalaxis.frag index 7a61cf8..9bcc4dc 100644 --- a/src/viewer/assets/shaders/normalaxis.frag +++ b/src/viewer/assets/shaders/normalaxis.frag @@ -8,6 +8,7 @@ layout( location = 0 ) in vec3 inFragColor; // Outputs layout( location = 0 ) out vec4 outFragColor; -void main() { +void main() +{ outFragColor = vec4( inFragColor, 1.0 ); } \ No newline at end of file diff --git a/src/viewer/assets/shaders/normalaxis.vert b/src/viewer/assets/shaders/normalaxis.vert index 928c175..3868bef 100644 --- a/src/viewer/assets/shaders/normalaxis.vert +++ b/src/viewer/assets/shaders/normalaxis.vert @@ -20,21 +20,21 @@ layout( binding = 1 ) uniform AxisConfig // Inputs // inputs are per instance, not per vertex -layout( location = 0 ) in vec3 inPosition; -layout( location = 1 ) in vec3 inNormal; +layout( location = 0 ) in vec3 inPosition; +layout( location = 1 ) in vec3 inNormal; // Output layout ( location = 0 ) out vec3 outColor; void main() { - vec4 position = vec4(inPosition, 1.0); + vec4 position = vec4( inPosition, 1.0 ); if( gl_VertexIndex == 1 ) { position.xyz += axisConfig.scale * inNormal; } - gl_Position = perframe.projectionMatrix * perframe.viewMatrix * position ; + gl_Position = perframe.projectionMatrix * perframe.viewMatrix * position; outColor = axisConfig.color; } \ No newline at end of file diff --git a/src/viewer/assets/shaders/packed_tangent.inc b/src/viewer/assets/shaders/packed_tangent.inc index fa0ad94..d35ee96 100644 --- a/src/viewer/assets/shaders/packed_tangent.inc +++ b/src/viewer/assets/shaders/packed_tangent.inc @@ -11,7 +11,7 @@ vec2 SinCosFloat( float angle ) return vec2( sin( angle ), cos( angle ) ); } -TangentSpace unpackTangentSpaceLegacy( vec4 tangents ) +TangentSpace UnpackTangentSpaceLegacy( vec4 tangents ) { vec4 angles = tangents * 6.28318530718 - 3.14159265359; vec4 sc0, sc1; @@ -30,7 +30,7 @@ TangentSpace unpackTangentSpaceLegacy( vec4 tangents ) return TangentSpace( tangent, binormal, normal ); } -TangentSpace unpackTangentSpace( vec4 t ) { +TangentSpace UnpackTangentSpace( vec4 t ) { // Heavily optimized shader code that constructs the TBN matrix. // Extract the xyz components and square them diff --git a/src/viewer/assets/shaders/packedtangentaxis.frag b/src/viewer/assets/shaders/packedtangentaxis.frag index 7a61cf8..9bcc4dc 100644 --- a/src/viewer/assets/shaders/packedtangentaxis.frag +++ b/src/viewer/assets/shaders/packedtangentaxis.frag @@ -8,6 +8,7 @@ layout( location = 0 ) in vec3 inFragColor; // Outputs layout( location = 0 ) out vec4 outFragColor; -void main() { +void main() +{ outFragColor = vec4( inFragColor, 1.0 ); } \ No newline at end of file diff --git a/src/viewer/assets/shaders/packedtangentaxis.vert b/src/viewer/assets/shaders/packedtangentaxis.vert index 9d02476..3c4d269 100644 --- a/src/viewer/assets/shaders/packedtangentaxis.vert +++ b/src/viewer/assets/shaders/packedtangentaxis.vert @@ -30,10 +30,10 @@ layout ( location = 0 ) out vec3 outColor; void main() { - vec4 position = vec4(inPosition, 1.0); + vec4 position = vec4( inPosition, 1.0 ); if( gl_VertexIndex == 1 ) { - TangentSpace space = unpackTangentSpace( inPackedTangents ); + TangentSpace space = UnpackTangentSpace( inPackedTangents ); vec3 axis = vec3(0.0); if( axisConfig.axisSelector == 0 ) { diff --git a/src/viewer/assets/shaders/packedtangentlegacyaxis.frag b/src/viewer/assets/shaders/packedtangentlegacyaxis.frag index 7a61cf8..9bcc4dc 100644 --- a/src/viewer/assets/shaders/packedtangentlegacyaxis.frag +++ b/src/viewer/assets/shaders/packedtangentlegacyaxis.frag @@ -8,6 +8,7 @@ layout( location = 0 ) in vec3 inFragColor; // Outputs layout( location = 0 ) out vec4 outFragColor; -void main() { +void main() +{ outFragColor = vec4( inFragColor, 1.0 ); } \ No newline at end of file diff --git a/src/viewer/assets/shaders/packedtangentlegacyaxis.vert b/src/viewer/assets/shaders/packedtangentlegacyaxis.vert index c817ae9..f5080f5 100644 --- a/src/viewer/assets/shaders/packedtangentlegacyaxis.vert +++ b/src/viewer/assets/shaders/packedtangentlegacyaxis.vert @@ -30,10 +30,10 @@ layout ( location = 0 ) out vec3 outColor; void main() { - vec4 position = vec4(inPosition, 1.0); + vec4 position = vec4( inPosition, 1.0 ); if( gl_VertexIndex == 1 ) { - TangentSpace space = unpackTangentSpaceLegacy( inPackedTangentsLegacy ); + TangentSpace space = UnpackTangentSpaceLegacy( inPackedTangentsLegacy ); vec3 axis = vec3(0.0); if( axisConfig.axisSelector == 0 ) { diff --git a/src/viewer/assets/shaders/tangentaxis.frag b/src/viewer/assets/shaders/tangentaxis.frag index 7a61cf8..9bcc4dc 100644 --- a/src/viewer/assets/shaders/tangentaxis.frag +++ b/src/viewer/assets/shaders/tangentaxis.frag @@ -8,6 +8,7 @@ layout( location = 0 ) in vec3 inFragColor; // Outputs layout( location = 0 ) out vec4 outFragColor; -void main() { +void main() +{ outFragColor = vec4( inFragColor, 1.0 ); } \ No newline at end of file diff --git a/src/viewer/assets/shaders/tangentaxis.vert b/src/viewer/assets/shaders/tangentaxis.vert index 9d1baf1..7bef8ed 100644 --- a/src/viewer/assets/shaders/tangentaxis.vert +++ b/src/viewer/assets/shaders/tangentaxis.vert @@ -20,21 +20,21 @@ layout( binding = 1 ) uniform AxisConfig // Inputs // inputs are per instance, not per vertex -layout( location = 0 ) in vec3 inPosition; -layout( location = 1 ) in vec3 inTangent; +layout( location = 0 ) in vec3 inPosition; +layout( location = 1 ) in vec3 inTangent; // Output layout ( location = 0 ) out vec3 outColor; -void main() +void main() { - vec4 position = vec4(inPosition, 1.0); + vec4 position = vec4( inPosition, 1.0 ); if( gl_VertexIndex == 1 ) { position.xyz += axisConfig.scale * inTangent; } - gl_Position = perframe.projectionMatrix * perframe.viewMatrix * position ; + gl_Position = perframe.projectionMatrix * perframe.viewMatrix * position; outColor = axisConfig.color; } \ No newline at end of file diff --git a/src/viewer/rendering/uiRenderer.cpp b/src/viewer/rendering/uiRenderer.cpp index 4b51675..a7e35d3 100644 --- a/src/viewer/rendering/uiRenderer.cpp +++ b/src/viewer/rendering/uiRenderer.cpp @@ -554,7 +554,7 @@ void UIRenderer::SetupVertexAxisRow( StateCollection>& } if( vertexAxisStates.empty() ) { - ImGui::BeginDisabled(); + ImGui::EndDisabled(); } } diff --git a/src/viewer/rendering/vulkan/shadercache.cpp b/src/viewer/rendering/vulkan/shadercache.cpp index b417d0d..ce22678 100644 --- a/src/viewer/rendering/vulkan/shadercache.cpp +++ b/src/viewer/rendering/vulkan/shadercache.cpp @@ -307,7 +307,7 @@ std::vector> auto inputDecl = shaderinputDeclaration; inputDecl.vertexDeclarations.push_back( element ); - if( multiUsageElements.size() != 0 ) + if( multiUsageElements.size() > 0 ) { inputDecl.shaderNameAddition = std::to_string( element.usageIndex ); } From f40d154f720432807e309cf643736c6ca4d30611 Mon Sep 17 00:00:00 2001 From: ccp-intern <180460643+ccp-intern@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:18:19 +0200 Subject: [PATCH 21/21] Update shadercache.cpp --- src/viewer/rendering/vulkan/shadercache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/viewer/rendering/vulkan/shadercache.cpp b/src/viewer/rendering/vulkan/shadercache.cpp index ce22678..36a17f3 100644 --- a/src/viewer/rendering/vulkan/shadercache.cpp +++ b/src/viewer/rendering/vulkan/shadercache.cpp @@ -307,7 +307,7 @@ std::vector> auto inputDecl = shaderinputDeclaration; inputDecl.vertexDeclarations.push_back( element ); - if( multiUsageElements.size() > 0 ) + if( multiUsageElements.size() > 1 ) { inputDecl.shaderNameAddition = std::to_string( element.usageIndex ); }