public static void SyncExportContext(GameObject exportRoot, ExportContext context) { context.exportRoot = exportRoot.transform.parent; Traverse(exportRoot, InitExportableObjects, context); Transform expRoot = context.exportRoot; var foundAnimators = new List <Transform>(); foreach (var rootBoneXf in context.meshToSkelRoot.Values.ToArray()) { bool alreadyProcessed = false; foreach (var xf in foundAnimators) { if (rootBoneXf.IsChildOf(xf)) { alreadyProcessed = true; break; } } if (alreadyProcessed) { continue; } var animatorXf = rootBoneXf; while (animatorXf != null) { // If there is an animator, assume this is the root of the rig. // This feels very ad hoc, it would be nice to not use a heuristic. var anim = animatorXf.GetComponent <Animator>(); if (anim != null) { // Any root bones under this animator will be merged into their most common ancestor, // which is returned here and becomes the skeleton root. Transform skeletonRoot = MergeBonesBelowAnimator(animatorXf, context); if (skeletonRoot == null) { animatorXf = animatorXf.parent; Debug.LogWarning("No children found under animator: " + UnityTypeConverter.GetPath(animatorXf) + " Root bone XF: " + UnityTypeConverter.GetPath(rootBoneXf)); continue; } foundAnimators.Add(anim.transform); // The skeleton is exported at the skeleton root and UsdSkelAnimation is nested under // this prim as a new prim called "_anim". Unity.Formats.USD.SkelRootSample rootSample = CreateSample <Unity.Formats.USD.SkelRootSample>(context); string skelRootPath = UnityTypeConverter.GetPath(animatorXf.transform, expRoot); string skelPath = UnityTypeConverter.GetPath(skeletonRoot, expRoot); string skelPathSuffix = ""; string skelAnimSuffix = "/_anim"; // When there is a collision between the SkelRoot and the Skeleton, make a new USD Prim // for the Skeleton object. The reason this is safe is as follows: if the object was // imported from USD, then the structure should already be correct and this code path will // not be hit (and hence overrides, etc, will work correctly). If the object was created // in Unity and there happened to be a collision, then we can safely create a new prim // for the Skeleton prim because there will be no existing USD skeleton for which // the namespace must match, hence adding a new prim is still safe. if (skelPath == skelRootPath) { Debug.LogWarning("SkelRoot and Skeleton have the same path, renaming Skeleton"); skelPathSuffix = "/_skel"; } rootSample.animationSource = skelPath + skelAnimSuffix; // For any skinned mesh exported under this SkelRoot, pass along the skeleton path in // the "additional data" member of the exporter. Note that this feels very ad hoc and // should probably be formalized in some way (perhaps as a separate export event for // which the SkinnedMesh exporter can explicitly register). // // While it is possible to bind the skel:skeleton relationship at the SkelRoot and // have it inherit down namespace, the Apple importer did not respect this inheritance // and it sometimes causes issues with geometry embedded in the bone hierarchy. foreach (var p in context.plans) { if (p.Key.transform.IsChildOf(animatorXf.transform)) { foreach (var e in p.Value.exporters) { if (e.exportFunc == MeshExporter.ExportSkinnedMesh) { e.data = skelPath + skelPathSuffix; } } } } CreateExportPlan( animatorXf.gameObject, rootSample, SkeletonExporter.ExportSkelRoot, context, insertFirst: true); CreateExportPlan( animatorXf.gameObject, rootSample, NativeExporter.ExportObject, context, insertFirst: false); CreateExportPlan( skeletonRoot.gameObject, CreateSample <SkeletonSample>(context), SkeletonExporter.ExportSkeleton, context, insertFirst: true, pathSuffix: skelPathSuffix); CreateExportPlan( skeletonRoot.gameObject, CreateSample <SkeletonSample>(context), NativeExporter.ExportObject, context, insertFirst: false, pathSuffix: skelPathSuffix); CreateExportPlan( skeletonRoot.gameObject, CreateSample <SkelAnimationSample>(context), SkeletonExporter.ExportSkelAnimation, context, insertFirst: true, pathSuffix: skelAnimSuffix); // Exporting animation is only possible while in-editor (in 2018 and earlier). #if UNITY_EDITOR #if false // Currently disabled, future work. if (anim.layerCount > 0) { for (int l = 0; l < anim.layerCount; l++) { int clipCount = anim.GetCurrentAnimatorClipInfoCount(l); var clipInfos = anim.GetCurrentAnimatorClipInfo(l); foreach (var clipInfo in clipInfos) { var bindings = UnityEditor.AnimationUtility.GetCurveBindings(clipInfo.clip); // Properties are expressed as individual values, for transforms this is: // m_LocalPosition.x,y,z // m_LocalScale.x,y,z // m_LocalRotation.x,y,z,w // Which means they must be reaggregated into matrices. foreach (var binding in bindings) { if (binding.type != typeof(Transform)) { continue; } Debug.Log(binding.path + "." + binding.propertyName); var knot = UnityEditor.AnimationUtility.GetEditorCurve(clipInfo.clip, binding); } } } } #endif // disabled. #endif // Editor only. break; } animatorXf = animatorXf.parent; } } }
public static void SyncExportContext(GameObject exportRoot, ExportContext context) { context.exportRoot = exportRoot.transform.parent; Traverse(exportRoot, InitExportableObjects, context); Transform expRoot = context.exportRoot; var foundAnimators = new List<Transform>(); foreach (var rootBoneXf in context.meshToSkelRoot.Values.ToArray()) { bool alreadyProcessed = false; foreach (var xf in foundAnimators) { if (rootBoneXf.IsChildOf(xf)) { alreadyProcessed = true; break; } } if (alreadyProcessed) { continue; } var animatorXf = rootBoneXf; while (animatorXf != null) { // If there is an animator, assume this is the root of the rig. // This feels very ad hoc, it would be nice to not use a heuristic. var anim = animatorXf.GetComponent<Animator>(); if (anim != null) { // Any root bones under this animator will be merged into their most common ancestor, // which is returned here and becomes the skeleton root. Transform skeletonRoot = MergeBonesBelowAnimator(animatorXf, context); if (skeletonRoot == null) { animatorXf = animatorXf.parent; Debug.LogWarning("No children found under animator: " + UnityTypeConverter.GetPath(animatorXf) + " Root bone XF: " + UnityTypeConverter.GetPath(rootBoneXf)); continue; } foundAnimators.Add(anim.transform); // The skeleton is exported at the skeleton root and UsdSkelAnimation is nested under // this prim as a new prim called "_anim". SkelRootSample rootSample = CreateSample<SkelRootSample>(context); string skelPath = UnityTypeConverter.GetPath(skeletonRoot, expRoot); rootSample.skeleton = skelPath; rootSample.animationSource = skelPath + "/_anim"; CreateExportPlan( animatorXf.gameObject, rootSample, SkeletonExporter.ExportSkelRoot, context, insertFirst: true); CreateExportPlan( animatorXf.gameObject, rootSample, NativeExporter.ExportObject, context, insertFirst: false); CreateExportPlan( skeletonRoot.gameObject, CreateSample<SkeletonSample>(context), SkeletonExporter.ExportSkeleton, context, insertFirst: true); CreateExportPlan( skeletonRoot.gameObject, CreateSample<SkeletonSample>(context), NativeExporter.ExportObject, context, insertFirst: false); CreateExportPlan( skeletonRoot.gameObject, CreateSample<SkelAnimationSample>(context), SkeletonExporter.ExportSkelAnimation, context, insertFirst: true, pathSuffix: "/_anim"); // Exporting animation is only possible while in-editor (in 2018 and earlier). #if UNITY_EDITOR #if false // Currently disabled, future work. if (anim.layerCount > 0) { for (int l = 0; l < anim.layerCount; l++) { int clipCount = anim.GetCurrentAnimatorClipInfoCount(l); var clipInfos = anim.GetCurrentAnimatorClipInfo(l); foreach (var clipInfo in clipInfos) { var bindings = UnityEditor.AnimationUtility.GetCurveBindings(clipInfo.clip); // Properties are expressed as individual values, for transforms this is: // m_LocalPosition.x,y,z // m_LocalScale.x,y,z // m_LocalRotation.x,y,z,w // Which means they must be reaggregated into matrices. foreach (var binding in bindings) { if (binding.type != typeof(Transform)) { continue; } Debug.Log(binding.path + "." + binding.propertyName); var knot = UnityEditor.AnimationUtility.GetEditorCurve(clipInfo.clip, binding); } } } } #endif // disabled. #endif // Editor only. break; } animatorXf = animatorXf.parent; } } }