Beispiel #1
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 #2
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);
        }
        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();
            }
        }
    }