#pragma once #include #include #include #include #include #include #include #include #include #include namespace XCEngine::UI::Editor::App { struct ViewportObjectIdPickContext { ::XCEngine::RHI::RHICommandQueue* commandQueue = nullptr; ::XCEngine::RHI::RHITexture* texture = nullptr; ::XCEngine::RHI::ResourceStates textureState = ::XCEngine::RHI::ResourceStates::Common; std::uint32_t textureWidth = 0; std::uint32_t textureHeight = 0; bool hasValidFrame = false; ::XCEngine::UI::UISize viewportSize = {}; ::XCEngine::UI::UIPoint viewportMousePosition = {}; }; struct ViewportObjectIdReadbackRequest { ::XCEngine::RHI::RHICommandQueue* commandQueue = nullptr; ::XCEngine::RHI::RHITexture* texture = nullptr; ::XCEngine::RHI::ResourceStates textureState = ::XCEngine::RHI::ResourceStates::Common; std::uint32_t pixelX = 0; std::uint32_t pixelY = 0; }; enum class ViewportObjectIdPickStatus : std::uint8_t { Unavailable = 0, Success, ReadbackFailed }; struct ViewportObjectIdPickResult { ViewportObjectIdPickStatus status = ViewportObjectIdPickStatus::Unavailable; ::XCEngine::Rendering::RenderObjectId renderObjectId = ::XCEngine::Rendering::kInvalidRenderObjectId; std::uint64_t entityId = 0; bool HasResolvedSample() const { return status == ViewportObjectIdPickStatus::Success; } }; inline bool CanPickViewportObjectId(const ViewportObjectIdPickContext& context) { return context.commandQueue != nullptr && context.texture != nullptr && context.textureWidth > 0u && context.textureHeight > 0u && context.hasValidFrame && context.viewportSize.width > 1.0f && context.viewportSize.height > 1.0f && context.viewportMousePosition.x >= 0.0f && context.viewportMousePosition.y >= 0.0f && context.viewportMousePosition.x <= context.viewportSize.width && context.viewportMousePosition.y <= context.viewportSize.height; } inline std::uint32_t ClampViewportObjectIdPixelCoordinate( float value, std::uint32_t extent) { if (extent == 0u) { return 0u; } const float maxCoordinate = static_cast(extent - 1u); const float clamped = (std::max)(0.0f, (std::min)(value, maxCoordinate)); return static_cast(std::floor(clamped)); } inline std::uint32_t ResolveViewportObjectIdPixelCoordinate( float viewportCoordinate, float viewportExtent, std::uint32_t textureExtent) { if (viewportExtent <= 0.0f || textureExtent == 0u) { return 0u; } const float normalized = viewportCoordinate / viewportExtent; const float textureCoordinate = normalized * static_cast(textureExtent); return ClampViewportObjectIdPixelCoordinate(textureCoordinate, textureExtent); } inline bool BuildViewportObjectIdReadbackRequest( const ViewportObjectIdPickContext& context, ViewportObjectIdReadbackRequest& outRequest) { outRequest = {}; if (!CanPickViewportObjectId(context)) { return false; } outRequest.commandQueue = context.commandQueue; outRequest.texture = context.texture; outRequest.textureState = context.textureState; outRequest.pixelX = ResolveViewportObjectIdPixelCoordinate( context.viewportMousePosition.x, context.viewportSize.width, context.textureWidth); outRequest.pixelY = ResolveViewportObjectIdPixelCoordinate( context.viewportMousePosition.y, context.viewportSize.height, context.textureHeight); return true; } template ViewportObjectIdPickResult PickViewportObjectIdEntity( const ViewportObjectIdPickContext& context, ReadPixelFn&& readPixel) { ViewportObjectIdPickResult result = {}; ViewportObjectIdReadbackRequest request = {}; if (!BuildViewportObjectIdReadbackRequest(context, request)) { return result; } std::array rgba = {}; if (!readPixel(request, rgba)) { result.status = ViewportObjectIdPickStatus::ReadbackFailed; return result; } result.status = ViewportObjectIdPickStatus::Success; result.renderObjectId = ::XCEngine::Rendering::DecodeRenderObjectIdFromColor( rgba[0], rgba[1], rgba[2], rgba[3]); result.entityId = ::XCEngine::Rendering::ConvertRenderObjectIdToRuntimeObjectId( result.renderObjectId); return result; } template bool TryPickViewportObjectIdEntity( const ViewportObjectIdPickContext& context, ReadPixelFn&& readPixel, std::uint64_t& outEntityId) { const ViewportObjectIdPickResult result = PickViewportObjectIdEntity(context, std::forward(readPixel)); outEntityId = result.entityId; return result.HasResolvedSample(); } } // namespace XCEngine::UI::Editor::App