Ejemplo n.º 1
0
        static void MergeBonesSimple(Transform source,
                                     Transform rootBone,
                                     Transform[] bones,
                                     Matrix4x4[] bindPoses,
                                     ExportContext context)
        {
            context.meshToSkelRoot.Add(source, rootBone);
            context.meshToBones.Add(source, bones);
            Matrix4x4 existingMatrix;

            for (int i = 0; i < bones.Length; i++)
            {
                Transform bone = bones[i];
                if (bone == null)
                {
                    var srcPath = UnityTypeConverter.GetPath(source);
                    Debug.LogWarning("Null bone at in bones list at position (" + i + ") " + srcPath);
                    continue;
                }

                var path = UnityTypeConverter.GetPath(bone);
                context.pathToBone[path] = bone;
                context.boneToRoot[bone] = rootBone;
                context.bindPoses[bone]  = bindPoses[i];
                if (context.bindPoses.TryGetValue(bone, out existingMatrix) && existingMatrix != bindPoses[i])
                {
                    Debug.LogWarning("Duplicate bone with different bind poses: " + path + "\n" +
                                     existingMatrix.ToString() + "\n" + bindPoses[i].ToString());
                }
            }
        }
Ejemplo n.º 2
0
        public static void ExportSkeleton(ObjectContext objContext, ExportContext exportContext)
        {
            var scene     = exportContext.scene;
            var sample    = (SkeletonSample)objContext.sample;
            var boneNames = exportContext.skelSortedMap[objContext.gameObject.transform];

            sample.joints         = new string[boneNames.Count];
            sample.bindTransforms = new Matrix4x4[boneNames.Count];
            sample.restTransforms = new Matrix4x4[boneNames.Count];

            string rootPath = UnityTypeConverter.GetPath(objContext.gameObject.transform);

            sample.transform = XformExporter.GetLocalTransformMatrix(
                objContext.gameObject.transform,
                scene.UpAxis == Scene.UpAxes.Z,
                new pxr.SdfPath(rootPath).IsRootPrimPath(),
                exportContext.basisTransform);

            int i = 0;

            foreach (string bonePath in boneNames)
            {
                if (string.IsNullOrEmpty(bonePath))
                {
                    sample.joints[i] = "";
                    i++;
                    continue;
                }
                var bone = exportContext.pathToBone[bonePath];
                if (bonePath == rootPath)
                {
                    sample.joints[i] = "/";
                }
                else
                {
                    sample.joints[i] = bonePath.Replace(rootPath + "/", "");
                }

                // TODO: When the bone bind transform contains the geomBindTransform from USD import, it
                // will be mixed into each bone. This transform should be saved in some way and removed
                // when exported as a skeleton.
                sample.bindTransforms[i] = exportContext.bindPoses[bone].inverse;
                sample.restTransforms[i] = XformExporter.GetLocalTransformMatrix(
                    bone, false, false, exportContext.basisTransform);

                if (exportContext.basisTransform == BasisTransformation.SlowAndSafe)
                {
                    sample.bindTransforms[i] = UnityTypeConverter.ChangeBasis(sample.bindTransforms[i]);
                    // The restTransforms will get a change of basis from GetLocalTransformMatrix().
                }

                i++;
            }
            scene.Write(objContext.path, sample);

            // Stop Skeleton from rendering bones in usdview by default.
            var im = new pxr.UsdGeomImageable(scene.GetPrimAtPath(objContext.path));

            im.CreatePurposeAttr().Set(pxr.UsdGeomTokens.guide);
        }
Ejemplo n.º 3
0
    static void CreateExportPlan(GameObject go,
                                 SampleBase sample,
                                 ExportFunction exportFunc,
                                 ExportContext context,
                                 string pathSuffix = null,
                                 object data = null,
                                 bool insertFirst = true) {
      // This is an exportable object.
      Transform expRoot = context.exportRoot;
      string path = UnityTypeConverter.GetPath(go.transform, expRoot);
      if (!string.IsNullOrEmpty(pathSuffix)) {
        path += pathSuffix;
      }
      if (!context.plans.ContainsKey(go)) {
        context.plans.Add(go, new ExportPlan());
      }

      var exp = new Exporter { exportFunc = exportFunc, sample = sample, path = path, data = data };
      if (insertFirst) {
        context.plans[go].exporters.Insert(0, exp);
      } else {
        context.plans[go].exporters.Add(exp);
      }

      // Include the parent xform hierarchy.
      // Note that the parent hierarchy is memoised, so despite looking expensive, the time
      // complexity is linear.
      Transform xf = go.transform.parent;
      if (xf != context.exportRoot && !context.plans.ContainsKey(xf.gameObject)) {
        // Since all GameObjects have a Transform, export all un-exported parents as transform.
        CreateExportPlan(xf.gameObject, CreateSample<XformSample>(context), XformExporter.ExportXform, context);
        CreateExportPlan(xf.gameObject, CreateSample<XformSample>(context), NativeExporter.ExportObject, context, insertFirst: false);
      }
    }
Ejemplo n.º 4
0
    static void InitExportableObjects(GameObject go,
                                      ExportContext context) {
      var smr = go.GetComponent<SkinnedMeshRenderer>();
      var mr = go.GetComponent<MeshRenderer>();
      var mf = go.GetComponent<MeshFilter>();
      var cam = go.GetComponent<Camera>();
      Transform expRoot = context.exportRoot;

      var tmpPath = new pxr.SdfPath(UnityTypeConverter.GetPath(go.transform, expRoot));
      while (!tmpPath.IsRootPrimPath()) {
        tmpPath = tmpPath.GetParentPath();
      }

      // TODO: What if this path is in use?
      string materialBasePath = tmpPath.ToString() + "/Materials/";

      // Ensure the "Materials" prim is defined with a valid prim type.
      context.scene.Write(materialBasePath.TrimEnd('/'), new ScopeSample());

      if (smr != null) {
        foreach (var mat in smr.sharedMaterials) {
          if (!context.matMap.ContainsKey(mat)) {
            string usdPath = materialBasePath + pxr.UsdCs.TfMakeValidIdentifier(mat.name + "_" + mat.GetInstanceID().ToString());
            context.matMap.Add(mat, usdPath);
          }
        }
        CreateExportPlan(go, CreateSample<MeshSample>(context), MeshExporter.ExportSkinnedMesh, context);
        CreateExportPlan(go, CreateSample<MeshSample>(context), NativeExporter.ExportObject, context, insertFirst: false);
        if (smr.rootBone == null) {
          Debug.LogWarning("No root bone at: " + UnityTypeConverter.GetPath(go.transform, expRoot));
        } else if (smr.bones == null || smr.bones.Length == 0) {
          Debug.LogWarning("No bones at: " + UnityTypeConverter.GetPath(go.transform, expRoot));
        } else {
          // Each mesh in a model may have a different root bone, which now must be merged into a
          // single skeleton for export to USD.
          try {
            MergeBonesSimple(smr.transform, smr.rootBone, smr.bones, smr.sharedMesh.bindposes, context);
          } catch (Exception ex) {
            Debug.LogException(new Exception("Failed to merge bones for " + UnityTypeConverter.GetPath(smr.transform), ex));
          }
        }
      } else if (mf != null && mr != null) {
        foreach (var mat in mr.sharedMaterials) {
          if (mat == null) {
            continue;
          }
          if (!context.matMap.ContainsKey(mat)) {
            string usdPath = materialBasePath + pxr.UsdCs.TfMakeValidIdentifier(mat.name + "_" + mat.GetInstanceID().ToString());
            context.matMap.Add(mat, usdPath);
          }
        }
        CreateExportPlan(go, CreateSample<MeshSample>(context), MeshExporter.ExportMesh, context);
        CreateExportPlan(go, CreateSample<MeshSample>(context), NativeExporter.ExportObject, context, insertFirst: false);
      } else if (cam) {
        CreateExportPlan(go, CreateSample<CameraSample>(context), CameraExporter.ExportCamera, context);
        CreateExportPlan(go, CreateSample<CameraSample>(context), NativeExporter.ExportObject, context, insertFirst: false);
      }
    }
Ejemplo n.º 5
0
        public static void Export(GameObject root,
                                  ExportContext context,
                                  bool zeroRootTransform)
        {
            // Remove parent transform effects while exporting.
            // This must be restored before returning from this function.
            var parent = root.transform.parent;

            if (zeroRootTransform)
            {
                root.transform.SetParent(null, worldPositionStays: false);
            }

            // Also zero out and restore local rotations on the root.
            var localPos   = root.transform.localPosition;
            var localRot   = root.transform.localRotation;
            var localScale = root.transform.localScale;

            if (zeroRootTransform)
            {
                root.transform.localPosition = Vector3.zero;
                root.transform.localRotation = Quaternion.identity;
                root.transform.localScale    = Vector3.one;
            }

            // Scale overall scene for export (e.g. USDZ export needs scale 100)
            root.transform.localScale *= context.scale;

            UnityEngine.Profiling.Profiler.BeginSample("USD: Export");
            try
            {
                ExportImpl(root, context);
                var path = new pxr.SdfPath(UnityTypeConverter.GetPath(root.transform));
                var prim = context.scene.Stage.GetPrimAtPath(path);
                if (prim)
                {
                    context.scene.Stage.SetDefaultPrim(prim);
                }
            }
            finally
            {
                if (zeroRootTransform)
                {
                    root.transform.localPosition = localPos;
                    root.transform.localRotation = localRot;
                    root.transform.localScale    = localScale;
                    root.transform.SetParent(parent, worldPositionStays: false);
                }
                else
                {
                    root.transform.localScale = localScale;
                }

                UnityEngine.Profiling.Profiler.EndSample();
            }
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Attempts to build a valid per-vertex UV set from the given object. If the type T is not
        /// the held type of the object "uv" argument, null is returned. If there is an error such
        /// that the type is correct, but the uv values are somehow incompatible, error messages
        /// will be generated and an empty array will be returned.
        /// </summary>
        /// <returns>
        /// An array of size > 0 on succes, an array of size 0 on failure, or null if the given object
        /// is not of the desired type T.
        /// </returns>
        private static T[] TryGetUVSet <T>(object uv,
                                           int[] uvIndices,
                                           int[] faceVertexCounts,
                                           int[] faceVertexIndices,
                                           int vertexCount,
                                           GameObject go)
        {
            if (uv.GetType() != typeof(T[]))
            {
                return(null);
            }

            var uvVec = (T[])uv;

            if (uvVec.Length == 0)
            {
                return(uvVec);
            }

            // Unroll UV indices if specified.
            if (uvIndices != null && uvIndices.Length > 0)
            {
                var newUvs = new T[uvIndices.Length];
                for (int i = 0; i < uvIndices.Length; i++)
                {
                    newUvs[i] = uvVec[uvIndices[i]];
                }

                uvVec = newUvs;
            }

            // If there are more UVs than verts, the UVs must be face varying, e.g. each vertex for
            // each face has a unique UV value. These values must be collapsed such that verts shared
            // between faces also share a single UV value.
            if (uvVec.Length > vertexCount)
            {
                uvVec = UnrollFaceVarying(vertexCount, uvVec, faceVertexCounts, faceVertexIndices);
                if (uvVec == null)
                {
                    return(new T[0]);
                }

                Debug.Assert(uvVec.Length == vertexCount);
            }

            // If there are fewer values, these must be "varying" / one value per face.
            // This is not yet supported.
            if (uvVec.Length < vertexCount)
            {
                Debug.LogWarning("Mesh UVs are constant or uniform, ignored "
                                 + UnityTypeConverter.GetPath(go.transform));
                return(new T[0]);
            }

            return(uvVec);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Loads or unloads the given payload object. Throws an exception if game object deos not have
        /// a UsdPrimSource behaviour.
        /// </summary>
        public void SetPayloadState(GameObject go, bool isLoaded)
        {
            var primSrc = go.GetComponent <UsdPrimSource>();

            if (!primSrc)
            {
                throw new Exception("UsdPrimSource not found: " + UnityTypeConverter.GetPath(go.transform));
            }

            var usdPrimPath = primSrc.m_usdPrimPath;

            InitUsd.Initialize();
            var scene = GetScene();

            if (scene == null)
            {
                throw new Exception("Failed to open: " + usdFullPath);
            }

            var prim = scene.GetPrimAtPath(usdPrimPath);

            if (prim == null || !prim)
            {
                throw new Exception("Prim not found: " + usdPrimPath);
            }

            foreach (var child in go.transform.GetComponentsInChildren <UsdPrimSource>().ToList())
            {
                if (!child || child.gameObject == go)
                {
                    continue;
                }

                GameObject.DestroyImmediate(child.gameObject);
            }

            if (!isLoaded)
            {
                prim.Unload();
                return;
            }
            else
            {
                prim.Load();
            }

            SceneImportOptions importOptions = new SceneImportOptions();

            this.StateToOptions(ref importOptions);
            importOptions.usdRootPath = prim.GetPath();
            SceneImporter.ImportUsd(go, scene, new PrimMap(), true, importOptions);
        }
        public static void ExportSkinnedMesh(ObjectContext objContext, ExportContext exportContext)
        {
            var smr = objContext.gameObject.GetComponent <SkinnedMeshRenderer>();

            UnityEngine.Profiling.Profiler.BeginSample("USD: Skinned Mesh");
            ExportMesh(objContext,
                       exportContext,
                       smr.sharedMesh,
                       smr.sharedMaterial,
                       smr.sharedMaterials,
                       exportMeshPose: exportContext.scene.Time == null);
            UnityEngine.Profiling.Profiler.EndSample();

            if (exportContext.scene.Time != null)
            {
                return;
            }

            // Note that the baked mesh no longer has the bone weights, so here we switch back to the
            // shared SkinnedMeshRenderer mesh.
            Transform rootBone = null;

            if (smr.rootBone != null && !exportContext.boneToRoot.TryGetValue(smr.rootBone, out rootBone))
            {
                Debug.LogWarning("Root bone not found in export context for " +
                                 UnityTypeConverter.GetPath(smr.rootBone));
                return;
            }

            UnityEngine.Profiling.Profiler.BeginSample("USD: Skinning Weights");

            // Skeleton path is stored in additionalData via the SceneExporter SyncExportContext(). It
            // would be nice to formalize this, rather than passing it as blind data.
            var skeletonPath = (string)objContext.additionalData;

            ExportSkelWeights(exportContext.scene,
                              objContext.path,
                              smr.sharedMesh,
                              rootBone,
                              smr.bones,
                              skeletonPath);
            UnityEngine.Profiling.Profiler.EndSample();
        }
Ejemplo n.º 9
0
        bool InitExportableParents(GameObject go)
        {
            if (m_primMap.ContainsKey(go))
            {
                // Stop processing parents, this keeps the performance of the traversal linear.
                return(false);
            }

            // Any object we add will only be exported as an Xform.
            string     path   = UnityTypeConverter.GetPath(go.transform);
            SampleBase sample = new XformSample();

            m_primMap.Add(go, new ExportPlan {
                path = path, sample = sample, exportFunc = ExportXform
            });
            Debug.Log(path + " " + sample.GetType().Name);

            // Continue processing parents.
            return(true);
        }
Ejemplo n.º 10
0
        public static void ExportSkinnedMesh(ObjectContext objContext, ExportContext exportContext)
        {
            var smr = objContext.gameObject.GetComponent <SkinnedMeshRenderer>();

            UnityEngine.Profiling.Profiler.BeginSample("USD: Skinned Mesh");
            ExportMesh(objContext,
                       exportContext,
                       smr.sharedMesh,
                       smr.sharedMaterial,
                       smr.sharedMaterials,
                       exportMeshPose: exportContext.scene.Time == null);
            UnityEngine.Profiling.Profiler.EndSample();

            if (exportContext.scene.Time != null)
            {
                return;
            }

            // Note that the baked mesh no longer has the bone weights, so here we switch back to the
            // shared SkinnedMeshRenderer mesh.
            Transform rootBone = null;

            if (smr.rootBone != null && !exportContext.boneToRoot.TryGetValue(smr.rootBone, out rootBone))
            {
                Debug.LogWarning("Root bone not found in export context for " +
                                 UnityTypeConverter.GetPath(smr.rootBone));
                return;
            }

            UnityEngine.Profiling.Profiler.BeginSample("USD: Skinning Weights");
            ExportSkelWeights(exportContext.scene,
                              objContext.path,
                              smr.sharedMesh,
                              rootBone,
                              smr.bones);
            UnityEngine.Profiling.Profiler.EndSample();
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Applies the contents of this USD file to a foreign root object.
        /// </summary>
        /// <remarks>
        /// The idea here is that one may have many animation clips, but only a single GameObject in
        /// the Unity scenegraph.
        /// </remarks>
        public void SetTime(double time, UsdAsset foreignRoot, bool saveMeshUpdates)
        {
            var scene = GetScene();

            if (scene == null)
            {
                Debug.LogWarning("Null scene from GetScene() at " + UnityTypeConverter.GetPath(transform));
                return;
            }

            // Careful not to update any local members here, if this data is driven from a prefab, we
            // dont want those changes to be baked back into the asset.
            time += foreignRoot.m_usdTimeOffset;
            float usdTime = (float)(scene.StartTime + time * scene.Stage.GetTimeCodesPerSecond());

            if (usdTime > scene.EndTime)
            {
                return;
            }
            if (usdTime < scene.StartTime)
            {
                return;
            }

            scene.Time = usdTime;

            var options = new SceneImportOptions();

            foreignRoot.StateToOptions(ref options);

            PrepOptionsForTimeChange(ref options);

            if (foreignRoot.m_lastPrimMap == null)
            {
                foreignRoot.m_lastPrimMap = new PrimMap();
                options.importHierarchy   = true;
            }

            if (m_usdVariabilityCache)
            {
                if (m_lastAccessMask == null)
                {
                    m_lastAccessMask             = new AccessMask();
                    scene.IsPopulatingAccessMask = true;
                }
            }
            else
            {
                m_lastAccessMask = null;
            }

            if (m_debugPrintVariabilityCache && m_lastAccessMask != null &&
                !scene.IsPopulatingAccessMask)
            {
                var sb = new System.Text.StringBuilder();
                foreach (var kvp in m_lastAccessMask.Included)
                {
                    sb.AppendLine(kvp.Key);
                    foreach (var member in kvp.Value)
                    {
                        sb.AppendLine("  ." + member.Name);
                    }
                    sb.AppendLine();
                }
                Debug.Log(sb.ToString());
            }

            scene.AccessMask = m_lastAccessMask;
            SceneImporter.ImportUsd(foreignRoot.gameObject,
                                    scene,
                                    foreignRoot.m_lastPrimMap,
                                    options);
            scene.AccessMask = null;

            if (m_lastAccessMask != null)
            {
                scene.IsPopulatingAccessMask = false;
            }
        }
        static void ExportSkelWeights(Scene scene,
                                      string path,
                                      Mesh unityMesh,
                                      Transform rootBone,
                                      Transform[] bones,
                                      string skeletonPath)
        {
            var sample = new SkelBindingSample();

            sample.geomBindTransform.value = Matrix4x4.identity;
            sample.joints = new string[bones.Length];
            if (!string.IsNullOrEmpty(skeletonPath))
            {
                sample.skeleton.targetPaths = new string[] { skeletonPath };
            }

            int b        = 0;
            var rootPath = UnityTypeConverter.GetPath(rootBone);

            foreach (Transform bone in bones)
            {
                var bonePath = UnityTypeConverter.GetPath(bone);
                if (bonePath == rootPath)
                {
                    sample.joints[b++] = "/";
                }
                else
                {
                    sample.joints[b++] = bonePath.Replace(rootPath + "/", "");
                }
            }

            int i = 0;
            int w = 0;

            b = 0;
#if UNITY_2019
            var  bonesPerVertex   = unityMesh.GetBonesPerVertex();
            var  unityBoneWeights = unityMesh.GetAllBoneWeights();
            byte maxWeightCount   = 0;
            foreach (var c in bonesPerVertex)
            {
                maxWeightCount = maxWeightCount > c ? maxWeightCount : c;
            }

            sample.jointIndices.value         = new int[bonesPerVertex.Length * maxWeightCount];
            sample.jointIndices.elementSize   = maxWeightCount;
            sample.jointIndices.interpolation = PrimvarInterpolation.Vertex;

            sample.jointWeights.value         = new float[bonesPerVertex.Length * maxWeightCount];
            sample.jointWeights.elementSize   = maxWeightCount;
            sample.jointWeights.interpolation = PrimvarInterpolation.Vertex;

            foreach (var weightCount in bonesPerVertex)
            {
                for (int j = 0; j < weightCount; j++)
                {
                    var bw = unityBoneWeights[b++];
                    sample.jointIndices.value[i++] = bw.boneIndex;
                    sample.jointWeights.value[w++] = bw.weight;
                }

                // Unity allows a variable number of weights per bone, but we've made the array square,
                // which means we may need to skip a few indicies, if this vert doesn't use the max number
                // of weights.
                i += maxWeightCount - weightCount;
                w += maxWeightCount - weightCount;
            }
#else
            var unityBoneWeights = unityMesh.boneWeights;

            if (unityBoneWeights.Length == 0)
            {
                Debug.LogWarning("Found zero bone weights at: " + path);
                return;
            }

            sample.jointIndices.value         = new int[unityBoneWeights.Length * 4];
            sample.jointIndices.elementSize   = 4;
            sample.jointIndices.interpolation = PrimvarInterpolation.Vertex;

            sample.jointWeights.value         = new float[unityBoneWeights.Length * 4];
            sample.jointWeights.elementSize   = 4;
            sample.jointWeights.interpolation = PrimvarInterpolation.Vertex;

            foreach (var bone in unityBoneWeights)
            {
                sample.jointIndices.value[i++] = bone.boneIndex0;
                sample.jointIndices.value[i++] = bone.boneIndex1;
                sample.jointIndices.value[i++] = bone.boneIndex2;
                sample.jointIndices.value[i++] = bone.boneIndex3;
                sample.jointWeights.value[w++] = bone.weight0;
                sample.jointWeights.value[w++] = bone.weight1;
                sample.jointWeights.value[w++] = bone.weight2;
                sample.jointWeights.value[w++] = bone.weight3;
            }
#endif
            scene.Write(path, sample);
        }
Ejemplo n.º 13
0
        static Transform MergeBonesBelowAnimator(Transform animator, ExportContext context)
        {
            var       toRemove   = new Dictionary <Transform, Transform>();
            Transform commonRoot = null;

            foreach (var sourceAndRoot in context.meshToSkelRoot)
            {
                var meshXf       = sourceAndRoot.Key;
                var meshRootBone = sourceAndRoot.Value;
                if (!meshRootBone.IsChildOf(animator))
                {
                    continue;
                }

                toRemove.Add(meshXf, meshRootBone);
                if (commonRoot == null)
                {
                    // We use the parent because the root bone is part of the skeleton and we're establishing
                    // the skeleton root here. If the root bone is used as the skeleton root, its transform
                    // will get applied twice after export to USD: once for the UsdPrim which is the skeleton
                    // root and once for the bone which is in the skeleton itself. The root bone could be
                    // excluded from the skeleton, but this seems simpler.
                    commonRoot = meshRootBone.parent;
                }
                else if (meshRootBone.IsChildOf(commonRoot))
                {
                    // Nothing to do.
                }
                else if (commonRoot.IsChildOf(meshRootBone))
                {
                    // The new root is a parent of the current common root, use it as the root instead.
                    commonRoot = meshRootBone.parent;
                }
                else
                {
                    // We have an animator which is a common parent of two disjoint skeletons, this is not
                    // desirable because it requires that the animator be the common root, however this
                    // root will be tagged as a guide, which will cuase the geometry not to render, which
                    // will be confusing. Another option would be to construct a new common parent in USD,
                    // but this will cause the asset namespace to change, which is almost never a good idea.
                    commonRoot = animator;
                }
            }

            if (toRemove.Count == 0)
            {
                return(null);
            }

            // At this point, some number of root bones have been aggregated under some potentially new
            // common root. Next, we need to merge all these root bones and preserve the requirement that
            // the bones are in "parent first" order.
            var allBones = new List <Transform>();

            foreach (var kvp in toRemove)
            {
                Transform curMeshXf = kvp.Key;
                Transform rootBone  = kvp.Value;

                allBones.AddRange(context.meshToBones[curMeshXf]);

                // Downstream code will have a root bone and need to know how to make bone paths relative
                // to the new, arbitrary, common root which we have chosen.
                context.boneToRoot[rootBone] = commonRoot;

                context.meshToSkelRoot.Remove(curMeshXf);
                context.meshToBones.Remove(curMeshXf);
            }

            // Maintain a sorted list of bone names to ensure "parent first" ordering for UsdSkel.
            var allNames = allBones.Select(boneXf => UnityTypeConverter.GetPath(boneXf))
                           .OrderBy(str => str)
                           .Distinct()
                           .ToList();

            context.skelSortedMap[commonRoot] = allNames;

            return(commonRoot);
        }
Ejemplo n.º 14
0
        private static void BuildMesh_(string path,
                                       MeshSample usdMesh,
                                       Mesh unityMesh,
                                       GeometrySubsets geomSubsets,
                                       GameObject go,
                                       Renderer renderer,
                                       SceneImportOptions options)
        {
            // TODO: Because this method operates on a GameObject, it must be single threaded. For this
            // reason, it should be extremely light weight. All computation should be completed prior to
            // this step, allowing heavy computations to happen in parallel. This is not currently the
            // case, triangulation and change of basis are non-trivial operations. Computing the mesh
            // bounds, normals and tangents should similarly be moved out of this function and should not
            // rely on the UnityEngine.Mesh API.

            Material mat = renderer.sharedMaterial;
            bool     changeHandedness = options.changeHandedness == BasisTransformation.SlowAndSafe;

            //
            // Points.
            //

            if (options.meshOptions.points == ImportMode.Import && usdMesh.points != null)
            {
                if (changeHandedness)
                {
                    for (int i = 0; i < usdMesh.points.Length; i++)
                    {
                        usdMesh.points[i] = UnityTypeConverter.ChangeBasis(usdMesh.points[i]);
                    }
                }

                if (usdMesh.faceVertexIndices != null)
                {
                    // Annoyingly, there is a circular dependency between vertices and triangles, which makes
                    // it impossible to have a fixed update order in this function. As a result, we must clear
                    // the triangles before setting the points, to break that dependency.
                    unityMesh.SetTriangles(new int[0] {
                    }, 0);
                }
                unityMesh.vertices = usdMesh.points;
            }

            //
            // Purpose.
            //

            // Deactivate non-geometry prims (e.g. guides, render, etc).
            if (usdMesh.purpose != Purpose.Default)
            {
                go.SetActive(false);
            }

            //
            // Mesh Topology.
            //

            // TODO: indices should not be accessed if topology is not requested, however it may be
            // needed for facevarying primvars; that special case should throw a warning, rather than
            // reading the value.
            int[] originalIndices = new int[usdMesh.faceVertexIndices == null
                                      ? 0
                                      : usdMesh.faceVertexIndices.Length];
            // Optimization: only do this when there are face varying primvars.
            if (usdMesh.faceVertexIndices != null)
            {
                Array.Copy(usdMesh.faceVertexIndices, originalIndices, originalIndices.Length);
            }

            if (options.meshOptions.topology == ImportMode.Import && usdMesh.faceVertexIndices != null)
            {
                Profiler.BeginSample("Triangulate Mesh");
                if (options.meshOptions.triangulateMesh)
                {
                    // Triangulate n-gons.
                    // For best performance, triangulate off-line and skip conversion.
                    if (usdMesh.faceVertexIndices == null)
                    {
                        Debug.LogWarning("Mesh had no face indices: " + UnityTypeConverter.GetPath(go.transform));
                        return;
                    }
                    if (usdMesh.faceVertexCounts == null)
                    {
                        Debug.LogWarning("Mesh had no face counts: " + UnityTypeConverter.GetPath(go.transform));
                        return;
                    }
                    var indices = UnityTypeConverter.ToVtArray(usdMesh.faceVertexIndices);
                    var counts  = UnityTypeConverter.ToVtArray(usdMesh.faceVertexCounts);
                    UsdGeomMesh.Triangulate(indices, counts);
                    UnityTypeConverter.FromVtArray(indices, ref usdMesh.faceVertexIndices);
                }
                Profiler.EndSample();

                Profiler.BeginSample("Convert LeftHanded");
                bool isLeftHanded = usdMesh.orientation == Orientation.LeftHanded;
                if (changeHandedness && !isLeftHanded || !changeHandedness && isLeftHanded)
                {
                    // USD is right-handed, so the mesh needs to be flipped.
                    // Unity is left-handed, but that doesn't matter here.
                    for (int i = 0; i < usdMesh.faceVertexIndices.Length; i += 3)
                    {
                        int tmp = usdMesh.faceVertexIndices[i];
                        usdMesh.faceVertexIndices[i]     = usdMesh.faceVertexIndices[i + 1];
                        usdMesh.faceVertexIndices[i + 1] = tmp;
                    }
                }
                Profiler.EndSample();

                if (usdMesh.faceVertexIndices.Length > 65535)
                {
                    unityMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
                }

                Profiler.BeginSample("Breakdown triangles for Mesh Subsets");
                if (geomSubsets.Subsets.Count == 0)
                {
                    unityMesh.triangles = usdMesh.faceVertexIndices;
                }
                else
                {
                    unityMesh.subMeshCount = geomSubsets.Subsets.Count;
                    int subsetIndex = 0;
                    foreach (var kvp in geomSubsets.Subsets)
                    {
                        int[] faceIndices     = kvp.Value;
                        int[] triangleIndices = new int[faceIndices.Length * 3];

                        for (int i = 0; i < faceIndices.Length; i++)
                        {
                            triangleIndices[i * 3 + 0] = usdMesh.faceVertexIndices[faceIndices[i] * 3 + 0];
                            triangleIndices[i * 3 + 1] = usdMesh.faceVertexIndices[faceIndices[i] * 3 + 1];
                            triangleIndices[i * 3 + 2] = usdMesh.faceVertexIndices[faceIndices[i] * 3 + 2];
                        }

                        unityMesh.SetTriangles(triangleIndices, subsetIndex);
                        subsetIndex++;
                    }
                }
                Profiler.EndSample();
            }

            //
            // Extent / Bounds.
            //

            bool hasBounds = usdMesh.extent.size.x > 0 ||
                             usdMesh.extent.size.y > 0 ||
                             usdMesh.extent.size.z > 0;

            if (ShouldImport(options.meshOptions.boundingBox) && hasBounds)
            {
                Profiler.BeginSample("Import Bounds");
                if (changeHandedness)
                {
                    usdMesh.extent.center  = UnityTypeConverter.ChangeBasis(usdMesh.extent.center);
                    usdMesh.extent.extents = UnityTypeConverter.ChangeBasis(usdMesh.extent.extents);
                }
                unityMesh.bounds = usdMesh.extent;
                Profiler.EndSample();
            }
            else if (ShouldCompute(options.meshOptions.boundingBox))
            {
                Profiler.BeginSample("Calculate Bounds");
                unityMesh.RecalculateBounds();
                Profiler.EndSample();
            }

            //
            // Normals.
            //

            if (usdMesh.normals != null && ShouldImport(options.meshOptions.normals))
            {
                Profiler.BeginSample("Import Normals");
                if (changeHandedness)
                {
                    for (int i = 0; i < usdMesh.points.Length; i++)
                    {
                        usdMesh.normals[i] = UnityTypeConverter.ChangeBasis(usdMesh.normals[i]);
                    }
                }
                // If more normals than verts, assume face-varying.
                if (usdMesh.normals.Length > usdMesh.points.Length)
                {
                    usdMesh.normals = UnrollFaceVarying(usdMesh.points.Length, usdMesh.normals, usdMesh.faceVertexCounts, originalIndices);
                }
                unityMesh.normals = usdMesh.normals;
                Profiler.EndSample();
            }
            else if (ShouldCompute(options.meshOptions.normals))
            {
                Profiler.BeginSample("Calculate Normals");
                unityMesh.RecalculateNormals();
                Profiler.EndSample();
            }

            //
            // Tangents.
            //

            if (usdMesh.tangents != null && ShouldImport(options.meshOptions.tangents))
            {
                Profiler.BeginSample("Import Tangents");
                if (changeHandedness)
                {
                    for (int i = 0; i < usdMesh.points.Length; i++)
                    {
                        var w = usdMesh.tangents[i].w;
                        var t = UnityTypeConverter.ChangeBasis(usdMesh.tangents[i]);
                        usdMesh.tangents[i] = new Vector4(t.x, t.y, t.z, w);
                    }
                }
                unityMesh.tangents = usdMesh.tangents;
                Profiler.EndSample();
            }
            else if (ShouldCompute(options.meshOptions.tangents))
            {
                Profiler.BeginSample("Calculate Tangents");
                unityMesh.RecalculateTangents();
                Profiler.EndSample();
            }

            //
            // Display Color.
            //

            if (ShouldImport(options.meshOptions.color) && usdMesh.colors != null && usdMesh.colors.Length > 0)
            {
                Profiler.BeginSample("Import Display Color");
                // NOTE: The following color conversion assumes PlayerSettings.ColorSpace == Linear.
                // For best performance, convert color space to linear off-line and skip conversion.

                if (usdMesh.colors.Length == 1)
                {
                    // Constant color can just be set on the material.
                    if (options.useDisplayColorAsFallbackMaterial && options.materialImportMode != MaterialImportMode.None)
                    {
                        mat = options.materialMap.InstantiateSolidColor(usdMesh.colors[0].gamma);
                    }
                }
                else if (usdMesh.colors.Length == usdMesh.points.Length)
                {
                    // Vertex colors map on to verts.
                    // TODO: move the conversion to C++ and use the color management API.
                    for (int i = 0; i < usdMesh.colors.Length; i++)
                    {
                        usdMesh.colors[i] = usdMesh.colors[i];
                    }
                    unityMesh.colors = usdMesh.colors;
                }
                else if (usdMesh.colors.Length == usdMesh.faceVertexCounts.Length)
                {
                    // Uniform colors, one per face.
                    // Unroll face colors into vertex colors. This is not strictly correct, but it's much faster
                    // than the fully correct solution.
                    var colors = new Color[unityMesh.vertexCount];
                    int idx    = 0;
                    try {
                        for (int faceIndex = 0; faceIndex < usdMesh.colors.Length; faceIndex++)
                        {
                            var faceColor = usdMesh.colors[faceIndex];
                            for (int f = 0; f < usdMesh.faceVertexCounts[faceIndex]; f++)
                            {
                                int vertexInFaceIdx = originalIndices[idx++];
                                colors[vertexInFaceIdx] = faceColor;
                            }
                        }
                        unityMesh.colors = colors;
                    } catch (Exception ex) {
                        Debug.LogException(new Exception("Failed loading uniform/per-face colors at " + path, ex));
                    }
                }
                else if (usdMesh.colors.Length > usdMesh.points.Length)
                {
                    try {
                        usdMesh.colors = UnrollFaceVarying(unityMesh.vertexCount,
                                                           usdMesh.colors,
                                                           usdMesh.faceVertexCounts,
                                                           originalIndices);
                        for (int i = 0; i < usdMesh.colors.Length; i++)
                        {
                            usdMesh.colors[i] = usdMesh.colors[i];
                        }
                        unityMesh.colors = usdMesh.colors;
                    } catch (Exception ex) {
                        Debug.LogException(
                            new Exception("Error unrolling Face-Varying colors at <" + path + ">", ex));
                    }
                }
                else
                {
                    Debug.LogWarning("Uniform (color per face) display color not supported");
                }
                Profiler.EndSample();
            } // should import color

            //
            // UVs / Texture Coordinates.
            //

            // TODO: these should also be driven by the UV privmars required by the bound shader.
            Profiler.BeginSample("Import UV Sets");
            ImportUv(path, unityMesh, 0, usdMesh.st, usdMesh.indices, usdMesh.faceVertexCounts, originalIndices, options.meshOptions.texcoord0, go);
            ImportUv(path, unityMesh, 0, usdMesh.uv, null, usdMesh.faceVertexCounts, originalIndices, options.meshOptions.texcoord0, go);
            ImportUv(path, unityMesh, 1, usdMesh.uv2, null, usdMesh.faceVertexCounts, originalIndices, options.meshOptions.texcoord1, go);
            ImportUv(path, unityMesh, 2, usdMesh.uv3, null, usdMesh.faceVertexCounts, originalIndices, options.meshOptions.texcoord2, go);
            ImportUv(path, unityMesh, 3, usdMesh.uv4, null, usdMesh.faceVertexCounts, originalIndices, options.meshOptions.texcoord3, go);
            Profiler.EndSample();

            Profiler.BeginSample("Request Material Bindings");

            //
            // Materials.
            //

            if (options.materialImportMode != MaterialImportMode.None)
            {
                if (mat == null)
                {
                    mat = options.materialMap.InstantiateSolidColor(Color.white);
                }

                if (unityMesh.subMeshCount == 1)
                {
                    renderer.sharedMaterial = mat;
                    if (options.ShouldBindMaterials)
                    {
                        options.materialMap.RequestBinding(path,
                                                           (scene, boundMat, primvars) => BindMat(scene, unityMesh, boundMat, renderer, path, primvars, usdMesh.faceVertexCounts, originalIndices));
                    }
                }
                else
                {
                    var mats = new Material[unityMesh.subMeshCount];
                    for (int i = 0; i < mats.Length; i++)
                    {
                        mats[i] = mat;
                    }
                    renderer.sharedMaterials = mats;
                    if (options.ShouldBindMaterials)
                    {
                        Debug.Assert(geomSubsets.Subsets.Count == unityMesh.subMeshCount);
                        var subIndex = 0;
                        foreach (var kvp in geomSubsets.Subsets)
                        {
                            int idx = subIndex++;
                            options.materialMap.RequestBinding(kvp.Key,
                                                               (scene, boundMat, primvars) => BindMat(scene, unityMesh, boundMat, renderer, idx, path, primvars, usdMesh.faceVertexCounts, originalIndices));
                        }
                    }
                }
            }
            Profiler.EndSample();

            //
            // Lightmap UV Unwrapping.
            //

#if UNITY_EDITOR
            if (options.meshOptions.generateLightmapUVs)
            {
#if !UNITY_2018_3_OR_NEWER
                if (unityMesh.indexFormat == UnityEngine.Rendering.IndexFormat.UInt32)
                {
                    Debug.LogWarning("Skipping prim " + path + " due to large IndexFormat (UInt32) bug in older vesrsions of Unity");
                    return;
                }
#endif
                Profiler.BeginSample("Unwrap Lightmap UVs");
                var unwrapSettings = new UnityEditor.UnwrapParam();

                unwrapSettings.angleError = options.meshOptions.unwrapAngleError;
                unwrapSettings.areaError  = options.meshOptions.unwrapAngleError;
                unwrapSettings.hardAngle  = options.meshOptions.unwrapHardAngle;

                // Convert pixels to unitless UV space, which is what unwrapSettings uses internally.
                unwrapSettings.packMargin = options.meshOptions.unwrapPackMargin / 1024.0f;

                UnityEditor.Unwrapping.GenerateSecondaryUVSet(unityMesh, unwrapSettings);
                Profiler.EndSample();
            }
#else
            if (options.meshOptions.generateLightmapUVs)
            {
                Debug.LogWarning("Lightmap UVs were requested to be generated, but cannot be generated outside of the editor");
            }
#endif
        }
Ejemplo n.º 15
0
    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;
        }
      }
    }
Ejemplo n.º 16
0
        void OnEnable()
        {
            InitUsd.Initialize();

            if (string.IsNullOrEmpty(m_usdMeshPath))
            {
                m_usdMeshPath = UnityTypeConverter.GetPath(transform);
            }

            var    scene   = GetScene();
            var    binding = ReadUsdWeights(scene);
            string skelRootPath;
            var    skeleton = ReadUsdSkeleton(scene, out skelRootPath);

            if (binding == null)
            {
                binding = new SkelBindingSample();
            }

            var mesh = GetComponent <SkinnedMeshRenderer>().sharedMesh;

            // Process classic four-bone weights first.
            var sb = new System.Text.StringBuilder();

#if UNITY_2019_1_OR_NEWER
            var bonesPerVert   = mesh.GetBonesPerVertex();
            int weightsPerBone = 0;
            foreach (int count in bonesPerVert)
            {
                weightsPerBone = weightsPerBone > count ? weightsPerBone : count;
            }
            var boneWeights = mesh.GetAllBoneWeights();
            sb.AppendLine("Many-bone indices: (" + boneWeights.Length + " * 4)");
            int bone = 0;
            int bi   = 0;
            int wi   = 0;
            foreach (var weight in boneWeights)
            {
                if (wi == 0)
                {
                    sb.Append("i: " + bone + " [");
                }

                sb.Append(weight.boneIndex + GetUsdBoneData(bi, wi, binding.jointIndices) + ",");

                wi++;
                if (wi == weightsPerBone)
                {
                    sb.Append("]\n");
                    bi++;
                    wi = 0;
                }

                if (bonesPerVert[bi] != weightsPerBone)
                {
                    // TODO: Unity supports a variable number of weights per bone, but USD does not.
                    // Therefore, the number of weights may be greater in USD than in Unity. Currently
                    // the way this works does not correctly handle that case.
                    Debug.LogWarning("Unity bone count issue, see code comment for details.");
                }

                bone++;
            }
            Debug.Log(sb.ToString());

            bone = 0;
            bi   = 0;
            wi   = 0;
            sb   = new System.Text.StringBuilder();
            sb.AppendLine("Many-bone weights: (" + boneWeights.Length + " * 4)");
            foreach (var weight in boneWeights)
            {
                if (wi == 0)
                {
                    sb.Append("i: " + bone + " [");
                }
                sb.Append(weight.weight + GetUsdBoneData(bi, wi, binding.jointWeights) + ",");

                wi++;
                if (wi == weightsPerBone)
                {
                    sb.Append("]\n");
                    bi++;
                    wi = 0;
                }
                bone++;
            }
            Debug.Log(sb.ToString());
#else
            sb.AppendLine("Legacy 4-bone indices: (" + mesh.boneWeights.Length + " * 4)");
            int bone = 0;
            foreach (var weight in mesh.boneWeights)
            {
                sb.Append("[");
                sb.Append(weight.boneIndex0 + GetUsdBoneData(bone, 0, binding.jointIndices) + ",");
                sb.Append(weight.boneIndex1 + GetUsdBoneData(bone, 1, binding.jointIndices) + ",");
                sb.Append(weight.boneIndex2 + GetUsdBoneData(bone, 2, binding.jointIndices) + ",");
                sb.Append(weight.boneIndex3 + GetUsdBoneData(bone, 3, binding.jointIndices) + "]\n");
                bone++;
            }
            Debug.Log(sb.ToString());

            bone = 0;
            sb   = new System.Text.StringBuilder();
            sb.AppendLine("Legacy 4-bone weights: (" + mesh.boneWeights.Length + " * 4)");
            foreach (var weight in mesh.boneWeights)
            {
                sb.Append("[");
                sb.Append(weight.weight0 + GetUsdBoneData(bone, 0, binding.jointWeights) + ",");
                sb.Append(weight.weight1 + GetUsdBoneData(bone, 1, binding.jointWeights) + ",");
                sb.Append(weight.weight2 + GetUsdBoneData(bone, 2, binding.jointWeights) + ",");
                sb.Append(weight.weight3 + GetUsdBoneData(bone, 3, binding.jointWeights) + "]\n");
                bone++;
            }
            Debug.Log(sb.ToString());
#endif


            sb = new System.Text.StringBuilder();
            var bones    = GetComponent <SkinnedMeshRenderer>().bones;
            var rootBone = GetComponent <SkinnedMeshRenderer>().rootBone;
            var root     = UnityTypeConverter.GetPath(rootBone);
            sb.AppendLine("Bones: (" + bones.Length + ")");
            sb.AppendLine("Root Bone: " + root);
            int i = 0;
            foreach (var boneXf in bones)
            {
                sb.AppendLine(UnityTypeConverter.GetPath(boneXf));
                if (binding.joints != null)
                {
                    sb.AppendLine(root + "\\" + binding.joints[i++] + "\n");
                }
            }
            Debug.Log(sb.ToString());

            sb = new System.Text.StringBuilder();
            sb.AppendLine("Bind Transforms: (" + mesh.bindposes.Length + ")");
            i = -1;
            var options = new SceneImportOptions();
            options.changeHandedness = m_basisTransform;
            foreach (var boneXf in bones)
            {
                i++;
                var bindPose = mesh.bindposes[i];
                var bonePath = UnityTypeConverter.GetPath(boneXf);
                sb.AppendLine("Pose[" + i + "] " + bonePath);
                sb.AppendLine(bindPose.ToString());

                if (skeleton.bindTransforms != null)
                {
                    if (string.IsNullOrEmpty(skelRootPath))
                    {
                        continue;
                    }

                    bonePath = bonePath.Substring(skelRootPath.Length);
                    bonePath = bonePath.TrimStart('/');
                    foreach (var joint in skeleton.joints)
                    {
                        if (joint == bonePath)
                        {
                            var usdMat = skeleton.bindTransforms[i];
                            XformImporter.ImportXform(ref usdMat, options);
                            sb.AppendLine(usdMat.ToString() + "\n");
                            bonePath = null;
                            break;
                        }
                    }

                    if (string.IsNullOrEmpty(bonePath))
                    {
                        continue;
                    }
                    sb.Append("Bone not found in USD: " + bonePath + "\n\n");
                }
            }
            Debug.Log(sb.ToString());
        }
Ejemplo n.º 17
0
        public static void ExportSkelAnimation(ObjectContext objContext, ExportContext exportContext)
        {
            var scene     = exportContext.scene;
            var sample    = (SkelAnimationSample)objContext.sample;
            var go        = objContext.gameObject;
            var boneNames = exportContext.skelSortedMap[go.transform];
            var skelRoot  = go.transform;

            sample.joints = new string[boneNames.Count];

            var worldXf    = new Matrix4x4[boneNames.Count];
            var worldXfInv = new Matrix4x4[boneNames.Count];

            string rootPath = UnityTypeConverter.GetPath(go.transform);

            var basisChange = Matrix4x4.identity;

            basisChange[2, 2] = -1;

            for (int i = 0; i < boneNames.Count; i++)
            {
                var bonePath = boneNames[i];
                if (!exportContext.pathToBone.ContainsKey(bonePath))
                {
                    sample.joints[i] = "";
                    continue;
                }

                var bone = exportContext.pathToBone[bonePath];
                sample.joints[i] = bonePath.Replace(rootPath + "/", "");

                worldXf[i] = bone.localToWorldMatrix;
                if (exportContext.basisTransform == BasisTransformation.SlowAndSafe)
                {
                    worldXf[i] = UnityTypeConverter.ChangeBasis(worldXf[i]);
                }

                worldXfInv[i] = worldXf[i].inverse;
            }

            var rootXf = skelRoot.localToWorldMatrix.inverse;

            if (exportContext.basisTransform == BasisTransformation.SlowAndSafe)
            {
                rootXf = UnityTypeConverter.ChangeBasis(rootXf);
            }

            var skelWorldTransform = UnityTypeConverter.ToGfMatrix(rootXf);

            pxr.VtMatrix4dArray vtJointsLS    = new pxr.VtMatrix4dArray((uint)boneNames.Count);
            pxr.VtMatrix4dArray vtJointsWS    = UnityTypeConverter.ToVtArray(worldXf);
            pxr.VtMatrix4dArray vtJointsWSInv = UnityTypeConverter.ToVtArray(worldXfInv);

            var translations = new pxr.VtVec3fArray();
            var rotations    = new pxr.VtQuatfArray();

            sample.scales = new pxr.VtVec3hArray();

            var topo = new pxr.UsdSkelTopology(UnityTypeConverter.ToVtArray(sample.joints));

            pxr.UsdCs.UsdSkelComputeJointLocalTransforms(topo,
                                                         vtJointsWS,
                                                         vtJointsWSInv,
                                                         vtJointsLS,
                                                         skelWorldTransform);

            pxr.UsdCs.UsdSkelDecomposeTransforms(
                vtJointsLS,
                translations,
                rotations,
                sample.scales);
            sample.translations = UnityTypeConverter.FromVtArray(translations);
            sample.rotations    = UnityTypeConverter.FromVtArray(rotations);

            scene.Write(objContext.path, sample);
        }
Ejemplo n.º 18
0
        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;
                }
            }
        }