#include "MetaCoreRender/MetaCoreSceneRenderSync.h" #include "MetaCoreRender/MetaCoreRenderTypes.h" #include "MetaCoreScene/MetaCoreScene.h" #include "MetaCoreScene/MetaCoreTransformUtils.h" #include #include #include namespace MetaCore { namespace { [[nodiscard]] bool MetaCoreIsUsableDirection(const glm::vec3& value) { return std::isfinite(value.x) && std::isfinite(value.y) && std::isfinite(value.z) && glm::length(value) > 0.0001F; } [[nodiscard]] glm::vec3 MetaCoreExtractTranslation(const glm::mat4& matrix) { return glm::vec3(matrix[3]); } [[nodiscard]] std::string MetaCoreNormalizeSyncPath(std::string path) { std::replace(path.begin(), path.end(), '\\', '/'); return path; } } // namespace MetaCoreSceneRenderSyncSnapshot MetaCoreSceneRenderSync::BuildSnapshot(const MetaCoreScene& scene) const { MetaCoreSceneRenderSyncSnapshot snapshot; snapshot.FrameId = ++FrameId_; snapshot.SceneRevision = scene.GetRevision(); // 预处理:构建子树模型网格节点计数表,支撑在没有 Tag 时的 Fallback 回溯 std::unordered_map> subtreeModelCounts; for (MetaCoreId objectId : scene.BuildHierarchyPreorder()) { const MetaCoreGameObject gameObject = scene.FindGameObject(objectId); if (gameObject && gameObject.HasComponent()) { const auto& mesh = gameObject.GetComponent(); if (mesh.ModelNodeIndex >= 0 && !mesh.SourceModelPath.empty()) { std::string normPath = MetaCoreNormalizeSyncPath(mesh.SourceModelPath); MetaCoreId currentId = objectId; while (currentId != 0) { auto currentObj = scene.FindGameObject(currentId); if (!currentObj) break; subtreeModelCounts[currentId][normPath]++; currentId = currentObj.GetParentId(); } } } } for (MetaCoreId objectId : scene.BuildHierarchyPreorder()) { const MetaCoreGameObject gameObject = scene.FindGameObject(objectId); if (!gameObject) { continue; } glm::mat4 localMatrix{1.0F}; if (gameObject.HasComponent()) { localMatrix = MetaCoreBuildTransformMatrix(gameObject.GetComponent()); } glm::mat4 worldMatrix = localMatrix; const MetaCoreId parentId = gameObject.GetParentId(); if (parentId != 0) { const auto parentWorldIt = snapshot.WorldMatrices.find(parentId); if (parentWorldIt != snapshot.WorldMatrices.end()) { worldMatrix = parentWorldIt->second * localMatrix; } } snapshot.WorldMatrices[objectId] = worldMatrix; snapshot.LocalMatrices[objectId] = localMatrix; if (gameObject.HasComponent()) { const auto& meshRenderer = gameObject.GetComponent(); // 黄金算法:溯源确定唯一的模型根加载主体 MetaCoreGameObject hostRoot = gameObject; if (meshRenderer.ModelNodeIndex >= 0) { const std::string& currentModelPath = meshRenderer.SourceModelPath; std::string normCurrentPath = MetaCoreNormalizeSyncPath(currentModelPath); MetaCoreGameObject current = gameObject; MetaCoreGameObject foundRoot = {}; // 1. 优先通过运行时 MetaCoreModelRootTag 快速寻找顶级根 while (current) { if (current.HasComponent()) { auto& tag = current.GetComponent(); if (MetaCoreNormalizeSyncPath(tag.SourceModelPath) == normCurrentPath) { foundRoot = current; break; } } if (current.GetParentId() == 0) break; current = scene.FindGameObject(current.GetParentId()); } if (foundRoot) { hostRoot = foundRoot; } else { // 2. Fallback:若无明确 Tag,使用转折点判定算法 current = gameObject; MetaCoreGameObject bestCandidate = gameObject; int maxCount = subtreeModelCounts[gameObject.GetId()][normCurrentPath]; while (current) { int count = subtreeModelCounts[current.GetId()][normCurrentPath]; if (count > maxCount) { maxCount = count; bestCandidate = current; } if (current.GetParentId() == 0) break; current = scene.FindGameObject(current.GetParentId()); } hostRoot = bestCandidate; } } snapshot.Renderables.push_back(MetaCoreRenderSyncRenderable{ objectId, parentId, gameObject.GetName(), localMatrix, worldMatrix, meshRenderer.Visible, meshRenderer.MeshSource, meshRenderer.BuiltinMesh, meshRenderer.MeshAssetGuid, meshRenderer.SourceModelAssetGuid, meshRenderer.SourceModelPath, meshRenderer.ModelNodeIndex, hostRoot.GetId(), hostRoot.GetName(), hostRoot.HasComponent(), meshRenderer.BaseColor }); } if (gameObject.HasComponent()) { const auto& camera = gameObject.GetComponent(); MetaCoreRenderSyncCamera syncCamera{ objectId, camera.IsPrimary, camera.FieldOfViewDegrees, camera.NearClip, camera.FarClip, worldMatrix, camera.Enabled }; snapshot.Cameras.push_back(syncCamera); if (syncCamera.IsPrimary && syncCamera.Enabled && !snapshot.PrimaryCamera.has_value()) { snapshot.PrimaryCamera = syncCamera; } } if (gameObject.HasComponent()) { const auto& light = gameObject.GetComponent(); snapshot.Lights.push_back(MetaCoreRenderSyncLight{ objectId, light.Color, light.Intensity, worldMatrix, light.Enabled }); } } if (!snapshot.PrimaryCamera.has_value() && !snapshot.Cameras.empty()) { for (const auto& cam : snapshot.Cameras) { if (cam.Enabled) { snapshot.PrimaryCamera = cam; break; } } if (!snapshot.PrimaryCamera.has_value()) { snapshot.PrimaryCamera = snapshot.Cameras.front(); } } return snapshot; } bool MetaCoreSceneRenderSync::TryBuildSceneViewFromPrimaryCamera( const MetaCoreSceneRenderSyncSnapshot& snapshot, MetaCoreSceneView& sceneView ) { if (!snapshot.PrimaryCamera.has_value()) { return false; } const MetaCoreRenderSyncCamera& camera = *snapshot.PrimaryCamera; const glm::vec3 cameraPosition = MetaCoreExtractTranslation(camera.WorldMatrix); glm::vec3 cameraForward = glm::vec3(camera.WorldMatrix * glm::vec4(0.0F, 0.0F, -1.0F, 0.0F)); glm::vec3 cameraUp = glm::vec3(camera.WorldMatrix * glm::vec4(0.0F, 1.0F, 0.0F, 0.0F)); if (!MetaCoreIsUsableDirection(cameraForward)) { cameraForward = glm::vec3(0.0F, 0.0F, -1.0F); } if (!MetaCoreIsUsableDirection(cameraUp)) { cameraUp = glm::vec3(0.0F, 1.0F, 0.0F); } sceneView.CameraPosition = cameraPosition; sceneView.CameraTarget = cameraPosition + glm::normalize(cameraForward); sceneView.CameraUp = glm::normalize(cameraUp); sceneView.VerticalFieldOfViewDegrees = camera.FieldOfViewDegrees; sceneView.NearClip = camera.NearClip; sceneView.FarClip = camera.FarClip; return true; } } // namespace MetaCore