diff --git a/CMakeLists.txt b/CMakeLists.txt index 172a4c2d..0331c7c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,15 @@ enable_testing() option(XCENGINE_ENABLE_MONO_SCRIPTING "Build the Mono-based C# scripting runtime" ON) option(XCENGINE_BUILD_XCUI_EDITOR_APP "Build the XCUI editor shell app" ON) +if(XCENGINE_BUILD_XCUI_EDITOR_APP) + set(XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT_DEFAULT ON) +else() + set(XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT_DEFAULT OFF) +endif() +option( + XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT + "Build renderer-owned editor support features such as object-id picking" + ${XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT_DEFAULT}) set( XCENGINE_PHYSX_ROOT_DIR "${CMAKE_SOURCE_DIR}/engine/third_party/physx" diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 4b9222de..2b40c4a4 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -98,6 +98,15 @@ Containers::String GetUIDocumentDefaultRootTag(UIDocumentKind kind) message(STATUS "UIDocumentCompiler.cpp is missing; using generated fallback implementation for build stability.") endif() +set(XCENGINE_RENDERING_EDITOR_SUPPORT_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Picking/ObjectIdCodec.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Picking/RenderObjectIdRegistry.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Picking/RenderObjectIdRegistry.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPass.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPassResources.cpp +) + add_library(XCEngine STATIC # Core (Types, RefCounted, SmartPtr, Event) ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Types.h @@ -520,6 +529,7 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Planning/SceneRenderRequestPlanner.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Planning/SceneRenderRequestUtils.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Picking/ObjectIdCodec.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Picking/RenderObjectIdRegistry.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Extraction/RenderSceneExtractor.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Extraction/RenderSceneUtility.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraphTypes.h @@ -597,8 +607,6 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthOnlyPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinShadowCasterPass.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPass.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPassResources.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinVectorFullscreenPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinFinalColorPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Features/BuiltinGaussianSplatPass.cpp @@ -893,3 +901,77 @@ if(XCENGINE_HAS_NANOVDB) ) endif() +if(XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT) + target_compile_definitions(XCEngine PRIVATE + XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT=1 + ) + + add_library(XCEngineRenderingEditorSupport STATIC + ${XCENGINE_RENDERING_EDITOR_SUPPORT_SOURCES} + ) + + target_include_directories(XCEngineRenderingEditorSupport PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/third_party + ${CMAKE_CURRENT_SOURCE_DIR}/third_party/GLAD/include + ${CMAKE_CURRENT_SOURCE_DIR}/third_party/stb + ${CMAKE_CURRENT_SOURCE_DIR}/third_party/assimp/include + ) + + if(XCENGINE_HAS_NANOVDB) + target_include_directories(XCEngineRenderingEditorSupport PRIVATE + ${XCENGINE_NANOVDB_INCLUDE_DIR} + ) + target_compile_definitions(XCEngineRenderingEditorSupport PRIVATE + XCENGINE_HAS_NANOVDB=1 + ) + endif() + + if(XCENGINE_ENABLE_PHYSX) + target_include_directories(XCEngineRenderingEditorSupport PRIVATE + ${XCENGINE_PHYSX_INCLUDE_DIR} + ) + target_compile_definitions(XCEngineRenderingEditorSupport PRIVATE + XCENGINE_ENABLE_PHYSX=1 + ) + else() + target_compile_definitions(XCEngineRenderingEditorSupport PRIVATE + XCENGINE_ENABLE_PHYSX=0 + ) + endif() + + if(MSVC) + target_compile_options(XCEngineRenderingEditorSupport PRIVATE + /FS + /W3 + $<$:/Z7>) + set_target_properties(XCEngineRenderingEditorSupport PROPERTIES + MSVC_DEBUG_INFORMATION_FORMAT "$<$:Embedded>" + COMPILE_PDB_NAME "XCEngineRenderingEditorSupport-compile" + COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb" + COMPILE_PDB_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/Debug" + COMPILE_PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/Release" + COMPILE_PDB_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/MinSizeRel" + COMPILE_PDB_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/RelWithDebInfo" + ) + else() + target_compile_options(XCEngineRenderingEditorSupport PRIVATE -Wall) + endif() + + target_compile_definitions(XCEngineRenderingEditorSupport PRIVATE + XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT=1 + XCENGINE_SUPPORT_OPENGL + XCENGINE_SUPPORT_VULKAN + ) + + target_link_libraries(XCEngine PRIVATE + XCEngineRenderingEditorSupport + ) +else() + target_compile_definitions(XCEngine PRIVATE + XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT=0 + ) +endif() + diff --git a/engine/include/XCEngine/Rendering/Picking/ObjectIdCodec.h b/engine/include/XCEngine/Rendering/Picking/ObjectIdCodec.h index a3b8a52e..105c7d9f 100644 --- a/engine/include/XCEngine/Rendering/Picking/ObjectIdCodec.h +++ b/engine/include/XCEngine/Rendering/Picking/ObjectIdCodec.h @@ -3,8 +3,6 @@ #include #include -#include - namespace XCEngine { namespace Rendering { @@ -17,27 +15,6 @@ inline bool IsValidRenderObjectId(RenderObjectId renderObjectId) { return renderObjectId != kInvalidRenderObjectId; } -inline bool CanConvertRuntimeObjectIdToRenderObjectId(Core::uint64 runtimeObjectId) { - return runtimeObjectId != 0u && - runtimeObjectId <= static_cast(std::numeric_limits::max()); -} - -inline bool TryConvertRuntimeObjectIdToRenderObjectId( - Core::uint64 runtimeObjectId, - RenderObjectId& outRenderObjectId) { - outRenderObjectId = kInvalidRenderObjectId; - if (!CanConvertRuntimeObjectIdToRenderObjectId(runtimeObjectId)) { - return false; - } - - outRenderObjectId = static_cast(runtimeObjectId); - return true; -} - -inline Core::uint64 ConvertRenderObjectIdToRuntimeObjectId(RenderObjectId renderObjectId) { - return static_cast(renderObjectId); -} - inline EncodedObjectId EncodeRenderObjectIdToEncodedId(RenderObjectId renderObjectId) { return renderObjectId; } diff --git a/engine/include/XCEngine/Rendering/Picking/RenderObjectIdRegistry.h b/engine/include/XCEngine/Rendering/Picking/RenderObjectIdRegistry.h new file mode 100644 index 00000000..2eb2dfcc --- /dev/null +++ b/engine/include/XCEngine/Rendering/Picking/RenderObjectIdRegistry.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +#include +#include + +namespace XCEngine { +namespace Rendering { + +class RenderObjectIdRegistry { +public: + static RenderObjectIdRegistry& Get(); + + RenderObjectIdRegistry(const RenderObjectIdRegistry&) = delete; + RenderObjectIdRegistry& operator=(const RenderObjectIdRegistry&) = delete; + + RenderObjectId GetOrAllocateRenderObjectId(Core::uint64 runtimeObjectId); + bool TryGetRenderObjectId( + Core::uint64 runtimeObjectId, + RenderObjectId& outRenderObjectId) const; + bool TryResolveRuntimeObjectId( + RenderObjectId renderObjectId, + Core::uint64& outRuntimeObjectId) const; + Core::uint64 ResolveRuntimeObjectId(RenderObjectId renderObjectId) const; + + void Reset(); + Core::uint64 GetGeneration() const; + +private: + RenderObjectIdRegistry() = default; + + RenderObjectId AllocateRenderObjectIdLocked(); + void BumpGenerationLocked(); + + mutable std::mutex m_mutex = {}; + RenderObjectId m_nextRenderObjectId = 1u; + Core::uint64 m_generation = 1u; + std::unordered_map + m_renderObjectIdByRuntimeObjectId = {}; + std::unordered_map + m_runtimeObjectIdByRenderObjectId = {}; +}; + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/include/XCEngine/Resources/BuiltinResources.h b/engine/include/XCEngine/Resources/BuiltinResources.h index 72514396..0b77d9a1 100644 --- a/engine/include/XCEngine/Resources/BuiltinResources.h +++ b/engine/include/XCEngine/Resources/BuiltinResources.h @@ -34,9 +34,6 @@ Containers::String GetBuiltinUnlitShaderPath(); Containers::String GetBuiltinDepthOnlyShaderPath(); Containers::String GetBuiltinShadowCasterShaderPath(); Containers::String GetBuiltinObjectIdShaderPath(); -Containers::String GetBuiltinObjectIdOutlineShaderPath(); -Containers::String GetBuiltinSelectionMaskShaderPath(); -Containers::String GetBuiltinSelectionOutlineShaderPath(); Containers::String GetBuiltinSkyboxShaderPath(); Containers::String GetBuiltinGaussianSplatShaderPath(); Containers::String GetBuiltinGaussianSplatUtilitiesShaderPath(); diff --git a/engine/src/Core/Asset/AssetDatabase.cpp b/engine/src/Core/Asset/AssetDatabase.cpp index a77a3613..ed7a78b9 100644 --- a/engine/src/Core/Asset/AssetDatabase.cpp +++ b/engine/src/Core/Asset/AssetDatabase.cpp @@ -683,10 +683,9 @@ std::vector CollectBuiltinShaderAssetPaths() { GetBuiltinUnlitShaderPath(), GetBuiltinDepthOnlyShaderPath(), GetBuiltinShadowCasterShaderPath(), +#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT GetBuiltinObjectIdShaderPath(), - GetBuiltinObjectIdOutlineShaderPath(), - GetBuiltinSelectionMaskShaderPath(), - GetBuiltinSelectionOutlineShaderPath(), +#endif GetBuiltinSkyboxShaderPath(), GetBuiltinGaussianSplatShaderPath(), GetBuiltinGaussianSplatUtilitiesShaderPath(), diff --git a/engine/src/Rendering/Execution/CameraRenderer.cpp b/engine/src/Rendering/Execution/CameraRenderer.cpp index de5d08ed..65032fc5 100644 --- a/engine/src/Rendering/Execution/CameraRenderer.cpp +++ b/engine/src/Rendering/Execution/CameraRenderer.cpp @@ -5,7 +5,9 @@ #include "Rendering/Execution/Internal/CameraFrameGraph/Executor.h" #include "Rendering/GraphicsSettingsState.h" #include "Rendering/Internal/RenderPipelineFactory.h" +#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT #include "Rendering/Passes/BuiltinObjectIdPass.h" +#endif #include "Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h" #include "Rendering/RenderPipelineAsset.h" #include "Rendering/RenderSurface.h" @@ -31,11 +33,15 @@ bool IsManagedPipelineAsset( } void ConfigureTopLevelToolingPasses(RenderPipeline& pipeline) { +#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT // Object-id is a tooling/editor request. It should live on the // top-level pipeline used by CameraRenderer, not on a scene backend. pipeline.SetCameraFrameStandalonePass( CameraFrameStage::ObjectId, std::make_unique()); +#else + (void)pipeline; +#endif } RenderPipelineStageSupportContext BuildStageSupportContext( @@ -247,6 +253,7 @@ bool CameraRenderer::Render( return false; } } +#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT if (plan.request.objectId.IsRequested() && !plan.request.objectId.IsValid()) { Debug::Logger::Get().Error( @@ -254,6 +261,14 @@ bool CameraRenderer::Render( "CameraRenderer::Render failed: object-id request invalid"); return false; } +#else + if (plan.request.objectId.IsRequested()) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "CameraRenderer::Render failed: object-id support is disabled in this build"); + return false; + } +#endif DirectionalShadowExecutionState shadowState = {}; if (m_directionalShadowRuntime == nullptr || diff --git a/engine/src/Rendering/Extraction/RenderSceneExtractor.cpp b/engine/src/Rendering/Extraction/RenderSceneExtractor.cpp index 2f19dd9d..0735f5f2 100644 --- a/engine/src/Rendering/Extraction/RenderSceneExtractor.cpp +++ b/engine/src/Rendering/Extraction/RenderSceneExtractor.cpp @@ -128,7 +128,13 @@ bool CompareVisibleGaussianSplats( void BuildRendererLists(RenderSceneData& sceneData) { sceneData.cullingResults.Clear(); - sceneData.cullingResults.rendererLists.reserve(5u); + sceneData.cullingResults.rendererLists.reserve( +#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT + 5u +#else + 4u +#endif + ); sceneData.cullingResults.rendererLists.push_back( BuildRendererList(RendererListType::AllVisible, sceneData.visibleItems)); sceneData.cullingResults.rendererLists.push_back( @@ -137,8 +143,10 @@ void BuildRendererLists(RenderSceneData& sceneData) { BuildRendererList(RendererListType::Transparent, sceneData.visibleItems)); sceneData.cullingResults.rendererLists.push_back( BuildRendererList(RendererListType::ShadowCaster, sceneData.visibleItems)); +#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT sceneData.cullingResults.rendererLists.push_back( BuildRendererList(RendererListType::ObjectId, sceneData.visibleItems)); +#endif } } // namespace diff --git a/engine/src/Rendering/Extraction/RenderSceneUtility.cpp b/engine/src/Rendering/Extraction/RenderSceneUtility.cpp index ea468125..58963f4e 100644 --- a/engine/src/Rendering/Extraction/RenderSceneUtility.cpp +++ b/engine/src/Rendering/Extraction/RenderSceneUtility.cpp @@ -9,6 +9,9 @@ #include "Components/TransformComponent.h" #include "Components/VolumeRendererComponent.h" #include "Rendering/Materials/RenderMaterialResolve.h" +#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT +#include "Rendering/Picking/RenderObjectIdRegistry.h" +#endif #include "Scene/Scene.h" #include @@ -19,9 +22,13 @@ namespace Rendering { namespace { RenderObjectId BuildRenderObjectIdOrInvalid(const Components::GameObject& gameObject) { - RenderObjectId renderObjectId = kInvalidRenderObjectId; - TryConvertRuntimeObjectIdToRenderObjectId(gameObject.GetID(), renderObjectId); - return renderObjectId; +#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT + return RenderObjectIdRegistry::Get().GetOrAllocateRenderObjectId( + gameObject.GetID()); +#else + (void)gameObject; + return kInvalidRenderObjectId; +#endif } } // namespace diff --git a/engine/src/Rendering/Picking/RenderObjectIdRegistry.cpp b/engine/src/Rendering/Picking/RenderObjectIdRegistry.cpp new file mode 100644 index 00000000..3563a85a --- /dev/null +++ b/engine/src/Rendering/Picking/RenderObjectIdRegistry.cpp @@ -0,0 +1,133 @@ +#include + +#include + +namespace XCEngine { +namespace Rendering { + +RenderObjectIdRegistry& RenderObjectIdRegistry::Get() { + static RenderObjectIdRegistry registry = {}; + return registry; +} + +RenderObjectId RenderObjectIdRegistry::GetOrAllocateRenderObjectId( + Core::uint64 runtimeObjectId) { + if (runtimeObjectId == 0u) { + return kInvalidRenderObjectId; + } + + std::lock_guard lock(m_mutex); + const auto existing = + m_renderObjectIdByRuntimeObjectId.find(runtimeObjectId); + if (existing != m_renderObjectIdByRuntimeObjectId.end()) { + return existing->second; + } + + const RenderObjectId renderObjectId = AllocateRenderObjectIdLocked(); + if (!IsValidRenderObjectId(renderObjectId)) { + return kInvalidRenderObjectId; + } + + m_renderObjectIdByRuntimeObjectId.emplace(runtimeObjectId, renderObjectId); + m_runtimeObjectIdByRenderObjectId.emplace(renderObjectId, runtimeObjectId); + BumpGenerationLocked(); + return renderObjectId; +} + +bool RenderObjectIdRegistry::TryGetRenderObjectId( + Core::uint64 runtimeObjectId, + RenderObjectId& outRenderObjectId) const { + outRenderObjectId = kInvalidRenderObjectId; + if (runtimeObjectId == 0u) { + return false; + } + + std::lock_guard lock(m_mutex); + const auto existing = + m_renderObjectIdByRuntimeObjectId.find(runtimeObjectId); + if (existing == m_renderObjectIdByRuntimeObjectId.end()) { + return false; + } + + outRenderObjectId = existing->second; + return true; +} + +bool RenderObjectIdRegistry::TryResolveRuntimeObjectId( + RenderObjectId renderObjectId, + Core::uint64& outRuntimeObjectId) const { + outRuntimeObjectId = 0u; + if (!IsValidRenderObjectId(renderObjectId)) { + return false; + } + + std::lock_guard lock(m_mutex); + const auto existing = + m_runtimeObjectIdByRenderObjectId.find(renderObjectId); + if (existing == m_runtimeObjectIdByRenderObjectId.end()) { + return false; + } + + outRuntimeObjectId = existing->second; + return true; +} + +Core::uint64 RenderObjectIdRegistry::ResolveRuntimeObjectId( + RenderObjectId renderObjectId) const { + Core::uint64 runtimeObjectId = 0u; + TryResolveRuntimeObjectId(renderObjectId, runtimeObjectId); + return runtimeObjectId; +} + +void RenderObjectIdRegistry::Reset() { + std::lock_guard lock(m_mutex); + m_nextRenderObjectId = 1u; + m_renderObjectIdByRuntimeObjectId.clear(); + m_runtimeObjectIdByRenderObjectId.clear(); + BumpGenerationLocked(); +} + +Core::uint64 RenderObjectIdRegistry::GetGeneration() const { + std::lock_guard lock(m_mutex); + return m_generation; +} + +RenderObjectId RenderObjectIdRegistry::AllocateRenderObjectIdLocked() { + constexpr Core::uint64 kMaxAssignableRenderObjectIdCount = + static_cast(std::numeric_limits::max()) - 1u; + if (m_runtimeObjectIdByRenderObjectId.size() >= + static_cast(kMaxAssignableRenderObjectIdCount)) { + return kInvalidRenderObjectId; + } + + for (Core::uint64 attempt = 0u; + attempt < kMaxAssignableRenderObjectIdCount; + ++attempt) { + const RenderObjectId candidate = m_nextRenderObjectId; + ++m_nextRenderObjectId; + if (m_nextRenderObjectId == kInvalidRenderObjectId) { + m_nextRenderObjectId = 1u; + } + + if (!IsValidRenderObjectId(candidate)) { + continue; + } + + if (m_runtimeObjectIdByRenderObjectId.find(candidate) == + m_runtimeObjectIdByRenderObjectId.end()) { + return candidate; + } + } + + return kInvalidRenderObjectId; +} + +void RenderObjectIdRegistry::BumpGenerationLocked() { + ++m_generation; + if (m_generation == 0u) { + m_generation = 1u; + } +} + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/src/Resources/BuiltinResources.cpp b/engine/src/Resources/BuiltinResources.cpp index 19a2e60b..d73decb7 100644 --- a/engine/src/Resources/BuiltinResources.cpp +++ b/engine/src/Resources/BuiltinResources.cpp @@ -32,9 +32,6 @@ constexpr const char* kBuiltinUnlitShaderPath = "builtin://shaders/unlit"; constexpr const char* kBuiltinDepthOnlyShaderPath = "builtin://shaders/depth-only"; constexpr const char* kBuiltinShadowCasterShaderPath = "builtin://shaders/shadow-caster"; constexpr const char* kBuiltinObjectIdShaderPath = "builtin://shaders/object-id"; -constexpr const char* kBuiltinObjectIdOutlineShaderPath = "builtin://shaders/object-id-outline"; -constexpr const char* kBuiltinSelectionMaskShaderPath = "builtin://shaders/selection-mask"; -constexpr const char* kBuiltinSelectionOutlineShaderPath = "builtin://shaders/selection-outline"; constexpr const char* kBuiltinSkyboxShaderPath = "builtin://shaders/skybox"; constexpr const char* kBuiltinGaussianSplatShaderPath = "builtin://shaders/gaussian-splat"; constexpr const char* kBuiltinGaussianSplatUtilitiesShaderPath = @@ -63,12 +60,6 @@ constexpr const char* kBuiltinShadowCasterShaderAssetRelativePath = "engine/assets/builtin/shaders/shadow-caster.shader"; constexpr const char* kBuiltinObjectIdShaderAssetRelativePath = "engine/assets/builtin/shaders/object-id.shader"; -constexpr const char* kBuiltinObjectIdOutlineShaderAssetRelativePath = - "engine/assets/builtin/shaders/object-id-outline.shader"; -constexpr const char* kBuiltinSelectionMaskShaderAssetRelativePath = - "engine/assets/builtin/shaders/selection-mask.shader"; -constexpr const char* kBuiltinSelectionOutlineShaderAssetRelativePath = - "engine/assets/builtin/shaders/selection-outline.shader"; constexpr const char* kBuiltinSkyboxShaderAssetRelativePath = "engine/assets/builtin/shaders/skybox.shader"; constexpr const char* kBuiltinGaussianSplatShaderAssetRelativePath = @@ -156,18 +147,11 @@ const char* GetBuiltinShaderAssetRelativePath(const Containers::String& builtinS if (builtinShaderPath == Containers::String(kBuiltinShadowCasterShaderPath)) { return kBuiltinShadowCasterShaderAssetRelativePath; } +#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT if (builtinShaderPath == Containers::String(kBuiltinObjectIdShaderPath)) { return kBuiltinObjectIdShaderAssetRelativePath; } - if (builtinShaderPath == Containers::String(kBuiltinObjectIdOutlineShaderPath)) { - return kBuiltinObjectIdOutlineShaderAssetRelativePath; - } - if (builtinShaderPath == Containers::String(kBuiltinSelectionMaskShaderPath)) { - return kBuiltinSelectionMaskShaderAssetRelativePath; - } - if (builtinShaderPath == Containers::String(kBuiltinSelectionOutlineShaderPath)) { - return kBuiltinSelectionOutlineShaderAssetRelativePath; - } +#endif if (builtinShaderPath == Containers::String(kBuiltinSkyboxShaderPath)) { return kBuiltinSkyboxShaderAssetRelativePath; } @@ -731,19 +715,12 @@ Shader* BuildBuiltinShadowCasterShader(const Containers::String& path) { } Shader* BuildBuiltinObjectIdShader(const Containers::String& path) { +#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT return TryLoadBuiltinShaderFromAsset(path); -} - -Shader* BuildBuiltinObjectIdOutlineShader(const Containers::String& path) { - return TryLoadBuiltinShaderFromAsset(path); -} - -Shader* BuildBuiltinSelectionMaskShader(const Containers::String& path) { - return TryLoadBuiltinShaderFromAsset(path); -} - -Shader* BuildBuiltinSelectionOutlineShader(const Containers::String& path) { - return TryLoadBuiltinShaderFromAsset(path); +#else + (void)path; + return nullptr; +#endif } Shader* BuildBuiltinSkyboxShader(const Containers::String& path) { @@ -852,22 +829,12 @@ bool TryGetBuiltinShaderPathByShaderName( outPath = GetBuiltinShadowCasterShaderPath(); return true; } +#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT if (shaderName == "Builtin Object Id") { outPath = GetBuiltinObjectIdShaderPath(); return true; } - if (shaderName == "Builtin Object Id Outline") { - outPath = GetBuiltinObjectIdOutlineShaderPath(); - return true; - } - if (shaderName == "Builtin Selection Mask") { - outPath = GetBuiltinSelectionMaskShaderPath(); - return true; - } - if (shaderName == "Builtin Selection Outline") { - outPath = GetBuiltinSelectionOutlineShaderPath(); - return true; - } +#endif if (shaderName == "Builtin Skybox") { outPath = GetBuiltinSkyboxShaderPath(); return true; @@ -948,19 +915,11 @@ Containers::String GetBuiltinShadowCasterShaderPath() { } Containers::String GetBuiltinObjectIdShaderPath() { +#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT return Containers::String(kBuiltinObjectIdShaderPath); -} - -Containers::String GetBuiltinObjectIdOutlineShaderPath() { - return Containers::String(kBuiltinObjectIdOutlineShaderPath); -} - -Containers::String GetBuiltinSelectionMaskShaderPath() { - return Containers::String(kBuiltinSelectionMaskShaderPath); -} - -Containers::String GetBuiltinSelectionOutlineShaderPath() { - return Containers::String(kBuiltinSelectionOutlineShaderPath); +#else + return Containers::String(); +#endif } Containers::String GetBuiltinSkyboxShaderPath() { @@ -1083,14 +1042,10 @@ LoadResult CreateBuiltinShaderResource(const Containers::String& path) { shader = BuildBuiltinDepthOnlyShader(path); } else if (path == GetBuiltinShadowCasterShaderPath()) { shader = BuildBuiltinShadowCasterShader(path); +#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT } else if (path == GetBuiltinObjectIdShaderPath()) { shader = BuildBuiltinObjectIdShader(path); - } else if (path == GetBuiltinObjectIdOutlineShaderPath()) { - shader = BuildBuiltinObjectIdOutlineShader(path); - } else if (path == GetBuiltinSelectionMaskShaderPath()) { - shader = BuildBuiltinSelectionMaskShader(path); - } else if (path == GetBuiltinSelectionOutlineShaderPath()) { - shader = BuildBuiltinSelectionOutlineShader(path); +#endif } else if (path == GetBuiltinSkyboxShaderPath()) { shader = BuildBuiltinSkyboxShader(path); } else if (path == GetBuiltinGaussianSplatShaderPath()) { diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index e5736663..795c94ef 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -722,9 +722,6 @@ bool TryResolveManagedCameraFrameStage( case static_cast(Rendering::CameraFrameStage::FinalOutput): outStage = Rendering::CameraFrameStage::FinalOutput; return true; - case static_cast(Rendering::CameraFrameStage::ObjectId): - outStage = Rendering::CameraFrameStage::ObjectId; - return true; case static_cast(Rendering::CameraFrameStage::PostScenePasses): outStage = Rendering::CameraFrameStage::PostScenePasses; return true; @@ -1136,7 +1133,7 @@ struct ManagedFilteringSettingsData { int32_t renderQueueMax = std::numeric_limits::max(); uint32_t renderLayerMask = std::numeric_limits::max(); uint8_t requireShadowCasting = 0u; - uint8_t requireRenderObjectId = 0u; + uint8_t reservedEditorObjectIdFilter = 0u; }; static_assert( @@ -1303,6 +1300,80 @@ Rendering::RenderStateMask ResolveManagedRenderStateMask( value & knownMask); } +bool TryResolveManagedRendererListType( + uint32_t value, + Rendering::RendererListType& outType) { + switch (value) { + case static_cast(Rendering::RendererListType::AllVisible): + outType = Rendering::RendererListType::AllVisible; + return true; + case static_cast(Rendering::RendererListType::Opaque): + outType = Rendering::RendererListType::Opaque; + return true; + case static_cast(Rendering::RendererListType::Transparent): + outType = Rendering::RendererListType::Transparent; + return true; + case static_cast(Rendering::RendererListType::ShadowCaster): + outType = Rendering::RendererListType::ShadowCaster; + return true; + default: + outType = Rendering::RendererListType::AllVisible; + return false; + } +} + +Rendering::RendererSortMode ResolveManagedRendererSortMode( + uint32_t value) { + switch (value) { + case static_cast(Rendering::RendererSortMode::FrontToBack): + return Rendering::RendererSortMode::FrontToBack; + case static_cast(Rendering::RendererSortMode::BackToFront): + return Rendering::RendererSortMode::BackToFront; + case static_cast(Rendering::RendererSortMode::None): + default: + return Rendering::RendererSortMode::None; + } +} + +Rendering::FilteringSettings BuildManagedFilteringSettings( + const ManagedFilteringSettingsData& filteringData) { + Rendering::FilteringSettings filteringSettings = {}; + filteringSettings.renderQueueMin = filteringData.renderQueueMin; + filteringSettings.renderQueueMax = filteringData.renderQueueMax; + filteringSettings.renderLayerMask = filteringData.renderLayerMask; + filteringSettings.requireShadowCasting = + filteringData.requireShadowCasting != 0u; + return filteringSettings; +} + +Rendering::SortingSettings BuildManagedSortingSettings( + const ManagedSortingSettingsData& sortingData) { + Rendering::SortingSettings sortingSettings = {}; + sortingSettings.sortMode = + ResolveManagedRendererSortMode( + sortingData.sortMode); + return sortingSettings; +} + +bool TryBuildManagedRendererListDesc( + const ManagedRendererListDescData& rendererListDescData, + Rendering::RendererListDesc& outRendererListDesc) { + outRendererListDesc = {}; + if (!TryResolveManagedRendererListType( + rendererListDescData.type, + outRendererListDesc.type)) { + return false; + } + + outRendererListDesc.filtering = + BuildManagedFilteringSettings( + rendererListDescData.filtering); + outRendererListDesc.sorting = + BuildManagedSortingSettings( + rendererListDescData.sorting); + return true; +} + Rendering::DepthState BuildManagedDepthState( const ManagedDepthStateData& depthStateData) { Rendering::DepthState depthState = {}; @@ -5571,9 +5642,14 @@ InternalCall_Rendering_ScriptableRenderContext_DrawRenderersByDesc( Rendering::DrawSettings drawSettings = {}; drawSettings.scenePhase = static_cast(scenePhase); - drawSettings.rendererListDesc = - *reinterpret_cast( - rendererListDescData); + if (!TryBuildManagedRendererListDesc( + *rendererListDescData, + drawSettings.rendererListDesc)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "ScriptableRenderContext DrawRenderers received an unsupported renderer-list type"); + return 0; + } const std::string overrideMaterialPathUtf8 = MonoStringToUtf8(overrideMaterialPath); if (!overrideMaterialPathUtf8.empty()) { diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererFeature.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererFeature.cs index 120a83f8..788faf01 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererFeature.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererFeature.cs @@ -284,7 +284,6 @@ namespace XCEngine.Rendering.Universal hash = Combine(hash, value.renderQueueMax); hash = Combine(hash, value.renderLayerMask); hash = Combine(hash, value.requireShadowCasting); - hash = Combine(hash, value.requireRenderObjectId); return hash; } diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/CameraFrameStage.cs b/managed/XCEngine.ScriptCore/Rendering/Core/CameraFrameStage.cs index fb6c1b56..a1a72eb9 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Core/CameraFrameStage.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Core/CameraFrameStage.cs @@ -10,7 +10,6 @@ namespace XCEngine.Rendering MainScene = 3, PostProcess = 4, FinalOutput = 5, - ObjectId = 6, PostScenePasses = 7, OverlayPasses = 8 } diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/FilteringSettings.cs b/managed/XCEngine.ScriptCore/Rendering/Core/FilteringSettings.cs index 53828b31..57f806e2 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Core/FilteringSettings.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Core/FilteringSettings.cs @@ -12,8 +12,9 @@ namespace XCEngine.Rendering [MarshalAs(UnmanagedType.I1)] public bool requireShadowCasting; + // Preserve native bridge layout; runtime scripts do not expose object-id filtering. [MarshalAs(UnmanagedType.I1)] - public bool requireRenderObjectId; + private bool m_reservedEditorObjectIdFilter; public RenderQueueRange renderQueueRange { @@ -58,9 +59,6 @@ namespace XCEngine.Rendering case RendererListType.ShadowCaster: filteringSettings.requireShadowCasting = true; break; - case RendererListType.ObjectId: - filteringSettings.requireRenderObjectId = true; - break; } return filteringSettings; diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/RendererListType.cs b/managed/XCEngine.ScriptCore/Rendering/Core/RendererListType.cs index 73eb63b2..584118c3 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Core/RendererListType.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Core/RendererListType.cs @@ -5,7 +5,6 @@ namespace XCEngine.Rendering AllVisible = 0, Opaque = 1, Transparent = 2, - ShadowCaster = 3, - ObjectId = 4 + ShadowCaster = 3 } } diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/SortingSettings.cs b/managed/XCEngine.ScriptCore/Rendering/Core/SortingSettings.cs index 28da0ff8..8d0c7795 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Core/SortingSettings.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Core/SortingSettings.cs @@ -16,7 +16,6 @@ namespace XCEngine.Rendering { case RendererListType.Opaque: case RendererListType.ShadowCaster: - case RendererListType.ObjectId: sortingSettings.sortMode = RendererSortMode.FrontToBack; break; diff --git a/new_editor/CMakeLists.txt b/new_editor/CMakeLists.txt index ffe40d76..46d53a39 100644 --- a/new_editor/CMakeLists.txt +++ b/new_editor/CMakeLists.txt @@ -19,6 +19,12 @@ function(xcui_editor_apply_common_target_settings target visibility) endif() endfunction() +if(XCENGINE_BUILD_XCUI_EDITOR_APP AND + NOT XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT) + message(FATAL_ERROR + "XCUIEditorApp requires XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT=ON") +endif() + set(XCUI_EDITOR_FOUNDATION_SOURCES src/Foundation/UIEditorCommandDispatcher.cpp src/Foundation/UIEditorCommandRegistry.cpp @@ -88,6 +94,7 @@ set(XCUI_EDITOR_MENU_SOURCES set(XCUI_EDITOR_PANEL_SOURCES src/Panels/UIEditorPanelContentHost.cpp src/Panels/UIEditorPanelFrame.cpp + src/Panels/UIEditorHostedPanelDispatch.cpp src/Panels/UIEditorPanelHostLifecycle.cpp src/Panels/UIEditorPanelRegistry.cpp ) @@ -110,6 +117,7 @@ set(XCUI_EDITOR_VIEWPORT_SOURCES set(XCUI_EDITOR_WORKSPACE_SOURCES src/Workspace/UIEditorWorkspaceCompose.cpp src/Workspace/UIEditorWorkspaceController.cpp + src/Workspace/UIEditorDetachedWindowPolicy.cpp src/Workspace/UIEditorWorkspaceInteraction.cpp src/Workspace/UIEditorWorkspaceInputOwner.cpp src/Workspace/UIEditorWorkspaceLayoutPersistence.cpp @@ -313,6 +321,7 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP) target_link_libraries(XCUIEditorAppCore PRIVATE XCUIEditorLib + XCEngineRenderingEditorSupport ) add_library(XCUIEditorAppLib STATIC diff --git a/new_editor/app/Features/Scene/SceneViewportController.cpp b/new_editor/app/Features/Scene/SceneViewportController.cpp index 7ec883d6..5344d17f 100644 --- a/new_editor/app/Features/Scene/SceneViewportController.cpp +++ b/new_editor/app/Features/Scene/SceneViewportController.cpp @@ -442,8 +442,8 @@ void SceneViewportController::Update( viewportFrame->viewportShellFrame.requestedViewportSize, transition.localPointerPosition); if (pickResult.status == ViewportObjectIdPickStatus::Success) { - if (pickResult.entityId != 0u) { - sceneRuntime.SetSelection(pickResult.entityId); + if (pickResult.HasResolvedEntity()) { + sceneRuntime.SetSelection(pickResult.resolvedEntityId); } else { sceneRuntime.ClearSelection(); } diff --git a/new_editor/app/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp b/new_editor/app/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp index e46cd4f1..3c6cdde5 100644 --- a/new_editor/app/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp +++ b/new_editor/app/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp @@ -1,7 +1,9 @@ #include "Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.h" +#include "Rendering/Viewport/SceneViewportResourcePaths.h" #include "Rendering/Viewport/ViewportRenderTargets.h" +#include #include #include #include @@ -12,14 +14,12 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include #include @@ -36,7 +36,7 @@ public: SceneViewportSelectionMaskPass() : BuiltinDepthStylePassBase( ::XCEngine::Rendering::BuiltinMaterialPass::SelectionMask, - ::XCEngine::Resources::GetBuiltinSelectionMaskShaderPath()) { + GetSceneViewportSelectionMaskShaderPath()) { } const char* GetName() const override { @@ -51,12 +51,8 @@ public: m_selectedObjectIds.clear(); m_selectedObjectIds.reserve(selectedObjectIds.size()); for (const std::uint64_t selectedObjectId : selectedObjectIds) { - ::XCEngine::Rendering::RenderObjectId renderObjectId = - ::XCEngine::Rendering::kInvalidRenderObjectId; - if (::XCEngine::Rendering::TryConvertRuntimeObjectIdToRenderObjectId( - selectedObjectId, - renderObjectId)) { - m_selectedObjectIds.push_back(renderObjectId); + if (selectedObjectId != 0u) { + m_selectedObjectIds.push_back(selectedObjectId); } } @@ -84,19 +80,23 @@ public: protected: bool ShouldRenderVisibleItem( const ::XCEngine::Rendering::VisibleRenderItem& visibleItem) const override { - if (!::XCEngine::Rendering::IsValidRenderObjectId( - visibleItem.renderObjectId)) { + if (visibleItem.gameObject == nullptr) { + return false; + } + + const std::uint64_t runtimeObjectId = visibleItem.gameObject->GetID(); + if (runtimeObjectId == 0u) { return false; } return std::find( m_selectedObjectIds.begin(), m_selectedObjectIds.end(), - visibleItem.renderObjectId) != m_selectedObjectIds.end(); + runtimeObjectId) != m_selectedObjectIds.end(); } private: - std::vector<::XCEngine::Rendering::RenderObjectId> m_selectedObjectIds = {}; + std::vector m_selectedObjectIds = {}; }; const ::XCEngine::Resources::ShaderPass* FindSelectionOutlineCompatiblePass( @@ -272,8 +272,7 @@ public: Impl() : m_selectionMaskPass(std::make_unique()) - , m_shaderPath( - ::XCEngine::Resources::GetBuiltinSelectionOutlineShaderPath()) { + , m_shaderPath(GetSceneViewportSelectionOutlineShaderPath()) { } void Shutdown() { diff --git a/new_editor/app/Rendering/Viewport/SceneViewportRenderPlan.h b/new_editor/app/Rendering/Viewport/SceneViewportRenderPlan.h index 53636aa4..304a398e 100644 --- a/new_editor/app/Rendering/Viewport/SceneViewportRenderPlan.h +++ b/new_editor/app/Rendering/Viewport/SceneViewportRenderPlan.h @@ -185,6 +185,9 @@ inline void MarkSceneViewportRenderSuccess( ? ::XCEngine::RHI::ResourceStates::PixelShaderResource : ::XCEngine::RHI::ResourceStates::Common; targets.hasValidObjectIdFrame = framePlan.request.objectId.IsRequested(); + if (!targets.hasValidObjectIdFrame) { + targets.objectIdFrameSerial = 0u; + } } } // namespace XCEngine::UI::Editor::App diff --git a/new_editor/app/Rendering/Viewport/SceneViewportRenderService.cpp b/new_editor/app/Rendering/Viewport/SceneViewportRenderService.cpp index 68f28552..e1e5ef21 100644 --- a/new_editor/app/Rendering/Viewport/SceneViewportRenderService.cpp +++ b/new_editor/app/Rendering/Viewport/SceneViewportRenderService.cpp @@ -1,6 +1,8 @@ #include "Rendering/Viewport/SceneViewportRenderService.h" +#include #include +#include #include @@ -55,10 +57,20 @@ void SceneViewportRenderService::Shutdown() { m_device = nullptr; m_lastTargets = nullptr; m_lastRenderContext = {}; + m_lastObjectIdScene = nullptr; + m_lastObjectIdCamera = nullptr; + m_objectIdFrameSerialCounter = 0u; } void SceneViewportRenderService::SetRenderRequest( SceneViewportRenderRequest request) { + if (m_lastTargets != nullptr && + m_lastTargets->hasValidObjectIdFrame && + (request.scene != m_lastObjectIdScene || + request.camera != m_lastObjectIdCamera)) { + InvalidateObjectIdFrame(); + } + m_renderRequest = std::move(request); } @@ -131,6 +143,19 @@ ViewportRenderResult SceneViewportRenderService::Render( targets, renderPlan.plan, plans.front()); + if (plans.front().request.objectId.IsRequested()) { + ++m_objectIdFrameSerialCounter; + if (m_objectIdFrameSerialCounter == 0u) { + m_objectIdFrameSerialCounter = 1u; + } + targets.objectIdFrameSerial = m_objectIdFrameSerialCounter; + m_lastObjectIdScene = m_renderRequest.scene; + m_lastObjectIdCamera = m_renderRequest.camera; + } else { + targets.objectIdFrameSerial = 0u; + m_lastObjectIdScene = nullptr; + m_lastObjectIdCamera = nullptr; + } result.rendered = true; return result; } @@ -151,10 +176,11 @@ ViewportObjectIdPickResult SceneViewportRenderService::PickObject( pickContext.textureWidth = m_lastTargets->width; pickContext.textureHeight = m_lastTargets->height; pickContext.hasValidFrame = m_lastTargets->hasValidObjectIdFrame; + pickContext.frameSerial = m_lastTargets->objectIdFrameSerial; pickContext.viewportSize = viewportSize; pickContext.viewportMousePosition = viewportMousePosition; - return PickViewportObjectIdEntity( + ViewportObjectIdPickResult result = PickViewportRenderObjectId( pickContext, [this]( const ViewportObjectIdReadbackRequest& request, @@ -167,6 +193,27 @@ ViewportObjectIdPickResult SceneViewportRenderService::PickObject( request.pixelY, outRgba); }); + + if (!result.HasResolvedSample() || + !::XCEngine::Rendering::IsValidRenderObjectId(result.renderObjectId)) { + return result; + } + + std::uint64_t runtimeObjectId = 0u; + if (!::XCEngine::Rendering::RenderObjectIdRegistry::Get() + .TryResolveRuntimeObjectId( + result.renderObjectId, + runtimeObjectId)) { + return result; + } + + if (m_lastObjectIdScene == nullptr || + m_lastObjectIdScene->FindByID(runtimeObjectId) == nullptr) { + return result; + } + + result.resolvedEntityId = runtimeObjectId; + return result; } void SceneViewportRenderService::EnsureSceneRenderer() { @@ -175,4 +222,14 @@ void SceneViewportRenderService::EnsureSceneRenderer() { } } +void SceneViewportRenderService::InvalidateObjectIdFrame() { + if (m_lastTargets != nullptr) { + m_lastTargets->hasValidObjectIdFrame = false; + m_lastTargets->objectIdFrameSerial = 0u; + } + + m_lastObjectIdScene = nullptr; + m_lastObjectIdCamera = nullptr; +} + } // namespace XCEngine::UI::Editor::App diff --git a/new_editor/app/Rendering/Viewport/SceneViewportRenderService.h b/new_editor/app/Rendering/Viewport/SceneViewportRenderService.h index 9326548b..e17ac8cf 100644 --- a/new_editor/app/Rendering/Viewport/SceneViewportRenderService.h +++ b/new_editor/app/Rendering/Viewport/SceneViewportRenderService.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -47,13 +48,17 @@ public: private: void EnsureSceneRenderer(); + void InvalidateObjectIdFrame(); SceneViewportRenderRequest m_renderRequest = {}; std::unique_ptr<::XCEngine::Rendering::SceneRenderer> m_sceneRenderer = {}; SceneViewportRenderPassBundle m_renderPassBundle = {}; ::XCEngine::RHI::RHIDevice* m_device = nullptr; - const ViewportRenderTargets* m_lastTargets = nullptr; + ViewportRenderTargets* m_lastTargets = nullptr; ::XCEngine::Rendering::RenderContext m_lastRenderContext = {}; + ::XCEngine::Components::Scene* m_lastObjectIdScene = nullptr; + ::XCEngine::Components::CameraComponent* m_lastObjectIdCamera = nullptr; + std::uint64_t m_objectIdFrameSerialCounter = 0u; }; } // namespace XCEngine::UI::Editor::App diff --git a/new_editor/app/Rendering/Viewport/SceneViewportResourcePaths.h b/new_editor/app/Rendering/Viewport/SceneViewportResourcePaths.h index 841f6c17..3661159a 100644 --- a/new_editor/app/Rendering/Viewport/SceneViewportResourcePaths.h +++ b/new_editor/app/Rendering/Viewport/SceneViewportResourcePaths.h @@ -42,4 +42,20 @@ inline ::XCEngine::Containers::String GetSceneViewportInfiniteGridShaderPath() { "infinite-grid.shader"); } +inline ::XCEngine::Containers::String GetSceneViewportSelectionMaskShaderPath() { + return BuildSceneViewportResourcePath( + std::filesystem::path("shaders") / + "scene-viewport" / + "selection-mask" / + "selection-mask.shader"); +} + +inline ::XCEngine::Containers::String GetSceneViewportSelectionOutlineShaderPath() { + return BuildSceneViewportResourcePath( + std::filesystem::path("shaders") / + "scene-viewport" / + "selection-outline" / + "selection-outline.shader"); +} + } // namespace XCEngine::UI::Editor::App diff --git a/new_editor/app/Rendering/Viewport/ViewportHostService.cpp b/new_editor/app/Rendering/Viewport/ViewportHostService.cpp index f6ac36f8..a02ebcc4 100644 --- a/new_editor/app/Rendering/Viewport/ViewportHostService.cpp +++ b/new_editor/app/Rendering/Viewport/ViewportHostService.cpp @@ -190,6 +190,7 @@ void ViewportHostService::ApplyViewportFallback( const ViewportRenderResult& renderResult) { entry.statusText = renderResult.statusText; entry.renderTargets.hasValidObjectIdFrame = false; + entry.renderTargets.objectIdFrameSerial = 0u; ClearViewport( entry, renderContext, @@ -230,6 +231,7 @@ void ViewportHostService::ClearViewport( entry.renderTargets.objectIdState = ResourceStates::Common; entry.renderTargets.selectionMaskState = ResourceStates::Common; entry.renderTargets.hasValidObjectIdFrame = false; + entry.renderTargets.objectIdFrameSerial = 0u; } ViewportFrame ViewportHostService::BuildFrame( diff --git a/new_editor/app/Rendering/Viewport/ViewportObjectIdPicker.h b/new_editor/app/Rendering/Viewport/ViewportObjectIdPicker.h index 06cc6c5d..296adb19 100644 --- a/new_editor/app/Rendering/Viewport/ViewportObjectIdPicker.h +++ b/new_editor/app/Rendering/Viewport/ViewportObjectIdPicker.h @@ -22,6 +22,7 @@ struct ViewportObjectIdPickContext { std::uint32_t textureWidth = 0; std::uint32_t textureHeight = 0; bool hasValidFrame = false; + std::uint64_t frameSerial = 0u; ::XCEngine::UI::UISize viewportSize = {}; ::XCEngine::UI::UIPoint viewportMousePosition = {}; }; @@ -43,13 +44,18 @@ enum class ViewportObjectIdPickStatus : std::uint8_t { struct ViewportObjectIdPickResult { ViewportObjectIdPickStatus status = ViewportObjectIdPickStatus::Unavailable; + std::uint64_t frameSerial = 0u; ::XCEngine::Rendering::RenderObjectId renderObjectId = ::XCEngine::Rendering::kInvalidRenderObjectId; - std::uint64_t entityId = 0; + std::uint64_t resolvedEntityId = 0u; bool HasResolvedSample() const { return status == ViewportObjectIdPickStatus::Success; } + + bool HasResolvedEntity() const { + return HasResolvedSample() && resolvedEntityId != 0u; + } }; inline bool CanPickViewportObjectId(const ViewportObjectIdPickContext& context) { @@ -58,6 +64,7 @@ inline bool CanPickViewportObjectId(const ViewportObjectIdPickContext& context) context.textureWidth > 0u && context.textureHeight > 0u && context.hasValidFrame && + context.frameSerial != 0u && context.viewportSize.width > 1.0f && context.viewportSize.height > 1.0f && context.viewportMousePosition.x >= 0.0f && @@ -115,7 +122,7 @@ inline bool BuildViewportObjectIdReadbackRequest( } template -ViewportObjectIdPickResult PickViewportObjectIdEntity( +ViewportObjectIdPickResult PickViewportRenderObjectId( const ViewportObjectIdPickContext& context, ReadPixelFn&& readPixel) { ViewportObjectIdPickResult result = {}; @@ -125,6 +132,7 @@ ViewportObjectIdPickResult PickViewportObjectIdEntity( return result; } + result.frameSerial = context.frameSerial; std::array rgba = {}; if (!readPixel(request, rgba)) { result.status = ViewportObjectIdPickStatus::ReadbackFailed; @@ -137,19 +145,17 @@ ViewportObjectIdPickResult PickViewportObjectIdEntity( rgba[1], rgba[2], rgba[3]); - result.entityId = ::XCEngine::Rendering::ConvertRenderObjectIdToRuntimeObjectId( - result.renderObjectId); return result; } template -bool TryPickViewportObjectIdEntity( +bool TryPickViewportRenderObjectId( const ViewportObjectIdPickContext& context, ReadPixelFn&& readPixel, - std::uint64_t& outEntityId) { + ::XCEngine::Rendering::RenderObjectId& outRenderObjectId) { const ViewportObjectIdPickResult result = - PickViewportObjectIdEntity(context, std::forward(readPixel)); - outEntityId = result.entityId; + PickViewportRenderObjectId(context, std::forward(readPixel)); + outRenderObjectId = result.renderObjectId; return result.HasResolvedSample(); } diff --git a/new_editor/app/Rendering/Viewport/ViewportRenderTargets.cpp b/new_editor/app/Rendering/Viewport/ViewportRenderTargets.cpp index b2b1cbe9..cd524b48 100644 --- a/new_editor/app/Rendering/Viewport/ViewportRenderTargets.cpp +++ b/new_editor/app/Rendering/Viewport/ViewportRenderTargets.cpp @@ -150,6 +150,7 @@ void ResetViewportTargetMetadata(ViewportRenderTargets& targets) { targets.objectIdState = ::XCEngine::RHI::ResourceStates::Common; targets.selectionMaskState = ::XCEngine::RHI::ResourceStates::Common; targets.hasValidObjectIdFrame = false; + targets.objectIdFrameSerial = 0u; } } // namespace @@ -245,6 +246,7 @@ bool ViewportRenderTargetManager::EnsureTargets( targets.objectIdState = ::XCEngine::RHI::ResourceStates::Common; targets.selectionMaskState = ::XCEngine::RHI::ResourceStates::Common; targets.hasValidObjectIdFrame = false; + targets.objectIdFrameSerial = 0u; return true; } diff --git a/new_editor/app/Rendering/Viewport/ViewportRenderTargets.h b/new_editor/app/Rendering/Viewport/ViewportRenderTargets.h index e69ba122..10abec98 100644 --- a/new_editor/app/Rendering/Viewport/ViewportRenderTargets.h +++ b/new_editor/app/Rendering/Viewport/ViewportRenderTargets.h @@ -31,6 +31,7 @@ struct ViewportRenderTargets { ::XCEngine::RHI::ResourceStates objectIdState = ::XCEngine::RHI::ResourceStates::Common; ::XCEngine::RHI::ResourceStates selectionMaskState = ::XCEngine::RHI::ResourceStates::Common; bool hasValidObjectIdFrame = false; + std::uint64_t objectIdFrameSerial = 0u; }; ViewportResourceReuseQuery BuildViewportRenderTargetsReuseQuery( diff --git a/new_editor/resources/shaders/scene-viewport/selection-mask/selection-mask.shader b/new_editor/resources/shaders/scene-viewport/selection-mask/selection-mask.shader new file mode 100644 index 00000000..8f8c6048 --- /dev/null +++ b/new_editor/resources/shaders/scene-viewport/selection-mask/selection-mask.shader @@ -0,0 +1,78 @@ +Shader "Builtin Selection Mask" +{ + Properties + { + _BaseColor ("Base Color", Color) = (1,1,1,1) [Semantic(BaseColor)] + _Cutoff ("Alpha Cutoff", Range) = 0.5 [Semantic(AlphaCutoff)] + _MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)] + } + HLSLINCLUDE + // XC_BUILTIN_SELECTION_MASK_D3D12_SHARED + cbuffer PerObjectConstants + { + float4x4 gProjectionMatrix; + float4x4 gViewMatrix; + float4x4 gModelMatrix; + }; + + cbuffer MaterialConstants + { + float4 gBaseColorFactor; + float4 gAlphaCutoffParams; + }; + + Texture2D BaseColorTexture; + SamplerState LinearClampSampler; + + struct VSInput + { + float3 position : POSITION; + float3 normal : NORMAL; + float2 texcoord : TEXCOORD0; + }; + + struct PSInput + { + float4 position : SV_POSITION; + float2 texcoord : TEXCOORD0; + }; + + PSInput MainVS(VSInput input) + { + PSInput output; + const float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f)); + const float4 positionVS = mul(gViewMatrix, positionWS); + output.position = mul(gProjectionMatrix, positionVS); + output.texcoord = input.texcoord; + return output; + } + + float4 MainPS(PSInput input) : SV_TARGET + { + // XC_BUILTIN_SELECTION_MASK_D3D12_PS + #ifdef XC_ALPHA_TEST + const float4 baseColor = + BaseColorTexture.Sample(LinearClampSampler, input.texcoord) * gBaseColorFactor; + clip(baseColor.a - gAlphaCutoffParams.x); + #endif + return float4(1.0, 1.0, 1.0, 1.0); + } + ENDHLSL + SubShader + { + Pass + { + Name "SelectionMask" + Tags { "LightMode" = "SelectionMask" } + Cull Back + ZWrite Off + ZTest LEqual + HLSLPROGRAM + #pragma target 4.5 + #pragma vertex MainVS + #pragma fragment MainPS + #pragma shader_feature_local _ XC_ALPHA_TEST + ENDHLSL + } + } +} diff --git a/engine/assets/builtin/shaders/object-id-outline.shader b/new_editor/resources/shaders/scene-viewport/selection-outline/selection-outline.shader similarity index 57% rename from engine/assets/builtin/shaders/object-id-outline.shader rename to new_editor/resources/shaders/scene-viewport/selection-outline/selection-outline.shader index 5a5da620..11c4bb48 100644 --- a/engine/assets/builtin/shaders/object-id-outline.shader +++ b/new_editor/resources/shaders/scene-viewport/selection-outline/selection-outline.shader @@ -1,16 +1,17 @@ -Shader "Builtin Object Id Outline" +Shader "Builtin Selection Outline" { HLSLINCLUDE - // XC_BUILTIN_OBJECT_ID_OUTLINE_D3D12_SHARED + // XC_BUILTIN_SELECTION_OUTLINE_D3D12_SHARED cbuffer OutlineConstants { float4 gViewportSizeAndTexelSize; float4 gOutlineColor; - float4 gSelectedInfo; - float4 gSelectedObjectColors[256]; + float4 gOutlineInfo; + float4 gDepthParams; }; - Texture2D gObjectIdTexture; + Texture2D gSelectionMaskTexture; + Texture2D gDepthTexture; struct VSOutput { @@ -19,7 +20,7 @@ Shader "Builtin Object Id Outline" VSOutput MainVS(uint vertexId : SV_VertexID) { - // XC_BUILTIN_OBJECT_ID_OUTLINE_D3D12_VS + // XC_BUILTIN_SELECTION_OUTLINE_D3D12_VS static const float2 positions[3] = { float2(-1.0, -1.0), float2(-1.0, 3.0), @@ -39,46 +40,27 @@ Shader "Builtin Object Id Outline" return clamp(pixelCoord, int2(0, 0), maxCoord); } - float4 LoadObjectId(int2 pixelCoord) + float LoadSelectionMask(int2 pixelCoord) { - return gObjectIdTexture.Load(int3(ClampPixelCoord(pixelCoord), 0)); + return gSelectionMaskTexture.Load(int3(ClampPixelCoord(pixelCoord), 0)).r; } - bool IsSelectedObject(float4 objectIdColor) + float LoadDepth(int2 pixelCoord) { - // Object-id surfaces encode the formal 32-bit render object id across - // RGBA. Low-valued ids legitimately have zero alpha, so only the - // all-zero clear color should be treated as "no object". - if (all(abs(objectIdColor) <= float4( - 0.0025, - 0.0025, - 0.0025, - 0.0025))) { - return false; - } + return gDepthTexture.Load(int3(ClampPixelCoord(pixelCoord), 0)).r; + } - const int selectedCount = min((int)gSelectedInfo.x, 256); - [loop] - for (int i = 0; i < selectedCount; ++i) { - const float4 selectedColor = gSelectedObjectColors[i]; - if (all(abs(objectIdColor - selectedColor) <= float4( - 0.0025, - 0.0025, - 0.0025, - 0.0025))) { - return true; - } - } - - return false; + bool IsSelectedPixel(float maskValue) + { + return maskValue > 0.5; } float4 MainPS(VSOutput input) : SV_TARGET { - // XC_BUILTIN_OBJECT_ID_OUTLINE_D3D12_PS + // XC_BUILTIN_SELECTION_OUTLINE_D3D12_PS const int2 pixelCoord = int2(input.position.xy); - const bool debugSelectionMask = gSelectedInfo.y > 0.5; - const bool centerSelected = IsSelectedObject(LoadObjectId(pixelCoord)); + const bool debugSelectionMask = gOutlineInfo.x > 0.5; + const bool centerSelected = IsSelectedPixel(LoadSelectionMask(pixelCoord)); if (debugSelectionMask) { return centerSelected ? float4(1.0, 1.0, 1.0, 1.0) : float4(0.0, 0.0, 0.0, 1.0); @@ -88,7 +70,10 @@ Shader "Builtin Object Id Outline" discard; } - const int outlineWidth = max((int)gSelectedInfo.z, 1); + const float centerDepth = LoadDepth(pixelCoord); + const int outlineWidth = max((int)gOutlineInfo.y, 1); + const float depthThreshold = max(gDepthParams.x, 1.0e-6f); + float outline = 0.0; [loop] for (int y = -2; y <= 2; ++y) { @@ -103,7 +88,13 @@ Shader "Builtin Object Id Outline" continue; } - if (!IsSelectedObject(LoadObjectId(pixelCoord + int2(x, y)))) { + const int2 sampleCoord = pixelCoord + int2(x, y); + if (!IsSelectedPixel(LoadSelectionMask(sampleCoord))) { + continue; + } + + const float selectedDepth = LoadDepth(sampleCoord); + if (!(centerDepth > selectedDepth + depthThreshold)) { continue; } @@ -124,8 +115,8 @@ Shader "Builtin Object Id Outline" { Pass { - Name "ObjectIdOutline" - Tags { "LightMode" = "ObjectIdOutline" } + Name "SelectionOutline" + Tags { "LightMode" = "SelectionOutline" } Cull Off ZWrite Off ZTest Always