Beispiel #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());
                }
            }
        }
Beispiel #2
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);
      }
    }
Beispiel #3
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);
        }
Beispiel #4
0
        /// <summary>
        /// Copyies the current sample values to the given camera.
        /// </summary>
        public void CopyToCamera(UnityEngine.Camera camera, bool setTransform)
        {
            // GfCamera is a gold mine of camera math.
            pxr.GfCamera c = new pxr.GfCamera(UnityTypeConverter.ToGfMatrix(transform),
                                              projection == ProjectionType.Perspective ? pxr.GfCamera.Projection.Perspective
                                                   : pxr.GfCamera.Projection.Orthographic,
                                              this.horizontalAperture,
                                              this.verticalAperture,
                                              this.horizontalApertureOffset,
                                              this.verticalApertureOffset,
                                              this.focalLength);

            camera.orthographic  = c.GetProjection() == pxr.GfCamera.Projection.Orthographic;
            camera.fieldOfView   = c.GetFieldOfView(pxr.GfCamera.FOVDirection.FOVVertical);
            camera.aspect        = c.GetAspectRatio();
            camera.nearClipPlane = clippingRange.x;
            camera.farClipPlane  = clippingRange.y;

            if (camera.orthographic)
            {
                // Note that USD default scale is cm and aperture is in mm.
                // Also Unity ortho size is the half aperture, so divide USD by 2.
                camera.orthographicSize = (verticalAperture / 10.0f) / 2.0f;
            }

            if (setTransform)
            {
                var tr = camera.transform;
                var xf = transform;
                UnityTypeConverter.SetTransform(xf, tr);
            }
        }
Beispiel #5
0
        public static void BuildXform(Matrix4x4 xf,
                                      GameObject go,
                                      SceneImportOptions options)
        {
            UnityEngine.Profiling.Profiler.BeginSample("Change Handedness");
            ImportXform(ref xf, options);
            UnityEngine.Profiling.Profiler.EndSample();

            Vector3    localPos;
            Quaternion localRot;
            Vector3    localScale;

            UnityEngine.Profiling.Profiler.BeginSample("Decompose Matrix");
            bool success = UnityTypeConverter.Decompose(xf, out localPos, out localRot, out localScale);

            UnityEngine.Profiling.Profiler.EndSample();

            if (!success)
            {
                Debug.LogError("Non-decomposable transform matrix for " + go.name);
                return;
            }

            UnityEngine.Profiling.Profiler.BeginSample("Assign Values");
            go.transform.localPosition = localPos;
            go.transform.localScale    = localScale;
            go.transform.localRotation = localRot;
            UnityEngine.Profiling.Profiler.EndSample();
        }
Beispiel #6
0
 /// <summary>
 /// Imports a matrix transform, correctly handling the change of basis.
 /// </summary>
 public static void ImportXform(ref Matrix4x4 mat, SceneImportOptions options)
 {
     if (options.changeHandedness == BasisTransformation.FastWithNegativeScale)
     {
         return;
     }
     mat = UnityTypeConverter.ChangeBasis(mat);
 }
Beispiel #7
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);
      }
    }
Beispiel #8
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);
        }
Beispiel #9
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();
            }
        }
Beispiel #10
0
        public static void ExportSkelRoot(ObjectContext objContext, ExportContext exportContext)
        {
            var sample = (SkelRootSample)objContext.sample;

            // Compute bounds for the root, required by USD.
            bool first = true;

            // Ensure the bounds are computed in root-local space.
            // This is required because USD expects the extent to be a local bound for the SkelRoot.
            var oldParent = objContext.gameObject.transform.parent;

            objContext.gameObject.transform.SetParent(null, worldPositionStays: false);

            try
            {
                foreach (var r in objContext.gameObject.GetComponentsInChildren <Renderer>())
                {
                    if (first)
                    {
                        // Ensure the bounds object starts growing from the first valid child bounds.
                        first         = false;
                        sample.extent = r.bounds;
                    }
                    else
                    {
                        sample.extent.Encapsulate(r.bounds);
                    }
                }
            }
            finally
            {
                // Restore the root parent.
                objContext.gameObject.transform.SetParent(oldParent, worldPositionStays: false);
            }

            // Convert handedness if needed.
            if (exportContext.basisTransform == BasisTransformation.SlowAndSafe)
            {
                sample.extent.center = UnityTypeConverter.ChangeBasis(sample.extent.center);
            }

            // Convert the transform
            var path = new pxr.SdfPath(objContext.path);

            // If exporting for Z-Up, rotate the world.
            bool correctZUp = exportContext.scene.UpAxis == Scene.UpAxes.Z;

            sample.transform = XformExporter.GetLocalTransformMatrix(
                objContext.gameObject.transform,
                correctZUp,
                path.IsRootPrimPath(),
                exportContext.basisTransform);

            exportContext.scene.Write(objContext.path, sample);
        }
Beispiel #11
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);
        }
Beispiel #12
0
        /// <summary>
        /// Reads geometry subsets if authored. If not authored, returns an empty dictionary.
        /// </summary>
        public static GeometrySubsets ReadGeomSubsets(Scene scene, string path)
        {
            var result = new GeometrySubsets();

            var prim = scene.GetPrimAtPath(path);

            if (prim == null || prim.IsValid() == false)
            {
                return(result);
            }

            var im = new pxr.UsdGeomImageable(prim);

            if (im._IsValid() == false)
            {
                return(result);
            }

            pxr.UsdGeomSubsetVector subsets =
                pxr.UsdGeomSubset.GetGeomSubsets(im, pxr.UsdGeomTokens.face,
                                                 new pxr.TfToken("materialBind"));

            // Cache these values to minimize garbage collector churn.
            var value = new pxr.VtValue();

            int[] intValue    = new int[0];
            var   defaultTime = pxr.UsdTimeCode.Default();

            foreach (var subset in subsets)
            {
                if (!subset._IsValid())
                {
                    continue;
                }

                var indices = subset.GetIndicesAttr();
                if (!indices.IsValid())
                {
                    continue;
                }

                if (!indices.Get(value, defaultTime))
                {
                    continue;
                }

                UnityTypeConverter.FromVtArray(value, ref intValue);
                result.Subsets.Add(subset.GetPath(), intValue);
            }

            return(result);
        }
Beispiel #13
0
        public static void BuildDebugBindTransforms(SkeletonSample skelSample,
                                                    GameObject goSkeleton,
                                                    SceneImportOptions options)
        {
            var debugPrefix = "usdSkel_bindPose_debug_cube";

            if (options.meshOptions.debugShowSkeletonBindPose)
            {
                int i = 0;
                foreach (var bindXf in skelSample.bindTransforms)
                {
                    // Undo the bindXf inversion for visualization.
                    var mat      = bindXf.inverse;
                    var cubeName = debugPrefix + i++;
                    var cube     = goSkeleton.transform.Find(cubeName);
                    if (!cube)
                    {
                        cube = GameObject.CreatePrimitive(PrimitiveType.Cube).transform;
                        cube.SetParent(goSkeleton.transform, worldPositionStays: false);
                        cube.name = cubeName;
                    }

                    Vector3    t, s;
                    Quaternion r;
                    UnityTypeConverter.Decompose(mat, out t, out r, out s);
                    cube.localPosition = t;
                    cube.localScale    = s;
                    cube.localRotation = r;
                }
            }
            else
            {
                var zero = goSkeleton.transform.Find(debugPrefix + 0);
                if (zero)
                {
                    var toDelete = new List <GameObject>();
                    foreach (Transform child in goSkeleton.transform)
                    {
                        if (child.name.StartsWith(debugPrefix))
                        {
                            toDelete.Add(child.gameObject);
                        }
                    }

                    foreach (var child in toDelete)
                    {
                        GameObject.DestroyImmediate(child);
                    }
                }
            }
        }
Beispiel #14
0
        /// <summary>
        /// Copyies the current sample values to the given camera.
        /// </summary>
        public void CopyToCamera(UnityEngine.Camera camera)
        {
            // GfCamera is a gold mine of camera math.
            pxr.GfCamera c = new pxr.GfCamera(UnityTypeConverter.ToGfMatrix(transform));

            camera.orthographic  = c.GetProjection() == pxr.GfCamera.Projection.Orthographic;
            camera.fieldOfView   = c.GetFieldOfView(pxr.GfCamera.FOVDirection.FOVVertical);
            camera.aspect        = c.GetAspectRatio();
            camera.nearClipPlane = c.GetClippingRange().GetMin();
            camera.farClipPlane  = c.GetClippingRange().GetMax();

            var tr = camera.transform;
            var xf = transform;

            UnityTypeConverter.SetTransform(xf, tr);
        }
Beispiel #15
0
        public static void ExportSkelRoot(ObjectContext objContext, ExportContext exportContext)
        {
            var sample   = (SkelRootSample)objContext.sample;
            var bindings = ((string[])objContext.additionalData);

            if (bindings != null)
            {
                sample.skeleton = bindings[0];
                if (bindings.Length > 1)
                {
                    sample.animationSource = bindings[1];
                }
            }

            // Compute bounds for the root, required by USD.
            bool first = true;

            foreach (var r in objContext.gameObject.GetComponentsInChildren <Renderer>())
            {
                if (first)
                {
                    // Ensure the bounds object starts growing from the first valid child bounds.
                    first         = false;
                    sample.extent = r.bounds;
                }
                else
                {
                    sample.extent.Encapsulate(r.bounds);
                }
            }


            // Convert the bounds from worldspace to local space.
            // This is required because USD expects the extent to be a local bound for the SkelRoot.
            var xf = objContext.gameObject.transform;

            sample.extent.min = xf.worldToLocalMatrix.MultiplyPoint(sample.extent.min);
            sample.extent.max = xf.worldToLocalMatrix.MultiplyPoint(sample.extent.max);

            if (exportContext.basisTransform == BasisTransformation.SlowAndSafe)
            {
                sample.extent.min = UnityTypeConverter.ChangeBasis(sample.extent.min);
                sample.extent.max = UnityTypeConverter.ChangeBasis(sample.extent.max);
            }

            exportContext.scene.Write(objContext.path, sample);
        }
            public UnityEngine.Matrix4x4[] ComputeInstanceMatrices(USD.NET.Scene scene, string primPath)
            {
                var prim   = scene.GetPrimAtPath(primPath);
                var pi     = new pxr.UsdGeomPointInstancer(prim);
                var xforms = new pxr.VtMatrix4dArray();

                pi.ComputeInstanceTransformsAtTime(xforms, scene.Time == null ? pxr.UsdTimeCode.Default() : scene.Time, 0);

                // Slow, but works.
                var matrices = new UnityEngine.Matrix4x4[xforms.size()];

                for (int i = 0; i < xforms.size(); i++)
                {
                    matrices[i] = UnityTypeConverter.FromMatrix(xforms[i]);
                }
                return(matrices);
            }
Beispiel #17
0
        public static void BuildSkeletonBone(string skelPath,
                                             GameObject go,
                                             Matrix4x4 restXform,
                                             VtTokenArray joints,
                                             SceneImportOptions importOptions)
        {
            // Perform change of basis, if needed.
            XformImporter.ImportXform(ref restXform, importOptions);

            // Decompose into TSR.
            Vector3    pos   = Vector3.zero;
            Quaternion rot   = Quaternion.identity;
            Vector3    scale = Vector3.one;

            if (!UnityTypeConverter.Decompose(restXform, out pos, out rot, out scale))
            {
                throw new Exception("Failed to decompose bind transforms for <" + skelPath + ">");
            }

            go.transform.localScale    = scale;
            go.transform.localRotation = rot;
            go.transform.localPosition = pos;

            var cubeDebugName = "usdSkel_restPose_debug_cube";

            if (importOptions.meshOptions.debugShowSkeletonRestPose)
            {
                var cube = go.transform.Find(cubeDebugName);
                if (!cube)
                {
                    cube      = GameObject.CreatePrimitive(PrimitiveType.Cube).transform;
                    cube.name = cubeDebugName;
                    cube.SetParent(go.transform, worldPositionStays: false);
                    cube.localScale = Vector3.one * 2;
                }
            }
            else
            {
                var existing = go.transform.Find(cubeDebugName);
                if (existing)
                {
                    GameObject.DestroyImmediate(existing.gameObject);
                }
            }
        }
Beispiel #18
0
        public static void CameraTest2()
        {
            CameraSample sample = new CameraSample();

            sample.transform     = UnityEngine.Matrix4x4.identity;
            sample.clippingRange = new UnityEngine.Vector2(.01f, 10);

            // GfCamera is a gold mine of camera math.
            pxr.GfCamera c = new pxr.GfCamera(UnityTypeConverter.ToGfMatrix(UnityEngine.Matrix4x4.identity));

            sample.focalLength        = c.GetFocalLength();
            sample.horizontalAperture = c.GetHorizontalAperture();
            sample.verticalAperture   = c.GetVerticalAperture();

            var scene = USD.NET.Scene.Create();

            scene.Write("/Foo/Bar", sample);
        }
        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();
        }
        public static Matrix4x4 GetLocalTransformMatrix(
            Transform tr,
            bool correctZUp,
            bool isRootPrim,
            BasisTransformation conversionType)
        {
            var  localRot    = tr.localRotation;
            bool fastConvert = conversionType == BasisTransformation.FastWithNegativeScale;

            if (correctZUp && isRootPrim)
            {
                float invert = fastConvert ? 1 : -1;
                localRot = localRot * Quaternion.AngleAxis(invert * 90, Vector3.right);
            }

            var mat = Matrix4x4.TRS(tr.localPosition, localRot, tr.localScale);

            // Unity uses a forward vector that matches DirectX, but USD matches OpenGL, so a change of
            // basis is required. There are shortcuts, but this is fully general.
            //
            // Here we can either put a partial conversion at the root (fast & dangerous) or convert the
            // entire hierarchy, along with the points, normals and triangle winding. The benefit of the
            // full conversion is that there are no negative scales left in the hierarchy.
            //
            // Note that this is the correct partial conversion for the root transforms, however the
            // camera and light matrices must contain the other half of the conversion
            // (e.g. mat * basisChangeInverse).
            if (fastConvert && isRootPrim)
            {
                // Partial change of basis.
                var basisChange = Matrix4x4.identity;
                // Invert the forward vector.
                basisChange[2, 2] = -1;
                mat = basisChange * mat;
            }
            else if (!fastConvert)
            {
                // Full change of basis.
                mat = UnityTypeConverter.ChangeBasis(mat);
            }

            return(mat);
        }
        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);
        }
Beispiel #22
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();
        }
        static void ExportMesh(ObjectContext objContext,
                               ExportContext exportContext,
                               Mesh mesh,
                               Material sharedMaterial,
                               Material[] sharedMaterials,
                               bool exportMeshPose = true)
        {
            string path = objContext.path;

            if (mesh == null)
            {
                Debug.LogWarning("Null mesh for: " + path, objContext.gameObject);
                return;
            }

#if UNITY_EDITOR
            if (!CanReadMesh(mesh))
            {
#else
            if (!mesh.isReadable)
            {
#endif
                Debug.LogError(
                    "Mesh is not readable: " + objContext.path +
                    ". To fix this, enable read/write in the inspector for the source asset that you are attempting to export.",
                    objContext.gameObject);
                return;
            }

            var scene                  = exportContext.scene;
            bool unvarying             = scene.Time == null;
            bool slowAndSafeConversion = exportContext.basisTransform == BasisTransformation.SlowAndSafe;
            var sample                 = (MeshSample)objContext.sample;
            var go = objContext.gameObject;

            if (mesh.bounds.center == Vector3.zero && mesh.bounds.extents == Vector3.zero)
            {
                mesh.RecalculateBounds();
            }

            sample.extent = mesh.bounds;

            if (slowAndSafeConversion)
            {
                // Unity uses a forward vector that matches DirectX, but USD matches OpenGL, so a change of
                // basis is required. There are shortcuts, but this is fully general.
                sample.ConvertTransform();
                sample.extent.center = UnityTypeConverter.ChangeBasis(sample.extent.center);
            }

            // Only export the mesh topology on the first frame.
            if (unvarying)
            {
                // TODO: Technically a mesh could be the root transform, which is not handled correctly here.
                // It should have the same logic for root prims as in ExportXform.
                sample.transform = XformExporter.GetLocalTransformMatrix(
                    go.transform,
                    scene.UpAxis == Scene.UpAxes.Z,
                    new pxr.SdfPath(path).IsRootPrimPath(),
                    exportContext.basisTransform);

                sample.normals  = mesh.normals;
                sample.points   = mesh.vertices;
                sample.tangents = mesh.tangents;

                sample.colors = mesh.colors;
                if (sample.colors != null && sample.colors.Length == 0)
                {
                    sample.colors = null;
                }

                if ((sample.colors == null || sample.colors.Length == 0) &&
                    (sharedMaterial != null && sharedMaterial.HasProperty("_Color")))
                {
                    sample.colors    = new Color[1];
                    sample.colors[0] = sharedMaterial.color.linear;
                }

                // Gah. There is no way to inspect a meshes UVs.
                sample.st = mesh.uv;

                // Set face vertex counts and indices.
                var tris = mesh.triangles;

                if (slowAndSafeConversion)
                {
                    // Unity uses a forward vector that matches DirectX, but USD matches OpenGL, so a change
                    // of basis is required. There are shortcuts, but this is fully general.

                    for (int i = 0; i < sample.points.Length; i++)
                    {
                        sample.points[i] = UnityTypeConverter.ChangeBasis(sample.points[i]);
                        if (sample.normals != null && sample.normals.Length == sample.points.Length)
                        {
                            sample.normals[i] = UnityTypeConverter.ChangeBasis(sample.normals[i]);
                        }

                        if (sample.tangents != null && sample.tangents.Length == sample.points.Length)
                        {
                            var w = sample.tangents[i].w;
                            var t = UnityTypeConverter.ChangeBasis(sample.tangents[i]);
                            sample.tangents[i] = new Vector4(t.x, t.y, t.z, w);
                        }
                    }

                    for (int i = 0; i < tris.Length; i += 3)
                    {
                        var t = tris[i];
                        tris[i]     = tris[i + 1];
                        tris[i + 1] = t;
                    }
                }

                sample.SetTriangles(tris);

                UnityEngine.Profiling.Profiler.BeginSample("USD: Mesh Write");
                scene.Write(path, sample);
                UnityEngine.Profiling.Profiler.EndSample();

                // TODO: this is a bit of a half-measure, we need real support for primvar interpolation.
                // Set interpolation based on color count.
                if (sample.colors != null && sample.colors.Length == 1)
                {
                    pxr.UsdPrim usdPrim      = scene.GetPrimAtPath(path);
                    var         colorPrimvar =
                        new pxr.UsdGeomPrimvar(usdPrim.GetAttribute(pxr.UsdGeomTokens.primvarsDisplayColor));
                    colorPrimvar.SetInterpolation(pxr.UsdGeomTokens.constant);
                    var opacityPrimvar =
                        new pxr.UsdGeomPrimvar(usdPrim.GetAttribute(pxr.UsdGeomTokens.primvarsDisplayOpacity));
                    opacityPrimvar.SetInterpolation(pxr.UsdGeomTokens.constant);
                }

                string usdMaterialPath;
                if (exportContext.exportMaterials && sharedMaterial != null)
                {
                    if (!exportContext.matMap.TryGetValue(sharedMaterial, out usdMaterialPath))
                    {
                        Debug.LogError("Invalid material bound for: " + path);
                    }
                    else
                    {
                        MaterialSample.Bind(scene, path, usdMaterialPath);
                    }
                }

                // In USD subMeshes are represented as UsdGeomSubsets.
                // When there are multiple subMeshes, convert them into UsdGeomSubsets.
                if (mesh.subMeshCount > 1)
                {
                    // Build a table of face indices, used to convert the subMesh triangles to face indices.
                    var faceTable = new Dictionary <Vector3, int>();
                    for (int i = 0; i < tris.Length; i += 3)
                    {
                        if (!slowAndSafeConversion)
                        {
                            faceTable.Add(new Vector3(tris[i], tris[i + 1], tris[i + 2]), i / 3);
                        }
                        else
                        {
                            // Under slow and safe export, index 0 and 1 are swapped.
                            // This swap will not be present in the subMesh indices, so must be undone here.
                            faceTable.Add(new Vector3(tris[i + 1], tris[i], tris[i + 2]), i / 3);
                        }
                    }

                    var usdPrim     = scene.GetPrimAtPath(path);
                    var usdGeomMesh = new pxr.UsdGeomMesh(usdPrim);

                    // Process each subMesh and create a UsdGeomSubset of faces this subMesh targets.
                    for (int si = 0; si < mesh.subMeshCount; si++)
                    {
                        int[] indices     = mesh.GetTriangles(si);
                        int[] faceIndices = new int[indices.Length / 3];

                        for (int i = 0; i < indices.Length; i += 3)
                        {
                            faceIndices[i / 3] = faceTable[new Vector3(indices[i], indices[i + 1], indices[i + 2])];
                        }

                        var vtIndices = UnityTypeConverter.ToVtArray(faceIndices);
                        var subset    = pxr.UsdGeomSubset.CreateUniqueGeomSubset(
                            usdGeomMesh,            // The object of which this subset belongs.
                            m_subMeshesToken,       // An arbitrary name for the subset.
                            pxr.UsdGeomTokens.face, // Indicator that these represent face indices
                            vtIndices,              // The actual face indices.
                            m_materialBindToken     // familyName = "materialBind"
                            );

                        if (exportContext.exportMaterials)
                        {
                            if (si >= sharedMaterials.Length || !sharedMaterials[si] ||
                                !exportContext.matMap.TryGetValue(sharedMaterials[si], out usdMaterialPath))
                            {
                                Debug.LogWarning("Invalid material bound for: " + path + "\n"
                                                 + (si >= sharedMaterials.Length
                                        ? "More submeshes than materials assigned."
                                        : (!sharedMaterials[si]
                                            ? "Submesh " + si + " has null material"
                                            : "ExportMap can't map material")));
                            }
                            else
                            {
                                MaterialSample.Bind(scene, subset.GetPath(), usdMaterialPath);
                            }
                        }
                    }
                }
            }
            else
            {
                // Only write the transform when animating.
                var meshSample = new MeshSampleBase();
                meshSample.extent    = sample.extent;
                meshSample.transform = XformExporter.GetLocalTransformMatrix(
                    go.transform,
                    scene.UpAxis == Scene.UpAxes.Z,
                    new pxr.SdfPath(path).IsRootPrimPath(),
                    exportContext.basisTransform);

                if (exportMeshPose)
                {
                    meshSample.points = mesh.vertices;

                    // Set face vertex counts and indices.
                    var tris = mesh.triangles;

                    if (slowAndSafeConversion)
                    {
                        // Unity uses a forward vector that matches DirectX, but USD matches OpenGL, so a change
                        // of basis is required. There are shortcuts, but this is fully general.
                        for (int i = 0; i < meshSample.points.Length; i++)
                        {
                            meshSample.points[i] = UnityTypeConverter.ChangeBasis(meshSample.points[i]);
                        }

                        for (int i = 0; i < tris.Length; i += 3)
                        {
                            var t = tris[i];
                            tris[i]     = tris[i + 1];
                            tris[i + 1] = t;
                        }
                    }

                    sample.SetTriangles(tris);
                }

                UnityEngine.Profiling.Profiler.BeginSample("USD: Mesh Write");
                scene.Write(path, meshSample);
                UnityEngine.Profiling.Profiler.EndSample();
            }
        }
    }
Beispiel #24
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);
        }
        /// <summary>
        /// Converts a VtValue to a SerializedProperty, to reconstruct the USD scene in Unity.
        /// </summary>
        static public void VtValueToProp(SerializedProperty prop, pxr.VtValue val)
        {
            switch (prop.propertyType)
            {
            case SerializedPropertyType.AnimationCurve:
                // TODO: needs to be broken down into atoms.
                throw new System.NotImplementedException();

            case SerializedPropertyType.ArraySize:
                //prop.intValue = (int)val;
                break;

            case SerializedPropertyType.Boolean:
                prop.boolValue = (bool)val;
                break;

            case SerializedPropertyType.Bounds:
                prop.boundsValue = UnityTypeConverter.BoundsFromVtArray(val);
                break;

            case SerializedPropertyType.BoundsInt:
                // TODO: add this to UnityTypeConverter.
                var bnds   = UnityTypeConverter.BoundsFromVtArray(val);
                var center = new Vector3Int((int)bnds.center.x, (int)bnds.center.y, (int)bnds.center.z);
                var size   = new Vector3Int((int)bnds.size.x, (int)bnds.size.y, (int)bnds.size.z);
                prop.boundsIntValue = new BoundsInt(center, size);
                break;

            case SerializedPropertyType.Character:
                prop.intValue = (int)val;
                break;

            case SerializedPropertyType.Color:
                prop.colorValue = UnityTypeConverter.Vec4fToColor(val);
                break;

            case SerializedPropertyType.Enum:
                prop.enumValueIndex = (int)val;
                break;

            case SerializedPropertyType.ExposedReference:
                // TODO.
                //prop.exposedReferenceValue;
                throw new System.NotImplementedException();

            case SerializedPropertyType.FixedBufferSize:
                //prop.fixedBufferSize = (int)val;
                // TODO.
                throw new System.NotImplementedException();

            case SerializedPropertyType.Float:
                prop.floatValue = (float)val;
                break;

            case SerializedPropertyType.Generic:
                throw new System.Exception();

            case SerializedPropertyType.Gradient:
                // TODO: gradientValue accessor is not public. wat?
                throw new System.NotImplementedException();

            case SerializedPropertyType.Integer:
                prop.intValue = (int)val;
                break;

            case SerializedPropertyType.LayerMask:
                prop.intValue = (int)val;
                break;

            case SerializedPropertyType.ObjectReference:
                /*
                 * var v2i = (pxr.GfVec2i)val;
                 * if (v2i[0] == 0 && v2i[1] == 0) {
                 * break;
                 * }
                 * Debug.Log("FileID: " + v2i[0] + " PathID: " + v2i[1]);
                 */
                if (val.IsEmpty())
                {
                    break;
                }

                string strValue = pxr.UsdCs.VtValueTostring(val);
                if (string.IsNullOrEmpty(strValue))
                {
                    break;
                }

                string[] names  = strValue.Split(':');
                int      pathId = int.Parse(names[0]);
                var      guid   = names[1];
                int      fileId = int.Parse(names[2]);

                string   assetPath = AssetDatabase.GUIDToAssetPath(guid);
                Object[] objs      = AssetDatabase.LoadAllAssetsAtPath(assetPath);

                Object obj = objs[pathId];

                Debug.Log("pathId: " + pathId
                          + " fileId: " + fileId
                          + " guid: " + guid.ToString()
                          + " obj: " + obj.ToString());

                //break;

                /* TODO:
                 * string expectedName = names[2];
                 * if (objs[index].name != expectedName) {
                 * Debug.LogWarning("Expected name '" + expectedName + "' but found '" + objs[index].name + "'");
                 * }
                 */
                prop.FindPropertyRelative("m_PathID").intValue = pathId;
                prop.FindPropertyRelative("m_FileID").intValue = fileId;
                prop.objectReferenceValue = obj;

                break;

            case SerializedPropertyType.Quaternion:
                prop.quaternionValue = UnityTypeConverter.QuatfToQuaternion(val);
                break;

            case SerializedPropertyType.Rect:
                prop.rectValue = UnityTypeConverter.Vec4fToRect(val);
                break;

            case SerializedPropertyType.RectInt:
                var rect = UnityTypeConverter.Vec4fToRect(val);
                prop.rectIntValue = new RectInt((int)rect.xMin, (int)rect.yMin,
                                                (int)rect.width, (int)rect.height);
                break;

            case SerializedPropertyType.String:
                var s = (string)val;
                if (s == null)
                {
                    break;
                }

                prop.stringValue = (string)val;
                break;

            case SerializedPropertyType.Vector2:
                prop.vector2Value = UnityTypeConverter.Vec2fToVector2(val);
                break;

            case SerializedPropertyType.Vector2Int:
                // TODO: add this to UnityTypeConverter.
                var v2 = (pxr.GfVec2i)val;
                prop.vector2IntValue = new Vector2Int(v2[0], v2[1]);
                break;

            case SerializedPropertyType.Vector3:
                prop.vector3Value = UnityTypeConverter.Vec3fToVector3(val);
                break;

            case SerializedPropertyType.Vector3Int:
                // TODO: add this to UnityTypeConverter.
                var v3 = (pxr.GfVec3i)val;
                prop.vector3IntValue = new Vector3Int(v3[0], v3[1], v3[2]);
                break;

            case SerializedPropertyType.Vector4:
                prop.vector4Value = UnityTypeConverter.Vec4fToVector4(val);
                break;
            }
        }
        /// <summary>
        /// Converts a SerializedProperty to a VtValue, for writing to USD.
        /// </summary>
        static public pxr.VtValue PropToVtValue(SerializedProperty prop)
        {
            switch (prop.propertyType)
            {
            case SerializedPropertyType.AnimationCurve:
                // TODO: needs to be broken down into atoms.
                return(new pxr.VtValue());

            case SerializedPropertyType.ArraySize:
                return(prop.intValue);

            case SerializedPropertyType.Boolean:
                return(prop.boolValue);

            case SerializedPropertyType.Bounds:
                return(UnityTypeConverter.BoundsToVtArray(prop.boundsValue));

            case SerializedPropertyType.BoundsInt:
                // TODO: add this to UnityTypeConverter.
                var bi   = prop.boundsIntValue;
                var bnds = new Bounds(bi.center, bi.size);
                return(UnityTypeConverter.BoundsToVtArray(bnds));

            case SerializedPropertyType.Character:
                return(prop.intValue);

            case SerializedPropertyType.Color:
                return(UnityTypeConverter.ColorToVec4f(prop.colorValue));

            case SerializedPropertyType.Enum:
                return(prop.enumDisplayNames[prop.enumValueIndex]);

            case SerializedPropertyType.ExposedReference:
                // TODO.
                //return prop.exposedReferenceValue;
                return(new pxr.VtValue());

            case SerializedPropertyType.FixedBufferSize:
                return(prop.fixedBufferSize);

            case SerializedPropertyType.Float:
                return(prop.floatValue);

            case SerializedPropertyType.Generic:
                return("GENERIC");

            case SerializedPropertyType.Gradient:
                // TODO: gradientValue accessor is not public. wat?
                return("Gradient");

            case SerializedPropertyType.Integer:
                return(prop.intValue);

            case SerializedPropertyType.LayerMask:
                return(prop.intValue);

            case SerializedPropertyType.ObjectReference:
                var obj = prop.objectReferenceValue;
                if (obj == null)
                {
                    return(new pxr.VtValue(""));
                }

                // For object references in the scene, the asset path will be empty/null.
                // However, for mesh and material instances in the scen, what do we want to do here?
                // They are serialized to .unity files with just a file id, rather than fileid, pathid, and guid.
                string assetPath = AssetDatabase.GetAssetPath(prop.objectReferenceValue);
                if (string.IsNullOrEmpty(assetPath))
                {
                    return(new pxr.VtValue(""));
                }

                var    fileId = prop.FindPropertyRelative("m_FileID").intValue;
                var    pathId = prop.FindPropertyRelative("m_PathID").intValue;
                string guid   = AssetDatabase.AssetPathToGUID(assetPath);
                return(prop.FindPropertyRelative("m_PathID").intValue + ":" + guid + ":" + fileId);

            case SerializedPropertyType.Quaternion:
                return(UnityTypeConverter.QuaternionToQuatf(prop.quaternionValue));

            case SerializedPropertyType.Rect:
                return(UnityTypeConverter.RectToVtVec4(prop.rectValue));

            case SerializedPropertyType.RectInt:
                // TODO: add this to UnityTypeConverter.
                var ri = prop.rectIntValue;
                return(new pxr.GfVec4i(ri.x, ri.y, ri.width, ri.height));

            case SerializedPropertyType.String:
                return(prop.stringValue);

            case SerializedPropertyType.Vector2:
                return(UnityTypeConverter.Vector2ToVec2f(prop.vector2Value));

            case SerializedPropertyType.Vector2Int:
                // TODO: add this to UnityTypeConverter.
                return(new pxr.GfVec2i(prop.vector2IntValue.x, prop.vector2IntValue.y));

            case SerializedPropertyType.Vector3:
                return(UnityTypeConverter.Vector3ToVec3f(prop.vector3Value));

            case SerializedPropertyType.Vector3Int:
                var v3 = prop.vector3IntValue;
                // TODO: add this to UnityTypeConverter.
                return(new pxr.GfVec3i(v3.x, v3.y, v3.z));

            case SerializedPropertyType.Vector4:
                return(UnityTypeConverter.Vector4ToVec4f(prop.vector4Value));
            }

            return("UNKNOWN");
        }
Beispiel #27
0
        /// <summary>
        /// If HierInfo represents a UsdSkelRoot, reads the associated skelton joints into the
        /// skelJoints member.
        /// </summary>
        static void ReadSkeletonJoints(ref HierInfo skelRootInfo)
        {
            if (skelRootInfo.prim == null)
            {
                return;
            }

            var skelRoot = new UsdSkelRoot(skelRootInfo.prim);

            if (!skelRoot)
            {
                return;
            }

            var processed = new HashSet <SdfPath>();

            foreach (UsdSkelBinding binding in skelRootInfo.skelBindings)
            {
                var skel = binding.GetSkeleton();

                if (!skel)
                {
                    continue;
                }

                // If the same skeleton is referenced multiple times, only process it once.
                if (processed.Contains(skel.GetPath()))
                {
                    continue;
                }
                processed.Add(skel.GetPath());

                var jointsAttr = skel.GetJointsAttr();
                if (!jointsAttr)
                {
                    continue;
                }

                var vtJoints = jointsAttr.Get();
                if (vtJoints.IsEmpty())
                {
                    continue;
                }
                var vtStrings = UsdCs.VtValueToVtTokenArray(vtJoints);
                var joints    = UnityTypeConverter.FromVtArray(vtStrings);

                var skelPath = skel.GetPath();
                skelRootInfo.skelJoints = new SdfPath[joints.Length];

                for (int i = 0; i < joints.Length; i++)
                {
                    var jointPath = new SdfPath(joints[i]);
                    if (joints[i] == "/")
                    {
                        skelRootInfo.skelJoints[i] = skelPath;
                        continue;
                    }
                    else if (jointPath.IsAbsolutePath())
                    {
                        Debug.LogException(new Exception("Unexpected absolute joint path: " + jointPath));
                        jointPath = new SdfPath(joints[i].TrimStart('/'));
                    }
                    skelRootInfo.skelJoints[i] = skelPath.AppendPath(jointPath);
                }
            }
        }
Beispiel #28
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());
        }
Beispiel #29
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);
        }