MetaCore/Source/MetaCoreRender/Private/MetaCoreSceneRenderSync.cpp

219 lines
8.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "MetaCoreRender/MetaCoreSceneRenderSync.h"
#include "MetaCoreRender/MetaCoreRenderTypes.h"
#include "MetaCoreScene/MetaCoreScene.h"
#include "MetaCoreScene/MetaCoreTransformUtils.h"
#include <glm/geometric.hpp>
#include <algorithm>
#include <cmath>
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<MetaCoreId, std::unordered_map<std::string, int>> subtreeModelCounts;
for (MetaCoreId objectId : scene.BuildHierarchyPreorder()) {
const MetaCoreGameObject gameObject = scene.FindGameObject(objectId);
if (gameObject && gameObject.HasComponent<MetaCoreMeshRendererComponent>()) {
const auto& mesh = gameObject.GetComponent<MetaCoreMeshRendererComponent>();
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<MetaCoreTransformComponent>()) {
localMatrix = MetaCoreBuildTransformMatrix(gameObject.GetComponent<MetaCoreTransformComponent>());
}
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<MetaCoreMeshRendererComponent>()) {
const auto& meshRenderer = gameObject.GetComponent<MetaCoreMeshRendererComponent>();
// 黄金算法:溯源确定唯一的模型根加载主体
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<MetaCoreModelRootTag>()) {
auto& tag = current.GetComponent<MetaCoreModelRootTag>();
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<MetaCoreModelRootTag>(),
meshRenderer.BaseColor
});
}
if (gameObject.HasComponent<MetaCoreCameraComponent>()) {
const auto& camera = gameObject.GetComponent<MetaCoreCameraComponent>();
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<MetaCoreLightComponent>()) {
const auto& light = gameObject.GetComponent<MetaCoreLightComponent>();
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