218 lines
8.4 KiB
C++
218 lines
8.4 KiB
C++
#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>()
|
||
});
|
||
}
|
||
|
||
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
|