From 837f2f6be8aef92aa16769e4f0fdb88179ba6dde Mon Sep 17 00:00:00 2001 From: Rix Date: Tue, 20 Oct 2026 10:42:32 -0700 Subject: [PATCH 1/2] Unity import: find a placed model's objects by their hashed IDs Unity 2019.3+ writes internalIDToNameTable: [] in a model's .meta and names the model's objects by XXH64 of "Type:->0", so an exporter added to a placed .fbx hung off an object we could not find and was dropped (#223). Hash the root and every node's GameObject, Transform, MeshFilter, MeshRenderer and SkinnedMeshRenderer; the .meta's table still wins. A lone top-level node is the prefab root, as in Unity. Fixture tests/fixtures/unity-models saved by Unity 6000.3. --- core/unity_import.cpp | 77 ++- core/unity_import.hh | 13 + src/editor/tests/editor_tests.cpp | 55 ++ .../unity-models/Assets/Models/multi.fbx | Bin 0 -> 18236 bytes .../unity-models/Assets/Models/multi.fbx.meta | 110 ++++ .../unity-models/Assets/Models/rig.fbx | Bin 0 -> 19820 bytes .../unity-models/Assets/Models/rig.fbx.meta | 110 ++++ .../unity-models/Assets/Models/solo.fbx | Bin 0 -> 13916 bytes .../unity-models/Assets/Models/solo.fbx.meta | 110 ++++ .../unity-models/Assets/Scenes/models.unity | 578 ++++++++++++++++++ .../Assets/Scenes/models.unity.meta | 7 + 11 files changed, 1057 insertions(+), 3 deletions(-) create mode 100644 tests/fixtures/unity-models/Assets/Models/multi.fbx create mode 100644 tests/fixtures/unity-models/Assets/Models/multi.fbx.meta create mode 100644 tests/fixtures/unity-models/Assets/Models/rig.fbx create mode 100644 tests/fixtures/unity-models/Assets/Models/rig.fbx.meta create mode 100644 tests/fixtures/unity-models/Assets/Models/solo.fbx create mode 100644 tests/fixtures/unity-models/Assets/Models/solo.fbx.meta create mode 100644 tests/fixtures/unity-models/Assets/Scenes/models.unity create mode 100644 tests/fixtures/unity-models/Assets/Scenes/models.unity.meta diff --git a/core/unity_import.cpp b/core/unity_import.cpp index f3088e1..bc90bcc 100644 --- a/core/unity_import.cpp +++ b/core/unity_import.cpp @@ -957,6 +957,50 @@ void bakeLayout(const YNode& rt, float scaleX, float scaleY, UIElement& e) { } } +// ---- Model internal IDs ---- + +uint64_t xxh64(std::string_view s, uint64_t seed) { + constexpr uint64_t P1 = 11400714785074694791ull, P2 = 14029467366897019727ull, P3 = 1609587929392839161ull, + P4 = 9650029242287828579ull, P5 = 2870177450012600261ull; + auto rotl = [](uint64_t x, int r) { return (x << r) | (x >> (64 - r)); }; + auto le = [&](size_t at, int bytes) { + uint64_t v = 0; + for (int b = 0; b < bytes; ++b) v |= uint64_t(uint8_t(s[at + size_t(b)])) << (8 * b); + return v; + }; + auto round = [&](uint64_t acc, uint64_t in) { return rotl(acc + in * P2, 31) * P1; }; + const size_t n = s.size(); + size_t p = 0; + uint64_t h; + if (n >= 32) { + uint64_t v[4] = {seed + P1 + P2, seed + P2, seed, seed - P1}; + for (; p + 32 <= n; p += 32) + for (int k = 0; k < 4; ++k) v[k] = round(v[k], le(p + size_t(k) * 8, 8)); + h = rotl(v[0], 1) + rotl(v[1], 7) + rotl(v[2], 12) + rotl(v[3], 18); + for (uint64_t x : v) h = (h ^ round(0, x)) * P1 + P4; + } else { + h = seed + P5; + } + h += n; + for (; p + 8 <= n; p += 8) h = rotl(h ^ round(0, le(p, 8)), 27) * P1 + P4; + if (p + 4 <= n) { + h = rotl(h ^ (le(p, 4) * P1), 23) * P2 + P3; + p += 4; + } + for (; p < n; ++p) h = rotl(h ^ (uint64_t(uint8_t(s[p])) * P5), 11) * P1; + h ^= h >> 33; + h *= P2; + h ^= h >> 29; + h *= P3; + h ^= h >> 32; + return h; +} + +long long modelObjectId(std::string_view type, std::string_view path, int n) { + const std::string key = "Type:" + std::string(type) + "->" + std::string(path) + std::to_string(n); + return (long long)xxh64(key); +} + // ---- The importer ---- namespace { @@ -1034,6 +1078,7 @@ struct ModelAsset { std::string error; // internal fileID -> (class, name) from the model's .meta std::map> ids; + bool preserveHierarchy = false; // the .meta keeps a lone top-level node below the root }; // The internal IDs of a model's .meta: internalIDToNameTable (Unity 2019.3+) @@ -1401,11 +1446,14 @@ Importer::Unit Importer::instantiateModel(const Ref& source, const YNode& mod) { std::vector nodeObj; if (ma && ma->error.empty()) { const ImportedModel& im = ma->model; - const bool single = im.nodes.size() == 1; + // Unity makes a lone top-level node the prefab root. + int tops = 0; + for (const ImportedNode& n : im.nodes) tops += n.parent < 0; + const bool single = tops == 1 && !ma->preserveHierarchy; for (size_t k = 0; k < im.nodes.size(); ++k) { const ImportedNode& n = im.nodes[k]; int o = root; - if (!single) { + if (!single || n.parent >= 0) { o = int(m_objs.size()); m_objs.emplace_back(); m_objs.back().name = n.name; @@ -1437,6 +1485,26 @@ Importer::Unit Importer::instantiateModel(const Ref& source, const YNode& mod) { break; } } + // Unity 2019.3+ leaves that table empty: the IDs are hashes of each + // object's path in the model. Where the table names an ID, it wins. + std::map taken; + auto hashed = [&](const std::string& path, int o) { + for (const char* t : {"GameObject", "Transform", "MeshFilter", "MeshRenderer", "SkinnedMeshRenderer"}) { + const std::string p = std::strcmp(t, "GameObject") == 0 ? path : path + "/" + t; + u.byFid.emplace(modelObjectId(t, p, taken[std::string(t) + "->" + p]++), o); + } + }; + std::vector paths(im.nodes.size()); + hashed("//RootNode/root", root); + for (size_t k = 0; k < im.nodes.size(); ++k) { + const ImportedNode& n = im.nodes[k]; + if (single && n.parent < 0) { + paths[k] = "//RootNode/root"; + continue; + } + paths[k] = (n.parent >= 0 ? paths[size_t(n.parent)] : std::string("//RootNode/root")) + "/" + n.name; + hashed(paths[k], nodeObj[k]); + } } for (const YNode& m : mod["m_Modifications"].seq) { const Ref target = refOf(m["target"]); @@ -1555,7 +1623,10 @@ ModelAsset* Importer::model(const std::string& guid, const std::string& path) { // Unity's model import scale, when the .meta sets one. for (const YDoc& d : parseYaml(readText(fs::path(src) += ".meta"))) if (const YNode* mi = d.body.find("ModelImporter")) - if (const YNode* meshes = mi->find("meshes")) s.scale = float((*meshes)["globalScale"].num(1)); + if (const YNode* meshes = mi->find("meshes")) { + s.scale = float((*meshes)["globalScale"].num(1)); + ma->preserveHierarchy = (*meshes)["preserveHierarchy"].flag(false); + } saveImportSettings(copy, s); ma->model = importModel(copy, m_dest, s); ma->ids = metaIds(fs::path(src) += ".meta"); diff --git a/core/unity_import.hh b/core/unity_import.hh index f3c6410..1112c5a 100644 --- a/core/unity_import.hh +++ b/core/unity_import.hh @@ -125,6 +125,19 @@ struct Primitive { std::optional builtinMesh(long long fileId); constexpr const char* kBuiltinGuid = "0000000000000000e000000000000000"; +// ---- Model internal IDs ---- + +// XXH64 of `bytes`. +uint64_t xxh64(std::string_view bytes, uint64_t seed = 0); +// The fileID Unity 2019.3+ gives an object inside a model it imported (its +// .meta's internalIDToNameTable is then empty): XXH64 of the UTF-8 +// "Type:->" as a signed 64-bit value, counting the +// earlier objects of the model with the same type and path. The prefab +// root's path is "//RootNode/root", a node's is its parent's + "/" +// (a model's single top-level node is the root), a component's is its +// object's + "/": "Type:Transform->//RootNode/root/Arm/Transform0". +long long modelObjectId(std::string_view type, std::string_view path, int n = 0); + // ---- UI ---- // A RectTransform body (m_AnchorMin/Max, m_AnchoredPosition, m_SizeDelta, diff --git a/src/editor/tests/editor_tests.cpp b/src/editor/tests/editor_tests.cpp index cfc1d68..05998ba 100644 --- a/src/editor/tests/editor_tests.cpp +++ b/src/editor/tests/editor_tests.cpp @@ -12715,6 +12715,60 @@ void testUnityPrefab() { CHECK(lamp2 && lamp2->children.size() == 2); } +// Exporters added to models placed in a scene (#223): Unity 6 leaves the +// model's internalIDToNameTable empty, the scene names the model's objects +// by hashed IDs. Fixture: tests/fixtures/unity-models, saved by Unity 6000.3. +void testUnityModelIds() { + using namespace splash::unity; + using namespace unitytest; + CHECK(xxh64("") == 0xEF46DB3751D8E999ull); + CHECK(xxh64("abc") == 0x44BC2CF5AD770999ull); + // IDs Unity wrote: the root and its Transform, a node and its renderer + // (URP's SandBoxModel.fbx), nested, a lone top-level node's child, skinned. + CHECK(modelObjectId("GameObject", "//RootNode/root") == 919132149155446097LL); + CHECK(modelObjectId("Transform", "//RootNode/root/Transform") == -8679921383154817045LL); + CHECK(modelObjectId("GameObject", "//RootNode/root/Circle2") == 3650834566929780429LL); + CHECK(modelObjectId("MeshRenderer", "//RootNode/root/Circle2/MeshRenderer") == 5912129206897006233LL); + CHECK(modelObjectId("GameObject", "//RootNode/root/Base/Arm") == -1537605290578348017LL); + CHECK(modelObjectId("GameObject", "//RootNode/root/Tip") == -8469990867830010200LL); + CHECK(modelObjectId("SkinnedMeshRenderer", "//RootNode/root/Body/SkinnedMeshRenderer") == 1630794972795428178LL); + + const fs::path proj = fs::path(SPLASHEDIT_SOURCE_DIR) / "tests" / "fixtures" / "unity-models"; + const fs::path dest = g_outDir / "unity-model-ids"; + fs::remove_all(dest); + fs::create_directories(dest); + ImportReport r; + const splash::Scene s = convertScene(proj, indexGuids(proj), proj / "Assets" / "Scenes" / "models.unity", dest, r); + std::function&, const std::string&)> at = + [&](const std::vector& objs, const std::string& path) -> const splash::Object* { + const size_t slash = path.find('/'); + const splash::Object* o = findObj(objs, path.substr(0, slash)); + return !o || slash == std::string::npos ? o : at(o->children, path.substr(slash + 1)); + }; + auto meshed = [&](const char* path) { + const splash::Object* o = at(s.objects, path); + return o && o->mesh && !o->mesh->mesh.empty(); + }; + const char* noExporter = "has a mesh but no PSXObjectExporter"; + // solo.fbx: its one top-level node is the root, its child under it. + CHECK(meshed("solo") && meshed("solo/Tip")); + CHECK(at(s.objects, "solo") && at(s.objects, "solo")->children.size() == 1); + // multi.fbx: a nested node and a top-level one carry the added exporter. + CHECK(meshed("multi/Base/Arm") && meshed("multi/Lamp")); + CHECK(!meshed("multi/Base") && !meshed("multi/Base/Arm/Hand")); + CHECK(hasItem(r, ReportItem::LeftOut, "multi/Base", noExporter) && hasItem(r, ReportItem::LeftOut, "multi/Base/Arm/Hand", noExporter)); + // rig.fbx: the skinned child holds its PSXSkinnedObjectExporter. + CHECK(meshed("rig/Prop") && at(s.objects, "rig/Body")); + CHECK(hasItem(r, ReportItem::Later, "rig/Body", "PSXSkinnedObjectExporter")); + for (const char* p : {"solo", "solo/Tip", "multi", "multi/Base/Arm", "multi/Lamp", "rig/Prop"}) + CHECK(!hasItem(r, ReportItem::LeftOut, p, noExporter)); + // The instances' root overrides land on the root. + CHECK(at(s.objects, "rig") && at(s.objects, "rig")->transform.position.x == 6); + CHECK(at(s.objects, "multi") && at(s.objects, "multi")->transform.position.x == 3); + if (g_failures) + for (const auto& it : r.items) std::fprintf(stderr, " item %d %s: %s\n", int(it.kind), it.object.c_str(), it.what.c_str()); +} + void testUnityPrimitives() { using namespace splash::unity; auto outward = [](const splash::Mesh& m) { @@ -13310,6 +13364,7 @@ int main(int argc, char** argv) { testUnityGuidIndex(); testUnityHierarchy(); testUnityPrefab(); + testUnityModelIds(); testUnityPrimitives(); testUnityReport(); testUnityUi(); diff --git a/tests/fixtures/unity-models/Assets/Models/multi.fbx b/tests/fixtures/unity-models/Assets/Models/multi.fbx new file mode 100644 index 0000000000000000000000000000000000000000..2d35cb991e279528102dfa62838fa4f0ad0e4c7b GIT binary patch literal 18236 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 literal 0 HcmV?d00001 diff --git a/tests/fixtures/unity-models/Assets/Models/multi.fbx.meta b/tests/fixtures/unity-models/Assets/Models/multi.fbx.meta new file mode 100644 index 0000000..5c9a7db --- /dev/null +++ b/tests/fixtures/unity-models/Assets/Models/multi.fbx.meta @@ -0,0 +1,110 @@ +fileFormatVersion: 2 +guid: 6a0cb84423ed59b5ba90c6029650e20b +ModelImporter: + serializedVersion: 24200 + internalIDToNameTable: [] + externalObjects: {} + materials: + materialImportMode: 2 + materialName: 0 + materialSearch: 1 + materialLocation: 1 + animations: + legacyGenerateAnimations: 4 + bakeSimulation: 0 + resampleCurves: 1 + optimizeGameObjects: 0 + removeConstantScaleCurves: 0 + motionNodeName: + animationImportErrors: + animationImportWarnings: + animationRetargetingWarnings: + animationDoRetargetingWarnings: 0 + importAnimatedCustomProperties: 0 + importConstraints: 0 + animationCompression: 1 + animationRotationError: 0.5 + animationPositionError: 0.5 + animationScaleError: 0.5 + animationWrapMode: 0 + extraExposedTransformPaths: [] + extraUserProperties: [] + clipAnimations: [] + isReadable: 0 + meshes: + lODScreenPercentages: [] + globalScale: 1 + meshCompression: 0 + addColliders: 0 + useSRGBMaterialColor: 1 + sortHierarchyByName: 1 + importPhysicalCameras: 1 + importVisibility: 1 + importBlendShapes: 1 + importCameras: 1 + importLights: 1 + nodeNameCollisionStrategy: 1 + fileIdsGeneration: 2 + swapUVChannels: 0 + generateSecondaryUV: 0 + useFileUnits: 1 + keepQuads: 0 + weldVertices: 1 + bakeAxisConversion: 0 + preserveHierarchy: 0 + skinWeightsMode: 0 + maxBonesPerVertex: 4 + minBoneWeight: 0.001 + optimizeBones: 1 + generateMeshLods: 0 + meshLodGenerationFlags: 0 + maximumMeshLod: -1 + meshOptimizationFlags: -1 + indexFormat: 0 + secondaryUVAngleDistortion: 8 + secondaryUVAreaDistortion: 15.000001 + secondaryUVHardAngle: 88 + secondaryUVMarginMethod: 1 + secondaryUVMinLightmapResolution: 40 + secondaryUVMinObjectScale: 1 + secondaryUVPackMargin: 4 + useFileScale: 1 + strictVertexDataChecks: 0 + tangentSpace: + normalSmoothAngle: 60 + normalImportMode: 0 + tangentImportMode: 3 + normalCalculationMode: 4 + legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 + blendShapeNormalImportMode: 1 + normalSmoothingSource: 0 + referencedClips: [] + importAnimation: 1 + humanDescription: + serializedVersion: 3 + human: [] + skeleton: [] + armTwist: 0.5 + foreArmTwist: 0.5 + upperLegTwist: 0.5 + legTwist: 0.5 + armStretch: 0.05 + legStretch: 0.05 + feetSpacing: 0 + globalScale: 1 + rootMotionBoneName: + hasTranslationDoF: 0 + hasExtraRoot: 0 + skeletonHasParents: 1 + lastHumanDescriptionAvatarSource: {instanceID: 0} + autoGenerateAvatarMappingIfUnspecified: 1 + animationType: 2 + humanoidOversampling: 1 + avatarSetup: 0 + addHumanoidExtraRootOnlyWhenUsingAvatar: 1 + importBlendShapeDeformPercent: 1 + remapMaterialsIfMaterialImportModeIsNone: 0 + additionalBone: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/tests/fixtures/unity-models/Assets/Models/rig.fbx b/tests/fixtures/unity-models/Assets/Models/rig.fbx new file mode 100644 index 0000000000000000000000000000000000000000..5378bef81dba92161bea41ef40aa22416f439967 GIT binary patch literal 19820 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 RPw)KATeDvq`K#r#{ulKGX9WNN literal 0 HcmV?d00001 diff --git a/tests/fixtures/unity-models/Assets/Models/rig.fbx.meta b/tests/fixtures/unity-models/Assets/Models/rig.fbx.meta new file mode 100644 index 0000000..3b7f2ec --- /dev/null +++ b/tests/fixtures/unity-models/Assets/Models/rig.fbx.meta @@ -0,0 +1,110 @@ +fileFormatVersion: 2 +guid: a3d6abaa1125b2f5e8d2e4f13466b8fd +ModelImporter: + serializedVersion: 24200 + internalIDToNameTable: [] + externalObjects: {} + materials: + materialImportMode: 2 + materialName: 0 + materialSearch: 1 + materialLocation: 1 + animations: + legacyGenerateAnimations: 4 + bakeSimulation: 0 + resampleCurves: 1 + optimizeGameObjects: 0 + removeConstantScaleCurves: 0 + motionNodeName: + animationImportErrors: + animationImportWarnings: + animationRetargetingWarnings: + animationDoRetargetingWarnings: 0 + importAnimatedCustomProperties: 0 + importConstraints: 0 + animationCompression: 1 + animationRotationError: 0.5 + animationPositionError: 0.5 + animationScaleError: 0.5 + animationWrapMode: 0 + extraExposedTransformPaths: [] + extraUserProperties: [] + clipAnimations: [] + isReadable: 0 + meshes: + lODScreenPercentages: [] + globalScale: 1 + meshCompression: 0 + addColliders: 0 + useSRGBMaterialColor: 1 + sortHierarchyByName: 1 + importPhysicalCameras: 1 + importVisibility: 1 + importBlendShapes: 1 + importCameras: 1 + importLights: 1 + nodeNameCollisionStrategy: 1 + fileIdsGeneration: 2 + swapUVChannels: 0 + generateSecondaryUV: 0 + useFileUnits: 1 + keepQuads: 0 + weldVertices: 1 + bakeAxisConversion: 0 + preserveHierarchy: 0 + skinWeightsMode: 0 + maxBonesPerVertex: 4 + minBoneWeight: 0.001 + optimizeBones: 1 + generateMeshLods: 0 + meshLodGenerationFlags: 0 + maximumMeshLod: -1 + meshOptimizationFlags: -1 + indexFormat: 0 + secondaryUVAngleDistortion: 8 + secondaryUVAreaDistortion: 15.000001 + secondaryUVHardAngle: 88 + secondaryUVMarginMethod: 1 + secondaryUVMinLightmapResolution: 40 + secondaryUVMinObjectScale: 1 + secondaryUVPackMargin: 4 + useFileScale: 1 + strictVertexDataChecks: 0 + tangentSpace: + normalSmoothAngle: 60 + normalImportMode: 0 + tangentImportMode: 3 + normalCalculationMode: 4 + legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 + blendShapeNormalImportMode: 1 + normalSmoothingSource: 0 + referencedClips: [] + importAnimation: 1 + humanDescription: + serializedVersion: 3 + human: [] + skeleton: [] + armTwist: 0.5 + foreArmTwist: 0.5 + upperLegTwist: 0.5 + legTwist: 0.5 + armStretch: 0.05 + legStretch: 0.05 + feetSpacing: 0 + globalScale: 1 + rootMotionBoneName: + hasTranslationDoF: 0 + hasExtraRoot: 0 + skeletonHasParents: 1 + lastHumanDescriptionAvatarSource: {instanceID: 0} + autoGenerateAvatarMappingIfUnspecified: 1 + animationType: 2 + humanoidOversampling: 1 + avatarSetup: 0 + addHumanoidExtraRootOnlyWhenUsingAvatar: 1 + importBlendShapeDeformPercent: 1 + remapMaterialsIfMaterialImportModeIsNone: 0 + additionalBone: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/tests/fixtures/unity-models/Assets/Models/solo.fbx b/tests/fixtures/unity-models/Assets/Models/solo.fbx new file mode 100644 index 0000000000000000000000000000000000000000..3040fc8ddcacaad9d450992811b41dd566063bf8 GIT binary patch literal 13916 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 literal 0 HcmV?d00001 diff --git a/tests/fixtures/unity-models/Assets/Models/solo.fbx.meta b/tests/fixtures/unity-models/Assets/Models/solo.fbx.meta new file mode 100644 index 0000000..0ac348b --- /dev/null +++ b/tests/fixtures/unity-models/Assets/Models/solo.fbx.meta @@ -0,0 +1,110 @@ +fileFormatVersion: 2 +guid: 5331126aa366f93aa9d03ddcd13dee94 +ModelImporter: + serializedVersion: 24200 + internalIDToNameTable: [] + externalObjects: {} + materials: + materialImportMode: 2 + materialName: 0 + materialSearch: 1 + materialLocation: 1 + animations: + legacyGenerateAnimations: 4 + bakeSimulation: 0 + resampleCurves: 1 + optimizeGameObjects: 0 + removeConstantScaleCurves: 0 + motionNodeName: + animationImportErrors: + animationImportWarnings: + animationRetargetingWarnings: + animationDoRetargetingWarnings: 0 + importAnimatedCustomProperties: 0 + importConstraints: 0 + animationCompression: 1 + animationRotationError: 0.5 + animationPositionError: 0.5 + animationScaleError: 0.5 + animationWrapMode: 0 + extraExposedTransformPaths: [] + extraUserProperties: [] + clipAnimations: [] + isReadable: 0 + meshes: + lODScreenPercentages: [] + globalScale: 1 + meshCompression: 0 + addColliders: 0 + useSRGBMaterialColor: 1 + sortHierarchyByName: 1 + importPhysicalCameras: 1 + importVisibility: 1 + importBlendShapes: 1 + importCameras: 1 + importLights: 1 + nodeNameCollisionStrategy: 1 + fileIdsGeneration: 2 + swapUVChannels: 0 + generateSecondaryUV: 0 + useFileUnits: 1 + keepQuads: 0 + weldVertices: 1 + bakeAxisConversion: 0 + preserveHierarchy: 0 + skinWeightsMode: 0 + maxBonesPerVertex: 4 + minBoneWeight: 0.001 + optimizeBones: 1 + generateMeshLods: 0 + meshLodGenerationFlags: 0 + maximumMeshLod: -1 + meshOptimizationFlags: -1 + indexFormat: 0 + secondaryUVAngleDistortion: 8 + secondaryUVAreaDistortion: 15.000001 + secondaryUVHardAngle: 88 + secondaryUVMarginMethod: 1 + secondaryUVMinLightmapResolution: 40 + secondaryUVMinObjectScale: 1 + secondaryUVPackMargin: 4 + useFileScale: 1 + strictVertexDataChecks: 0 + tangentSpace: + normalSmoothAngle: 60 + normalImportMode: 0 + tangentImportMode: 3 + normalCalculationMode: 4 + legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 + blendShapeNormalImportMode: 1 + normalSmoothingSource: 0 + referencedClips: [] + importAnimation: 1 + humanDescription: + serializedVersion: 3 + human: [] + skeleton: [] + armTwist: 0.5 + foreArmTwist: 0.5 + upperLegTwist: 0.5 + legTwist: 0.5 + armStretch: 0.05 + legStretch: 0.05 + feetSpacing: 0 + globalScale: 1 + rootMotionBoneName: + hasTranslationDoF: 0 + hasExtraRoot: 0 + skeletonHasParents: 1 + lastHumanDescriptionAvatarSource: {instanceID: 0} + autoGenerateAvatarMappingIfUnspecified: 1 + animationType: 2 + humanoidOversampling: 1 + avatarSetup: 0 + addHumanoidExtraRootOnlyWhenUsingAvatar: 1 + importBlendShapeDeformPercent: 1 + remapMaterialsIfMaterialImportModeIsNone: 0 + additionalBone: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/tests/fixtures/unity-models/Assets/Scenes/models.unity b/tests/fixtures/unity-models/Assets/Scenes/models.unity new file mode 100644 index 0000000..eaecaf7 --- /dev/null +++ b/tests/fixtures/unity-models/Assets/Scenes/models.unity @@ -0,0 +1,578 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 10 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_BakeOnSceneLoad: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 2 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 1 + m_PVRFilteringGaussRadiusAO: 1 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1001 &221815434 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: -8679921383154817045, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + propertyPath: m_LocalPosition.x + value: 6 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 919132149155446097, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + propertyPath: m_Name + value: rig + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: + - targetCorrespondingSourceObject: {fileID: 635547659545568360, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + insertIndex: -1 + addedObject: {fileID: 221815438} + - targetCorrespondingSourceObject: {fileID: -2971590244256713485, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + insertIndex: -1 + addedObject: {fileID: 221815437} + m_SourcePrefab: {fileID: 100100000, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} +--- !u!1 &221815435 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: -2971590244256713485, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + m_PrefabInstance: {fileID: 221815434} + m_PrefabAsset: {fileID: 0} +--- !u!1 &221815436 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 635547659545568360, guid: a3d6abaa1125b2f5e8d2e4f13466b8fd, type: 3} + m_PrefabInstance: {fileID: 221815434} + m_PrefabAsset: {fileID: 0} +--- !u!114 &221815437 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 221815435} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a192e0a30d827ba40be5c99d32a83a12, type: 3} + m_Name: + m_EditorClassIdentifier: net.psxsplash.splashedit.Runtime::SplashEdit.RuntimeCode.PSXObjectExporter + isActive: 1 + bitDepth: 8 + luaFile: {fileID: 0} + collisionType: 0 + vertexColorMode: 0 + flatVertexColor: + serializedVersion: 2 + rgba: 4286611584 + smoothNormals: 1 + isPlatform: 0 + uvOffsetMaterial: 0 +--- !u!114 &221815438 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 221815436} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a3b7e2f41c9d4a8e95f6d0c3b2e1a7f4, type: 3} + m_Name: + m_EditorClassIdentifier: net.psxsplash.splashedit.Runtime::SplashEdit.RuntimeCode.PSXSkinnedObjectExporter + AnimationClips: [] + TargetFPS: 15 + IsActive: 1 + LuaFile: {fileID: 0} + BitDepth: 8 + ColorMode: 0 + FlatVertexColor: + serializedVersion: 2 + rgba: 4286611584 + SmoothNormals: 1 +--- !u!1001 &718871217 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: -8679921383154817045, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + propertyPath: m_LocalRotation.w + value: 0.7071067 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + propertyPath: m_LocalRotation.x + value: -0.7071068 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 919132149155446097, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + propertyPath: m_Name + value: solo + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + insertIndex: -1 + addedObject: {fileID: 718871220} + - targetCorrespondingSourceObject: {fileID: -8469990867830010200, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + insertIndex: -1 + addedObject: {fileID: 718871221} + m_SourcePrefab: {fileID: 100100000, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} +--- !u!1 &718871218 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 919132149155446097, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + m_PrefabInstance: {fileID: 718871217} + m_PrefabAsset: {fileID: 0} +--- !u!1 &718871219 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: -8469990867830010200, guid: 5331126aa366f93aa9d03ddcd13dee94, type: 3} + m_PrefabInstance: {fileID: 718871217} + m_PrefabAsset: {fileID: 0} +--- !u!114 &718871220 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 718871218} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a192e0a30d827ba40be5c99d32a83a12, type: 3} + m_Name: + m_EditorClassIdentifier: net.psxsplash.splashedit.Runtime::SplashEdit.RuntimeCode.PSXObjectExporter + isActive: 1 + bitDepth: 8 + luaFile: {fileID: 0} + collisionType: 0 + vertexColorMode: 0 + flatVertexColor: + serializedVersion: 2 + rgba: 4286611584 + smoothNormals: 1 + isPlatform: 0 + uvOffsetMaterial: 0 +--- !u!114 &718871221 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 718871219} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a192e0a30d827ba40be5c99d32a83a12, type: 3} + m_Name: + m_EditorClassIdentifier: net.psxsplash.splashedit.Runtime::SplashEdit.RuntimeCode.PSXObjectExporter + isActive: 1 + bitDepth: 8 + luaFile: {fileID: 0} + collisionType: 0 + vertexColorMode: 0 + flatVertexColor: + serializedVersion: 2 + rgba: 4286611584 + smoothNormals: 1 + isPlatform: 0 + uvOffsetMaterial: 0 +--- !u!1001 &1607267918 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: -8679921383154817045, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + propertyPath: m_LocalPosition.x + value: 3 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 919132149155446097, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + propertyPath: m_Name + value: multi + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + insertIndex: -1 + addedObject: {fileID: 1607267924} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + insertIndex: -1 + addedObject: {fileID: 1607267923} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + insertIndex: -1 + addedObject: {fileID: 1607267922} + - targetCorrespondingSourceObject: {fileID: -1537605290578348017, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + insertIndex: -1 + addedObject: {fileID: 1607267926} + - targetCorrespondingSourceObject: {fileID: 8934777400606672865, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + insertIndex: -1 + addedObject: {fileID: 1607267925} + m_SourcePrefab: {fileID: 100100000, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} +--- !u!1 &1607267919 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 919132149155446097, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + m_PrefabInstance: {fileID: 1607267918} + m_PrefabAsset: {fileID: 0} +--- !u!1 &1607267920 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 8934777400606672865, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + m_PrefabInstance: {fileID: 1607267918} + m_PrefabAsset: {fileID: 0} +--- !u!1 &1607267921 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: -1537605290578348017, guid: 6a0cb84423ed59b5ba90c6029650e20b, type: 3} + m_PrefabInstance: {fileID: 1607267918} + m_PrefabAsset: {fileID: 0} +--- !u!114 &1607267922 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1607267919} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a192e0a30d827ba40be5c99d32a83a12, type: 3} + m_Name: + m_EditorClassIdentifier: net.psxsplash.splashedit.Runtime::SplashEdit.RuntimeCode.PSXObjectExporter + isActive: 1 + bitDepth: 8 + luaFile: {fileID: 0} + collisionType: 0 + vertexColorMode: 0 + flatVertexColor: + serializedVersion: 2 + rgba: 4286611584 + smoothNormals: 1 + isPlatform: 0 + uvOffsetMaterial: 0 +--- !u!23 &1607267923 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1607267919} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_MaskInteraction: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1607267924 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1607267919} + m_Mesh: {fileID: 0} +--- !u!114 &1607267925 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1607267920} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a192e0a30d827ba40be5c99d32a83a12, type: 3} + m_Name: + m_EditorClassIdentifier: net.psxsplash.splashedit.Runtime::SplashEdit.RuntimeCode.PSXObjectExporter + isActive: 1 + bitDepth: 8 + luaFile: {fileID: 0} + collisionType: 0 + vertexColorMode: 0 + flatVertexColor: + serializedVersion: 2 + rgba: 4286611584 + smoothNormals: 1 + isPlatform: 0 + uvOffsetMaterial: 0 +--- !u!114 &1607267926 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1607267921} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a192e0a30d827ba40be5c99d32a83a12, type: 3} + m_Name: + m_EditorClassIdentifier: net.psxsplash.splashedit.Runtime::SplashEdit.RuntimeCode.PSXObjectExporter + isActive: 1 + bitDepth: 8 + luaFile: {fileID: 0} + collisionType: 0 + vertexColorMode: 0 + flatVertexColor: + serializedVersion: 2 + rgba: 4286611584 + smoothNormals: 1 + isPlatform: 0 + uvOffsetMaterial: 0 +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 718871217} + - {fileID: 1607267918} + - {fileID: 221815434} diff --git a/tests/fixtures/unity-models/Assets/Scenes/models.unity.meta b/tests/fixtures/unity-models/Assets/Scenes/models.unity.meta new file mode 100644 index 0000000..f7bf611 --- /dev/null +++ b/tests/fixtures/unity-models/Assets/Scenes/models.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a33ad2e8bd281a29584aa5facb32c7cd +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 04197f7af2acf647162a4594dc719f6559bfd2be Mon Sep 17 00:00:00 2001 From: Rix Date: Tue, 20 Oct 2026 10:42:45 -0700 Subject: [PATCH 2/2] Unity import: no "no PSXObjectExporter" item beside a skinned exporter An object holding a PSXSkinnedObjectExporter was reported both as "not imported yet" and as having a mesh Unity did not export, which Unity does export (as a skinned mesh). Keep the first item only. --- core/unity_import.cpp | 4 +++- src/editor/tests/editor_tests.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/unity_import.cpp b/core/unity_import.cpp index bc90bcc..a7b3fe0 100644 --- a/core/unity_import.cpp +++ b/core/unity_import.cpp @@ -2178,8 +2178,10 @@ void Importer::convertObject(int idx, const std::string& parentPath, std::vector o.active = u.active; o.transform = u.t; const UComp *exp = nullptr, *mf = nullptr, *mr = nullptr; + bool skinExp = false; // its mesh is reported as not imported yet std::vector leftOut; for (const UComp& c : u.comps) { + skinExp |= c.classId == 114 && c.cls == "PSXSkinnedObjectExporter"; if (c.classId == 33) mf = &c; else if (c.classId == 23) mr = &c; else if (c.classId == 114 && c.cls == "PSXObjectExporter") exp = &c; @@ -2279,7 +2281,7 @@ void Importer::convertObject(int idx, const std::string& parentPath, std::vector } const std::string lua = luaOf(field("luaFile", "LuaFile"), path, "script"); if (!lua.empty()) o.script = ScriptComponent{lua, {}}; - } else if ((mf && !refOf((*mf->body)["m_Mesh"]).null()) || !u.modelMesh.empty()) { + } else if (!skinExp && ((mf && !refOf((*mf->body)["m_Mesh"]).null()) || !u.modelMesh.empty())) { item(ReportItem::LeftOut, path, "has a mesh but no PSXObjectExporter, so Unity did not export it either; it is kept without its mesh"); } if (!leftOut.empty()) { diff --git a/src/editor/tests/editor_tests.cpp b/src/editor/tests/editor_tests.cpp index 05998ba..146142a 100644 --- a/src/editor/tests/editor_tests.cpp +++ b/src/editor/tests/editor_tests.cpp @@ -12760,7 +12760,7 @@ void testUnityModelIds() { // rig.fbx: the skinned child holds its PSXSkinnedObjectExporter. CHECK(meshed("rig/Prop") && at(s.objects, "rig/Body")); CHECK(hasItem(r, ReportItem::Later, "rig/Body", "PSXSkinnedObjectExporter")); - for (const char* p : {"solo", "solo/Tip", "multi", "multi/Base/Arm", "multi/Lamp", "rig/Prop"}) + for (const char* p : {"solo", "solo/Tip", "multi", "multi/Base/Arm", "multi/Lamp", "rig/Prop", "rig/Body"}) CHECK(!hasItem(r, ReportItem::LeftOut, p, noExporter)); // The instances' root overrides land on the root. CHECK(at(s.objects, "rig") && at(s.objects, "rig")->transform.position.x == 6);