feat: add scene render sync bridge for Filament P1

This commit is contained in:
ayuan9957 2026-05-21 12:03:39 +08:00
parent 7d5887bbbb
commit 1a822288b7
10 changed files with 396 additions and 12 deletions

View File

@ -161,6 +161,7 @@ int main(int argc, char* argv[]) {
return 1;
}
const std::filesystem::path projectPath = MetaCore::MetaCoreGetProjectFilePath(*projectRoot);
viewportRenderer.SetProjectRootPath(*projectRoot);
MetaCore::MetaCoreTypeRegistry typeRegistry;
MetaCore::MetaCoreRegisterRuntimeDataGeneratedTypes(typeRegistry);
const auto loadedRuntimeProjectDocument = MetaCore::MetaCoreReadRuntimeProjectDocument(
@ -277,7 +278,7 @@ int main(int argc, char* argv[]) {
}
}
}
viewportRenderer.RenderSceneToViewport(scene, MetaCoreBuildPlayerSceneView());
viewportRenderer.RenderSceneToViewport(scene, MetaCoreBuildPlayerSceneView(), true);
renderDevice.RenderFrame();
renderDevice.PresentFrame();
window.EndFrame();

View File

@ -215,6 +215,7 @@ set(METACORE_RENDER_HEADERS
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreFilamentSceneBridge.h
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreImGuiHelper.h
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreRenderDevice.h
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreSceneRenderSync.h
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreRenderTypes.h
)
@ -223,6 +224,7 @@ set(METACORE_RENDER_SOURCES
Source/MetaCoreRender/Private/MetaCoreFilamentSceneBridge.cpp
Source/MetaCoreRender/Private/MetaCoreImGuiHelper.cpp
Source/MetaCoreRender/Private/MetaCoreRenderDevice.cpp
Source/MetaCoreRender/Private/MetaCoreSceneRenderSync.cpp
)

View File

@ -79,7 +79,7 @@ void MetaCoreEditorViewportRenderer::SetViewportRect(const MetaCoreViewportRect&
}
}
void MetaCoreEditorViewportRenderer::RenderSceneToViewport(MetaCoreScene& scene, const MetaCoreSceneView& sceneView) {
void MetaCoreEditorViewportRenderer::RenderSceneToViewport(MetaCoreScene& scene, const MetaCoreSceneView& sceneView, bool useScenePrimaryCamera) {
if (RenderDevice_ == nullptr) {
return;
}
@ -91,7 +91,7 @@ void MetaCoreEditorViewportRenderer::RenderSceneToViewport(MetaCoreScene& scene,
// 更新并同步 Filament 桥接器(但不在这里调用 Render交由 RenderAll 统一渲染)
FilamentSceneBridge_.ApplySceneView(sceneView);
FilamentSceneBridge_.SyncScene(scene, false);
FilamentSceneBridge_.SyncScene(scene, false, useScenePrimaryCamera);
}
void MetaCoreEditorViewportRenderer::RenderAll() {

View File

@ -4,6 +4,7 @@
#include "MetaCoreScene/MetaCoreComponents.h"
#include "MetaCoreRender/MetaCoreRenderTypes.h"
#include "MetaCoreRender/MetaCoreImGuiHelper.h"
#include "MetaCoreRender/MetaCoreSceneRenderSync.h"
#include <imgui.h>
#include "MetaCoreFoundation/MetaCoreAssetRegistry.h"
@ -31,13 +32,16 @@
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtc/type_ptr.hpp>
#include <glm/geometric.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/matrix_decompose.hpp>
#include <glm/gtc/quaternion.hpp>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <vector>
@ -155,6 +159,17 @@ public:
AssetLoader_->destroyAsset(asset);
}
LoadedAssets_.clear();
ObjectToFilamentEntity_.clear();
ObjectWorldMatrices_.clear();
for (auto& [id, entity] : SceneLightEntities_) {
if (Scene_) {
Scene_->remove(entity);
}
Engine_->destroy(entity);
utils::EntityManager::get().destroy(entity);
}
SceneLightEntities_.clear();
// 销毁 gltfio 资源
if (AssetLoader_) {
@ -342,8 +357,19 @@ public:
return path;
}
void SyncScene(MetaCoreScene& scene, bool compatibilityMeshOnly) {
void SyncScene(MetaCoreScene& scene, bool compatibilityMeshOnly, bool useScenePrimaryCamera) {
if (!AssetLoader_) return;
(void)compatibilityMeshOnly;
LastSyncSnapshot_ = RenderSync_.BuildSnapshot(scene);
ObjectWorldMatrices_ = LastSyncSnapshot_.WorldMatrices;
SyncSceneLights(LastSyncSnapshot_);
if (useScenePrimaryCamera) {
MetaCoreSceneView primarySceneView;
if (MetaCoreSceneRenderSync::TryBuildSceneViewFromPrimaryCamera(LastSyncSnapshot_, primarySceneView)) {
ApplySceneView(primarySceneView);
}
}
// 预处理:构建子树模型网格节点计数表,以支撑在没有 Tag 时的极速、完美 Fallback 回溯
std::unordered_map<MetaCoreId, std::unordered_map<std::string, int>> subtreeModelCounts;
@ -538,8 +564,8 @@ public:
Camera_->setProjection(
sceneView.VerticalFieldOfViewDegrees,
aspect,
0.1,
1000.0,
sceneView.NearClip,
sceneView.FarClip,
filament::Camera::Fov::VERTICAL
);
}
@ -632,18 +658,92 @@ public:
}
bool TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const {
return false;
const auto worldIt = ObjectWorldMatrices_.find(objectId);
if (worldIt == ObjectWorldMatrices_.end()) {
return false;
}
worldMatrix = worldIt->second;
return true;
}
bool HasRuntimeSyncFailure() const { return false; }
const std::string& GetLastRuntimeSyncFailure() const { return EmptyString_; }
private:
static filament::math::float3 ToFilamentFloat3(const glm::vec3& value) {
return filament::math::float3{ value.x, value.y, value.z };
}
static filament::math::float3 BuildFilamentLightDirection(const glm::mat4& worldMatrix) {
glm::vec3 direction = glm::vec3(worldMatrix * glm::vec4(0.0F, 0.0F, -1.0F, 0.0F));
if (glm::length(direction) <= 0.0001F) {
direction = glm::vec3(0.5F, 0.5F, -1.0F);
}
direction = glm::normalize(direction);
return ToFilamentFloat3(direction);
}
static filament::LinearColor BuildFilamentLightColor(const glm::vec3& color) {
return filament::Color::toLinear<filament::ACCURATE>({ color.x, color.y, color.z });
}
static float BuildFilamentLightIntensity(float intensity) {
return (intensity > 0.0F ? intensity : 0.0F) * 10000.0F;
}
void SyncSceneLights(const MetaCoreSceneRenderSyncSnapshot& snapshot) {
if (!Engine_ || !Scene_) {
return;
}
std::unordered_set<MetaCoreId> activeLightIds;
auto& lightManager = Engine_->getLightManager();
const auto defaultLightInstance = lightManager.getInstance(Light_);
if (defaultLightInstance) {
lightManager.setIntensity(defaultLightInstance, snapshot.Lights.empty() ? 100000.0F : 0.0F);
}
for (const MetaCoreRenderSyncLight& light : snapshot.Lights) {
activeLightIds.insert(light.ObjectId);
auto lightIt = SceneLightEntities_.find(light.ObjectId);
if (lightIt == SceneLightEntities_.end()) {
utils::Entity lightEntity = utils::EntityManager::get().create();
filament::LightManager::Builder(filament::LightManager::Type::DIRECTIONAL)
.color(BuildFilamentLightColor(light.Color))
.intensity(BuildFilamentLightIntensity(light.Intensity))
.direction(BuildFilamentLightDirection(light.WorldMatrix))
.castShadows(true)
.build(*Engine_, lightEntity);
Scene_->addEntity(lightEntity);
lightIt = SceneLightEntities_.emplace(light.ObjectId, lightEntity).first;
}
const auto lightInstance = lightManager.getInstance(lightIt->second);
if (lightInstance) {
lightManager.setColor(lightInstance, BuildFilamentLightColor(light.Color));
lightManager.setIntensity(lightInstance, BuildFilamentLightIntensity(light.Intensity));
lightManager.setDirection(lightInstance, BuildFilamentLightDirection(light.WorldMatrix));
}
}
for (auto it = SceneLightEntities_.begin(); it != SceneLightEntities_.end();) {
if (activeLightIds.contains(it->first)) {
++it;
continue;
}
Scene_->remove(it->second);
Engine_->destroy(it->second);
utils::EntityManager::get().destroy(it->second);
it = SceneLightEntities_.erase(it);
}
}
void UpdateTransform(const MetaCoreGameObject& gameObject) {
auto it = ObjectToFilamentEntity_.find(gameObject.GetId());
if (it == ObjectToFilamentEntity_.end()) return;
filament::gltfio::FilamentAsset* asset = it->second.first;
utils::Entity entity = it->second.second;
auto& tm = Engine_->getTransformManager();
@ -678,8 +778,12 @@ private:
filament::gltfio::MaterialProvider* MaterialProvider_ = nullptr;
utils::NameComponentManager* NameManager_ = nullptr;
MetaCoreSceneRenderSync RenderSync_{};
MetaCoreSceneRenderSyncSnapshot LastSyncSnapshot_{};
std::unordered_map<MetaCoreId, glm::mat4> ObjectWorldMatrices_{};
std::unordered_map<MetaCoreId, std::pair<filament::gltfio::FilamentAsset*, utils::Entity>> ObjectToFilamentEntity_;
std::unordered_map<MetaCoreId, filament::gltfio::FilamentAsset*> LoadedAssets_;
std::unordered_map<MetaCoreId, utils::Entity> SceneLightEntities_;
filament::View* UIView_ = nullptr;
MetaCoreImGuiHelper* ImGuiHelper_ = nullptr;
@ -712,8 +816,8 @@ void MetaCoreFilamentSceneBridge::SetProjectRootPath(const std::filesystem::path
Impl_->SetProjectRootPath(projectRootPath);
}
void MetaCoreFilamentSceneBridge::SyncScene(MetaCoreScene& scene, bool compatibilityMeshOnly) {
Impl_->SyncScene(scene, compatibilityMeshOnly);
void MetaCoreFilamentSceneBridge::SyncScene(MetaCoreScene& scene, bool compatibilityMeshOnly, bool useScenePrimaryCamera) {
Impl_->SyncScene(scene, compatibilityMeshOnly, useScenePrimaryCamera);
}
void MetaCoreFilamentSceneBridge::ApplySceneView(const MetaCoreSceneView& sceneView) {

View File

@ -0,0 +1,133 @@
#include "MetaCoreRender/MetaCoreSceneRenderSync.h"
#include "MetaCoreRender/MetaCoreRenderTypes.h"
#include "MetaCoreScene/MetaCoreScene.h"
#include "MetaCoreScene/MetaCoreTransformUtils.h"
#include <glm/geometric.hpp>
#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]);
}
} // namespace
MetaCoreSceneRenderSyncSnapshot MetaCoreSceneRenderSync::BuildSnapshot(const MetaCoreScene& scene) const {
MetaCoreSceneRenderSyncSnapshot snapshot;
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;
if (gameObject.HasComponent<MetaCoreMeshRendererComponent>()) {
const auto& meshRenderer = gameObject.GetComponent<MetaCoreMeshRendererComponent>();
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
});
}
if (gameObject.HasComponent<MetaCoreCameraComponent>()) {
const auto& camera = gameObject.GetComponent<MetaCoreCameraComponent>();
MetaCoreRenderSyncCamera syncCamera{
objectId,
camera.IsPrimary,
camera.FieldOfViewDegrees,
camera.NearClip,
camera.FarClip,
worldMatrix
};
snapshot.Cameras.push_back(syncCamera);
if (syncCamera.IsPrimary && !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
});
}
}
if (!snapshot.PrimaryCamera.has_value() && !snapshot.Cameras.empty()) {
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

View File

@ -18,7 +18,7 @@ public:
bool Initialize(MetaCoreRenderDevice& renderDevice, MetaCoreWindow& window);
void Shutdown();
void SetViewportRect(const MetaCoreViewportRect& viewportRect);
void RenderSceneToViewport(MetaCoreScene& scene, const MetaCoreSceneView& sceneView);
void RenderSceneToViewport(MetaCoreScene& scene, const MetaCoreSceneView& sceneView, bool useScenePrimaryCamera = false);
void RenderAll();
void SetProjectRootPath(const std::filesystem::path& projectRootPath);
[[nodiscard]] uint32_t GetFilamentGLTextureId() const;

View File

@ -26,7 +26,7 @@ public:
void Shutdown();
void Resize(int width, int height);
void SetProjectRootPath(const std::filesystem::path& projectRootPath);
void SyncScene(MetaCoreScene& scene, bool compatibilityMeshOnly = false);
void SyncScene(MetaCoreScene& scene, bool compatibilityMeshOnly = false, bool useScenePrimaryCamera = false);
void ApplySceneView(const MetaCoreSceneView& sceneView);
void RenderAll();
[[nodiscard]] uint32_t GetGLTextureId() const;

View File

@ -18,6 +18,8 @@ struct MetaCoreSceneView {
glm::vec3 CameraTarget{0.0F, 0.7F, 0.0F};
glm::vec3 CameraUp{0.0F, 1.0F, 0.0F};
float VerticalFieldOfViewDegrees = 60.0F;
float NearClip = 0.1F;
float FarClip = 1000.0F;
MetaCoreId SelectedObjectId = 0;
};

View File

@ -0,0 +1,68 @@
#pragma once
#include "MetaCoreFoundation/MetaCoreAssetGuid.h"
#include "MetaCoreFoundation/MetaCoreId.h"
#include "MetaCoreScene/MetaCoreComponents.h"
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
namespace MetaCore {
class MetaCoreScene;
struct MetaCoreSceneView;
struct MetaCoreRenderSyncRenderable {
MetaCoreId ObjectId = 0;
MetaCoreId ParentId = 0;
std::string Name{};
glm::mat4 LocalMatrix{1.0F};
glm::mat4 WorldMatrix{1.0F};
bool Visible = true;
MetaCoreMeshSourceKind MeshSource = MetaCoreMeshSourceKind::Builtin;
MetaCoreBuiltinMeshType BuiltinMesh = MetaCoreBuiltinMeshType::Cube;
MetaCoreAssetGuid MeshAssetGuid{};
MetaCoreAssetGuid SourceModelAssetGuid{};
std::string SourceModelPath{};
std::int32_t ModelNodeIndex = -1;
};
struct MetaCoreRenderSyncCamera {
MetaCoreId ObjectId = 0;
bool IsPrimary = false;
float FieldOfViewDegrees = 60.0F;
float NearClip = 0.1F;
float FarClip = 100.0F;
glm::mat4 WorldMatrix{1.0F};
};
struct MetaCoreRenderSyncLight {
MetaCoreId ObjectId = 0;
glm::vec3 Color{1.0F, 1.0F, 1.0F};
float Intensity = 1.5F;
glm::mat4 WorldMatrix{1.0F};
};
struct MetaCoreSceneRenderSyncSnapshot {
std::unordered_map<MetaCoreId, glm::mat4> WorldMatrices{};
std::vector<MetaCoreRenderSyncRenderable> Renderables{};
std::vector<MetaCoreRenderSyncCamera> Cameras{};
std::vector<MetaCoreRenderSyncLight> Lights{};
std::optional<MetaCoreRenderSyncCamera> PrimaryCamera{};
};
class MetaCoreSceneRenderSync {
public:
[[nodiscard]] MetaCoreSceneRenderSyncSnapshot BuildSnapshot(const MetaCoreScene& scene) const;
[[nodiscard]] static bool TryBuildSceneViewFromPrimaryCamera(
const MetaCoreSceneRenderSyncSnapshot& snapshot,
MetaCoreSceneView& sceneView
);
};
} // namespace MetaCore

View File

@ -9,12 +9,14 @@
#include "MetaCorePlatform/MetaCoreWindow.h"
#include "MetaCoreRender/MetaCoreEditorViewportRenderer.h"
#include "MetaCoreRender/MetaCoreRenderDevice.h"
#include "MetaCoreRender/MetaCoreSceneRenderSync.h"
#include "MetaCoreRuntimeData/MetaCoreRuntimeDataDispatcher.h"
#include "MetaCoreRuntimeData/MetaCoreRuntimeDataSource.h"
#include "MetaCoreRuntimeData/MetaCoreRuntimeDataProject.h"
#include "MetaCoreScene/MetaCoreScenePackage.h"
#include "MetaCoreScene/MetaCoreScene.h"
#include "MetaCoreScene/MetaCoreSceneSerializer.h"
#include "MetaCoreScene/MetaCoreTransformUtils.h"
#include <cstdlib>
#include <cmath>
@ -58,6 +60,21 @@ void MetaCoreExpectVec3Near(const glm::vec3& actual, const glm::vec3& expected,
}
}
void MetaCoreExpectMat4Near(const glm::mat4& actual, const glm::mat4& expected, const char* message) {
for (int column = 0; column < 4; ++column) {
for (int row = 0; row < 4; ++row) {
if (std::abs(actual[column][row] - expected[column][row]) > 0.0001F) {
std::cerr << "MetaCoreSmokeTests failed: " << message
<< " (column=" << column
<< " row=" << row
<< " actual=" << actual[column][row]
<< " expected=" << expected[column][row] << ")\n";
std::exit(1);
}
}
}
}
class MetaCoreDummyPanelProvider final : public MetaCore::MetaCoreIEditorPanelProvider {
public:
std::string GetPanelId() const override { return "Dummy"; }
@ -477,6 +494,61 @@ void MetaCoreTestComponentRegistryDescriptors() {
moduleRegistry.ShutdownServices();
}
void MetaCoreTestSceneRenderSyncBuildsRenderableCameraLightSnapshot() {
MetaCore::MetaCoreScene scene;
MetaCore::MetaCoreGameObject root = scene.CreateGameObject("RenderRoot");
root.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(1.0F, 2.0F, 3.0F);
auto& rootMesh = root.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
rootMesh.MeshSource = MetaCore::MetaCoreMeshSourceKind::Asset;
rootMesh.SourceModelPath = "Assets/Models/SyncRoot.glb";
MetaCore::MetaCoreGameObject child = scene.CreateGameObject("RenderChild", root.GetId());
child.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(4.0F, 5.0F, 6.0F);
auto& childMesh = child.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
childMesh.MeshSource = MetaCore::MetaCoreMeshSourceKind::Builtin;
childMesh.Visible = false;
MetaCore::MetaCoreGameObject cameraObject = scene.CreateGameObject("PrimaryCamera");
auto& camera = cameraObject.AddComponent<MetaCore::MetaCoreCameraComponent>();
camera.IsPrimary = true;
camera.FieldOfViewDegrees = 75.0F;
camera.NearClip = 0.25F;
camera.FarClip = 250.0F;
cameraObject.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(0.0F, 1.0F, 9.0F);
MetaCore::MetaCoreGameObject lightObject = scene.CreateGameObject("KeyLight");
auto& light = lightObject.AddComponent<MetaCore::MetaCoreLightComponent>();
light.Color = glm::vec3(0.25F, 0.5F, 1.0F);
light.Intensity = 3.0F;
const MetaCore::MetaCoreSceneRenderSync renderSync;
const MetaCore::MetaCoreSceneRenderSyncSnapshot snapshot = renderSync.BuildSnapshot(scene);
MetaCoreExpect(snapshot.Renderables.size() == 2, "RenderSync 应收集 MeshRenderer 对象");
MetaCoreExpect(snapshot.Cameras.size() == 1, "RenderSync 应收集 Camera 对象");
MetaCoreExpect(snapshot.Lights.size() == 1, "RenderSync 应收集 Light 对象");
MetaCoreExpect(snapshot.PrimaryCamera.has_value(), "RenderSync 应识别主相机");
MetaCoreExpect(snapshot.PrimaryCamera->ObjectId == cameraObject.GetId(), "RenderSync 主相机 ID 应正确");
const glm::mat4 expectedRootWorld =
MetaCore::MetaCoreBuildTransformMatrix(root.GetComponent<MetaCore::MetaCoreTransformComponent>());
const glm::mat4 expectedChildWorld =
expectedRootWorld * MetaCore::MetaCoreBuildTransformMatrix(child.GetComponent<MetaCore::MetaCoreTransformComponent>());
MetaCoreExpectMat4Near(snapshot.WorldMatrices.at(root.GetId()), expectedRootWorld, "RenderSync 根对象 world matrix 应正确");
MetaCoreExpectMat4Near(snapshot.WorldMatrices.at(child.GetId()), expectedChildWorld, "RenderSync 子对象 world matrix 应合成父级");
MetaCore::MetaCoreSceneView primarySceneView;
MetaCoreExpect(
MetaCore::MetaCoreSceneRenderSync::TryBuildSceneViewFromPrimaryCamera(snapshot, primarySceneView),
"RenderSync 应能从主相机构建 SceneView"
);
MetaCoreExpectVec3Near(primarySceneView.CameraPosition, glm::vec3(0.0F, 1.0F, 9.0F), "主相机 SceneView 位置应来自 Transform");
MetaCoreExpect(std::abs(primarySceneView.VerticalFieldOfViewDegrees - 75.0F) <= 0.0001F, "主相机 FOV 应同步");
MetaCoreExpect(std::abs(primarySceneView.NearClip - 0.25F) <= 0.0001F, "主相机 NearClip 应同步");
MetaCoreExpect(std::abs(primarySceneView.FarClip - 250.0F) <= 0.0001F, "主相机 FarClip 应同步");
}
void MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson() {
const std::filesystem::path tempProjectRoot =
std::filesystem::temp_directory_path() / "MetaCoreSceneRoundTripProject";
@ -2404,6 +2476,8 @@ int main() {
MetaCoreTestAssetDatabaseMovePathUpdatesProjectDescriptor();
std::cout << "[RUN] MetaCoreTestComponentRegistryDescriptors..." << std::endl;
MetaCoreTestComponentRegistryDescriptors();
std::cout << "[RUN] MetaCoreTestSceneRenderSyncBuildsRenderableCameraLightSnapshot..." << std::endl;
MetaCoreTestSceneRenderSyncBuildsRenderableCameraLightSnapshot();
std::cout << "[RUN] MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson..." << std::endl;
MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson();
std::cout << "[RUN] MetaCoreTestImportPipelineAndCook..." << std::endl;