fix(render): 修复层级嵌套模型加载失败及拖动父节点时子级坐标同步Bug

This commit is contained in:
ayuan9957 2026-05-22 15:13:57 +08:00
parent 05157022bd
commit 36e3155a8a
5 changed files with 107 additions and 553 deletions

View File

@ -57,547 +57,12 @@
1.0
]
}
},
{
"Id": 3,
"MeshRenderer": {
"AlphaCutoff": 0.5,
"AlphaMode": 0,
"AoTextureGuid": "",
"AoTexturePath": "",
"BaseColor": [
1.0,
1.0,
1.0
],
"BaseColorTextureGuid": "",
"BaseColorTexturePath": "",
"BuiltinMesh": 0,
"DoubleSided": false,
"EmissiveColor": [
0.0,
0.0,
0.0
],
"EmissiveTextureGuid": "",
"EmissiveTexturePath": "",
"MaterialAssetGuids": [
"9833a707-d635-426e-5db2-4cb58fbb5d03"
],
"MeshAssetGuid": "34a6ed73-d4a5-83a3-5863-742c204c4869",
"MeshSource": 1,
"Metallic": 0.0,
"MetallicRoughnessTextureGuid": "",
"MetallicRoughnessTexturePath": "",
"ModelNodeIndex": 0,
"NormalTextureGuid": "",
"NormalTexturePath": "",
"Roughness": 1.0,
"SourceModelAssetGuid": "7033a4a0-8f64-4826-8fec-22ce0fccb514",
"SourceModelPath": "Assets/Models/box1.glb",
"SourceNodePath": "box1",
"SubMeshIndex": -1,
"Visible": true
},
"Name": "box1.glb",
"ParentId": 0,
"Transform": {
"Position": [
0.0,
0.0,
0.0
],
"RotationEulerDegrees": [
0.0,
0.0,
0.0
],
"Scale": [
1.0,
1.0,
1.0
]
}
},
{
"Id": 4,
"MeshRenderer": {
"AlphaCutoff": 0.5,
"AlphaMode": 0,
"AoTextureGuid": "",
"AoTexturePath": "",
"BaseColor": [
0.75,
0.7799999713897705,
0.8399999737739563
],
"BaseColorTextureGuid": "",
"BaseColorTexturePath": "",
"BuiltinMesh": 0,
"DoubleSided": true,
"EmissiveColor": [
0.0,
0.0,
0.0
],
"EmissiveTextureGuid": "",
"EmissiveTexturePath": "",
"MaterialAssetGuids": [],
"MeshAssetGuid": "",
"MeshSource": 1,
"Metallic": 0.0,
"MetallicRoughnessTextureGuid": "",
"MetallicRoughnessTexturePath": "",
"ModelNodeIndex": 0,
"NormalTextureGuid": "",
"NormalTexturePath": "",
"Roughness": 1.0,
"SourceModelAssetGuid": "",
"SourceModelPath": "",
"SourceNodePath": "",
"SubMeshIndex": -1,
"Visible": true
},
"Name": "立方体",
"ParentId": 3,
"Transform": {
"Position": [
-1.6927926540374756,
-4.459673881530762,
1.949385364241607e-07
],
"RotationEulerDegrees": [
0.0,
-0.0,
0.0
],
"Scale": [
0.9999998807907104,
1.0,
1.0
]
}
},
{
"Id": 5,
"MeshRenderer": {
"AlphaCutoff": 0.5,
"AlphaMode": 0,
"AoTextureGuid": "",
"AoTexturePath": "",
"BaseColor": [
0.75,
0.7799999713897705,
0.8399999737739563
],
"BaseColorTextureGuid": "",
"BaseColorTexturePath": "",
"BuiltinMesh": 0,
"DoubleSided": true,
"EmissiveColor": [
0.0,
0.0,
0.0
],
"EmissiveTextureGuid": "",
"EmissiveTexturePath": "",
"MaterialAssetGuids": [],
"MeshAssetGuid": "",
"MeshSource": 1,
"Metallic": 0.0,
"MetallicRoughnessTextureGuid": "",
"MetallicRoughnessTexturePath": "",
"ModelNodeIndex": 1,
"NormalTextureGuid": "",
"NormalTexturePath": "",
"Roughness": 1.0,
"SourceModelAssetGuid": "",
"SourceModelPath": "",
"SourceNodePath": "",
"SubMeshIndex": -1,
"Visible": true
},
"Name": "立方体.001",
"ParentId": 4,
"Transform": {
"Position": [
0.0,
-3.55507755279541,
1.553973731915903e-07
],
"RotationEulerDegrees": [
0.0,
-0.0,
0.0
],
"Scale": [
0.9999998807907104,
1.0,
1.0
]
}
},
{
"Id": 6,
"MeshRenderer": {
"AlphaCutoff": 0.5,
"AlphaMode": 0,
"AoTextureGuid": "",
"AoTexturePath": "",
"BaseColor": [
0.75,
0.7799999713897705,
0.8399999737739563
],
"BaseColorTextureGuid": "",
"BaseColorTexturePath": "",
"BuiltinMesh": 0,
"DoubleSided": true,
"EmissiveColor": [
0.0,
0.0,
0.0
],
"EmissiveTextureGuid": "",
"EmissiveTexturePath": "",
"MaterialAssetGuids": [],
"MeshAssetGuid": "",
"MeshSource": 1,
"Metallic": 0.0,
"MetallicRoughnessTextureGuid": "",
"MetallicRoughnessTexturePath": "",
"ModelNodeIndex": 2,
"NormalTextureGuid": "",
"NormalTexturePath": "",
"Roughness": 1.0,
"SourceModelAssetGuid": "",
"SourceModelPath": "",
"SourceNodePath": "",
"SubMeshIndex": -1,
"Visible": true
},
"Name": "立方体.007",
"ParentId": 5,
"Transform": {
"Position": [
4.175572872161865,
0.0,
0.0
],
"RotationEulerDegrees": [
0.0,
-0.0,
0.0
],
"Scale": [
0.9999998807907104,
1.0,
1.0
]
}
},
{
"Id": 7,
"MeshRenderer": {
"AlphaCutoff": 0.5,
"AlphaMode": 0,
"AoTextureGuid": "",
"AoTexturePath": "",
"BaseColor": [
0.75,
0.7799999713897705,
0.8399999737739563
],
"BaseColorTextureGuid": "",
"BaseColorTexturePath": "",
"BuiltinMesh": 0,
"DoubleSided": true,
"EmissiveColor": [
0.0,
0.0,
0.0
],
"EmissiveTextureGuid": "",
"EmissiveTexturePath": "",
"MaterialAssetGuids": [],
"MeshAssetGuid": "",
"MeshSource": 1,
"Metallic": 0.0,
"MetallicRoughnessTextureGuid": "",
"MetallicRoughnessTexturePath": "",
"ModelNodeIndex": 3,
"NormalTextureGuid": "",
"NormalTexturePath": "",
"Roughness": 1.0,
"SourceModelAssetGuid": "",
"SourceModelPath": "",
"SourceNodePath": "",
"SubMeshIndex": -1,
"Visible": true
},
"Name": "立方体.003",
"ParentId": 3,
"Transform": {
"Position": [
0.0,
0.0,
0.0
],
"RotationEulerDegrees": [
0.0,
-0.0,
0.0
],
"Scale": [
0.9999998807907104,
1.0,
1.0
]
}
},
{
"Id": 8,
"MeshRenderer": {
"AlphaCutoff": 0.5,
"AlphaMode": 0,
"AoTextureGuid": "",
"AoTexturePath": "",
"BaseColor": [
0.75,
0.7799999713897705,
0.8399999737739563
],
"BaseColorTextureGuid": "",
"BaseColorTexturePath": "",
"BuiltinMesh": 0,
"DoubleSided": true,
"EmissiveColor": [
0.0,
0.0,
0.0
],
"EmissiveTextureGuid": "",
"EmissiveTexturePath": "",
"MaterialAssetGuids": [],
"MeshAssetGuid": "",
"MeshSource": 1,
"Metallic": 0.0,
"MetallicRoughnessTextureGuid": "",
"MetallicRoughnessTexturePath": "",
"ModelNodeIndex": 4,
"NormalTextureGuid": "",
"NormalTexturePath": "",
"Roughness": 1.0,
"SourceModelAssetGuid": "",
"SourceModelPath": "",
"SourceNodePath": "",
"SubMeshIndex": -1,
"Visible": true
},
"Name": "立方体.002",
"ParentId": 7,
"Transform": {
"Position": [
0.0,
-1.4481612709005276e-07,
-3.31300687789917
],
"RotationEulerDegrees": [
0.0,
-0.0,
0.0
],
"Scale": [
0.9999998807907104,
1.0,
1.0
]
}
},
{
"Id": 9,
"MeshRenderer": {
"AlphaCutoff": 0.5,
"AlphaMode": 0,
"AoTextureGuid": "",
"AoTexturePath": "",
"BaseColor": [
0.75,
0.7799999713897705,
0.8399999737739563
],
"BaseColorTextureGuid": "",
"BaseColorTexturePath": "",
"BuiltinMesh": 0,
"DoubleSided": true,
"EmissiveColor": [
0.0,
0.0,
0.0
],
"EmissiveTextureGuid": "",
"EmissiveTexturePath": "",
"MaterialAssetGuids": [],
"MeshAssetGuid": "",
"MeshSource": 1,
"Metallic": 0.0,
"MetallicRoughnessTextureGuid": "",
"MetallicRoughnessTexturePath": "",
"ModelNodeIndex": 5,
"NormalTextureGuid": "",
"NormalTexturePath": "",
"Roughness": 1.0,
"SourceModelAssetGuid": "",
"SourceModelPath": "",
"SourceNodePath": "",
"SubMeshIndex": -1,
"Visible": true
},
"Name": "立方体.004",
"ParentId": 7,
"Transform": {
"Position": [
3.776623010635376,
0.0,
0.0
],
"RotationEulerDegrees": [
0.0,
-0.0,
0.0
],
"Scale": [
0.9999998807907104,
1.0,
1.0
]
}
},
{
"Id": 10,
"MeshRenderer": {
"AlphaCutoff": 0.5,
"AlphaMode": 0,
"AoTextureGuid": "",
"AoTexturePath": "",
"BaseColor": [
0.75,
0.7799999713897705,
0.8399999737739563
],
"BaseColorTextureGuid": "",
"BaseColorTexturePath": "",
"BuiltinMesh": 0,
"DoubleSided": true,
"EmissiveColor": [
0.0,
0.0,
0.0
],
"EmissiveTextureGuid": "",
"EmissiveTexturePath": "",
"MaterialAssetGuids": [],
"MeshAssetGuid": "",
"MeshSource": 1,
"Metallic": 0.0,
"MetallicRoughnessTextureGuid": "",
"MetallicRoughnessTexturePath": "",
"ModelNodeIndex": 6,
"NormalTextureGuid": "",
"NormalTexturePath": "",
"Roughness": 1.0,
"SourceModelAssetGuid": "",
"SourceModelPath": "",
"SourceNodePath": "",
"SubMeshIndex": -1,
"Visible": true
},
"Name": "立方体.005",
"ParentId": 7,
"Transform": {
"Position": [
0.0,
4.31797981262207,
-1.8874489171594178e-07
],
"RotationEulerDegrees": [
0.0,
-0.0,
0.0
],
"Scale": [
0.9999998807907104,
1.0,
1.0
]
}
},
{
"Id": 11,
"MeshRenderer": {
"AlphaCutoff": 0.5,
"AlphaMode": 0,
"AoTextureGuid": "",
"AoTexturePath": "",
"BaseColor": [
0.75,
0.7799999713897705,
0.8399999737739563
],
"BaseColorTextureGuid": "",
"BaseColorTexturePath": "",
"BuiltinMesh": 0,
"DoubleSided": true,
"EmissiveColor": [
0.0,
0.0,
0.0
],
"EmissiveTextureGuid": "",
"EmissiveTexturePath": "",
"MaterialAssetGuids": [],
"MeshAssetGuid": "",
"MeshSource": 1,
"Metallic": 0.0,
"MetallicRoughnessTextureGuid": "",
"MetallicRoughnessTexturePath": "",
"ModelNodeIndex": 7,
"NormalTextureGuid": "",
"NormalTexturePath": "",
"Roughness": 1.0,
"SourceModelAssetGuid": "",
"SourceModelPath": "",
"SourceNodePath": "",
"SubMeshIndex": -1,
"Visible": true
},
"Name": "立方体.006",
"ParentId": 7,
"Transform": {
"Position": [
-4.638434886932373,
0.0,
0.0
],
"RotationEulerDegrees": [
0.0,
-0.0,
0.0
],
"Scale": [
0.9999998807907104,
1.0,
1.0
]
}
}
],
"SceneName": "Main",
"Selection": {
"ActiveObjectId": 3,
"SelectedObjectIds": [
3
],
"SelectionAnchorId": 3
"ActiveObjectId": 0,
"SelectedObjectIds": [],
"SelectionAnchorId": 0
}
}

View File

@ -5833,6 +5833,11 @@ std::optional<MetaCoreId> MetaCoreBuiltinSceneEditingService::InstantiateModelAs
std::vector<MetaCoreId> nodeToIdMap(importedDocument->Nodes.size(), 0);
if (importedDocument->Nodes.size() == 1) {
// 给顶级根节点打上 Tag用以在渲染同步回溯时精确定位宿主根
if (!modelRootObject.HasComponent<MetaCoreModelRootTag>()) {
modelRootObject.AddComponent<MetaCoreModelRootTag>(MetaCoreModelRootTag{ resolvedModelPath.generic_string() });
}
// 【平坦化优化】:针对单网格模型(如 box11.glb不创建多余子级直接将渲染器挂在顶级节点上
const auto& nodeMeta = importedDocument->Nodes[0];
nodeToIdMap[0] = instantiatedRootId;

View File

@ -653,7 +653,12 @@ public:
if (itMat != snapshot.LocalMatrices.end()) {
initLocalMat = itMat->second;
}
UpdateTransform(renderable.ObjectId, initLocalMat);
glm::mat4 initWorldMat = initLocalMat;
auto itWorldMat = snapshot.WorldMatrices.find(renderable.ObjectId);
if (itWorldMat != snapshot.WorldMatrices.end()) {
initWorldMat = itWorldMat->second;
}
UpdateTransform(renderable.ObjectId, initLocalMat, initWorldMat);
}
// 1. 构建反向查找映射,方便反查 entity 对应的 objectId
@ -753,10 +758,11 @@ public:
// 4. 同步所有存活实体的本地和全局变换矩阵
for (const auto& [objectId, assetEntity] : ObjectToFilamentEntity_) {
if (snapshot.WorldMatrices.contains(objectId)) {
auto itMat = snapshot.LocalMatrices.find(objectId);
if (itMat != snapshot.LocalMatrices.end()) {
UpdateTransform(objectId, itMat->second);
auto itWorld = snapshot.WorldMatrices.find(objectId);
if (itWorld != snapshot.WorldMatrices.end()) {
auto itLocal = snapshot.LocalMatrices.find(objectId);
if (itLocal != snapshot.LocalMatrices.end()) {
UpdateTransform(objectId, itLocal->second, itWorld->second);
}
}
}
@ -1036,7 +1042,7 @@ private:
}
}
void UpdateTransform(MetaCoreId objectId, const glm::mat4& localMatrix) {
void UpdateTransform(MetaCoreId objectId, const glm::mat4& localMatrix, const glm::mat4& worldMatrix) {
auto it = ObjectToFilamentEntity_.find(objectId);
if (it == ObjectToFilamentEntity_.end()) return;
@ -1045,7 +1051,8 @@ private:
auto& tm = Engine_->getTransformManager();
auto instance = tm.getInstance(entity);
if (instance) {
glm::mat4 matrix = localMatrix;
auto parent = tm.getParent(instance);
glm::mat4 matrix = parent ? localMatrix : worldMatrix;
// 【关键修复】:如果当前 entity 是子节点(即不是顶级映射的 pivotEntity
// 由于它的父级assetRoot带有了 -90X 旋转,我们必须对子节点应用基变换来抵消这个旋转。

View File

@ -89,18 +89,16 @@ MetaCoreSceneRenderSyncSnapshot MetaCoreSceneRenderSync::BuildSnapshot(const Met
MetaCoreGameObject foundRoot = {};
// 1. 优先通过运行时 MetaCoreModelRootTag 快速寻找顶级根
while (current.GetParentId() != 0) {
auto parentObj = scene.FindGameObject(current.GetParentId());
if (!parentObj) break;
if (parentObj.HasComponent<MetaCoreModelRootTag>()) {
auto& tag = parentObj.GetComponent<MetaCoreModelRootTag>();
while (current) {
if (current.HasComponent<MetaCoreModelRootTag>()) {
auto& tag = current.GetComponent<MetaCoreModelRootTag>();
if (MetaCoreNormalizeSyncPath(tag.SourceModelPath) == normCurrentPath) {
foundRoot = parentObj;
foundRoot = current;
break;
}
}
current = parentObj;
if (current.GetParentId() == 0) break;
current = scene.FindGameObject(current.GetParentId());
}
if (foundRoot) {

View File

@ -2830,6 +2830,82 @@ void MetaCoreTestDecoupledSnapshotSync() {
window.Shutdown();
}
void MetaCoreTestNestedModelHierarchyTransformSync() {
// 1. 创建测试场景,建立嵌套模型和父子变换结构
MetaCore::MetaCoreScene scene = MetaCore::MetaCoreCreateDefaultScene();
// 模拟根模型 (box1.glb)
MetaCore::MetaCoreGameObject parentModel = scene.CreateGameObject("ParentModel");
auto& parentMesh = parentModel.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
parentMesh.MeshSource = MetaCore::MetaCoreMeshSourceKind::Asset;
parentMesh.SourceModelPath = "Assets/Models/box1.glb";
parentMesh.SourceModelAssetGuid = MetaCore::MetaCoreAssetRegistry::Get().ResolvePathToGuid("Assets/Models/box1.glb");
parentModel.AddComponent<MetaCore::MetaCoreModelRootTag>(MetaCore::MetaCoreModelRootTag{ "Assets/Models/box1.glb" });
parentModel.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(0.0F, 1.0F, 0.0F);
// 模拟子模型 (嵌套相同的 box1.glb其 parent 为 parentModel)
MetaCore::MetaCoreGameObject childModel = scene.CreateGameObject("ChildModel", parentModel.GetId());
auto& childMesh = childModel.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
childMesh.MeshSource = MetaCore::MetaCoreMeshSourceKind::Asset;
childMesh.SourceModelPath = "Assets/Models/box1.glb";
childMesh.SourceModelAssetGuid = MetaCore::MetaCoreAssetRegistry::Get().ResolvePathToGuid("Assets/Models/box1.glb");
childModel.AddComponent<MetaCore::MetaCoreModelRootTag>(MetaCore::MetaCoreModelRootTag{ "Assets/Models/box1.glb" });
childModel.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(2.0F, 0.0F, 0.0F); // 局部偏移 x + 2.0
MetaCore::MetaCoreSceneRenderSync renderSync;
MetaCore::MetaCoreSceneRenderSyncSnapshot snapshot = renderSync.BuildSnapshot(scene);
// 验证 Bug 2 修复:子模型的 HostRootId 应该为它自己,而不是父模型
bool foundChild = false;
for (const auto& renderable : snapshot.Renderables) {
if (renderable.ObjectId == childModel.GetId()) {
foundChild = true;
MetaCoreExpect(renderable.HostRootId == childModel.GetId(), "子模型的 HostRootId 必须等于其自身的 ID不能被错判为父模型 ID");
}
}
MetaCoreExpect(foundChild, "快照的渲染实体列表中应包含 ChildModel");
// 2. 初始化桥接器
MetaCore::MetaCoreWindow window;
bool winOk = window.Initialize(800, 600, "NestedModelHierarchyTestWindow");
MetaCoreExpect(winOk, "测试窗口初始化成功");
MetaCore::MetaCoreFilamentSceneBridge bridge;
bridge.SetProjectRootPath("d:/MetaCore/SandboxProject");
bool bridgeOk = bridge.Initialize(window);
MetaCoreExpect(bridgeOk, "测试桥接器初始化成功");
// 同步快照
bridge.SyncScene(snapshot, nullptr, false, false);
// 验证 Bug 1 修复:在没有 Filament 层级关系下,子模型在 Filament 侧的变换矩阵应当正确跟随父模型的世界矩阵
// 子模型局部矩阵
glm::mat4 childLocalMat = glm::translate(glm::mat4(1.0F), glm::vec3(2.0F, 0.0F, 0.0F));
// 父模型世界矩阵
glm::mat4 parentWorldMat = glm::translate(glm::mat4(1.0F), glm::vec3(0.0F, 1.0F, 0.0F));
// 预期的子模型世界矩阵
glm::mat4 expectedChildWorldMat = parentWorldMat * childLocalMat;
bool transOk = bridge.VerifyTransformForTesting(childModel.GetId(), expectedChildWorldMat);
MetaCoreExpect(transOk, "子模型在 Filament 侧的最终变换矩阵应正确应用世界矩阵");
// 3. 拖拽父级:修改父模型位置,重新同步,并校验子模型是否跟着移动
parentModel.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(10.0F, 5.0F, 0.0F);
// 重新构建快照与同步
snapshot = renderSync.BuildSnapshot(scene);
bridge.SyncScene(snapshot, nullptr, false, false);
glm::mat4 newParentWorldMat = glm::translate(glm::mat4(1.0F), glm::vec3(10.0F, 5.0F, 0.0F));
glm::mat4 newExpectedChildWorldMat = newParentWorldMat * childLocalMat;
bool transOk2 = bridge.VerifyTransformForTesting(childModel.GetId(), newExpectedChildWorldMat);
MetaCoreExpect(transOk2, "拖拽父级后,子模型在 Filament 侧的最终变换矩阵应实时跟随父模型世界矩阵更新");
bridge.Shutdown();
window.Shutdown();
}
void MetaCoreTestP3ProductivityStage() {
const std::filesystem::path tempProjectRoot =
std::filesystem::temp_directory_path() / "MetaCoreTestP3Project";
@ -3115,6 +3191,9 @@ int main() {
std::cout << "[RUN] MetaCoreTestDecoupledSnapshotSync..." << std::endl;
MetaCoreTestDecoupledSnapshotSync();
std::cout << "[RUN] MetaCoreTestNestedModelHierarchyTransformSync..." << std::endl;
MetaCoreTestNestedModelHierarchyTransformSync();
std::cout << "[RUN] MetaCoreTestP3ProductivityStage..." << std::endl;
MetaCoreTestP3ProductivityStage();