Example #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);
        }
Example #2
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);
 }
Example #3
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);
        }
Example #4
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);
        }
Example #5
0
        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);
        }
Example #6
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);
        }
        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();
            }
        }
    }
Example #8
0
        /// <summary>
        /// Copy sphere data from USD to Unity with the given import options.
        /// </summary>
        /// <param name="skinnedMesh">
        /// Whether the Cube to build is skinned or not. This will allow to determine which Renderer to create
        /// on the GameObject (MeshRenderer or SkinnedMeshRenderer). Default value is false (not skinned).
        /// </param>
        public static void BuildSphere(SphereSample usdSphere,
                                       GameObject go,
                                       SceneImportOptions options,
                                       bool skinnedMesh = false)
        {
            Material mat = null;

            var sphereGo  = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            var unityMesh = sphereGo.GetComponent <MeshFilter>().sharedMesh;

            GameObject.DestroyImmediate(sphereGo);

            // Because Unity only handle a sphere with a default size, the custom size of it is define by the localScale
            // transform. This also need to be taken into account while computing the Unity extent of the mesh (see bellow).
            // This is doable because xformable data are always handled before mesh data, so go.transform already
            // contains any transform of the geometry.
            float size = (float)usdSphere.radius * 2;

            go.transform.localScale = go.transform.localScale * size;

            bool changeHandedness = options.changeHandedness == BasisTransformation.SlowAndSafe;
            bool hasBounds        = usdSphere.extent.size.x > 0 ||
                                    usdSphere.extent.size.y > 0 ||
                                    usdSphere.extent.size.z > 0;

            if (ShouldImport(options.meshOptions.boundingBox) && hasBounds)
            {
                if (changeHandedness)
                {
                    usdSphere.extent.center = UnityTypeConverter.ChangeBasis(usdSphere.extent.center);

                    // Divide the extent by the size of the cube. A custom size of the extent is define by
                    // the localScale transform (see above).
                    usdSphere.extent.extents = UnityTypeConverter.ChangeBasis(usdSphere.extent.extents) / size;
                }
                unityMesh.bounds = usdSphere.extent;
            }
            else if (ShouldCompute(options.meshOptions.boundingBox))
            {
                unityMesh.RecalculateBounds();
            }

            if (usdSphere.colors != null && ShouldImport(options.meshOptions.color))
            {
                // NOTE: The following color conversion assumes PlayerSettings.ColorSpace == Linear.
                // For best performance, convert color space to linear off-line and skip conversion.

                if (usdSphere.colors.Length == 1)
                {
                    // Constant color can just be set on the material.
                    mat = options.materialMap.InstantiateSolidColor(usdSphere.colors[0].gamma);
                }
                else
                {
                    // TODO: Improve logging by adding the path to the sphere prim. This would require that SphereSample
                    //       (and SampleBase class in general) allow to get the UsdPrim  back and it's path in the stage.
                    Debug.LogWarning(
                        "Only constant color are supported for sphere: (can't handle "
                        + usdSphere.colors.Length
                        + " color values)"
                        );
                }
            }

            if (mat == null)
            {
                mat = options.materialMap.InstantiateSolidColor(Color.white);
            }

            // Create Unity mesh.
            // TODO: This code is a duplicate of the CubeImporter code. It requires refactoring.
            Renderer renderer;

            if (skinnedMesh)
            {
                SkinnedMeshRenderer skinnedRenderer = ImporterBase.GetOrAddComponent <SkinnedMeshRenderer>(go);

                if (skinnedRenderer.sharedMesh == null)
                {
                    skinnedRenderer.sharedMesh = Mesh.Instantiate(unityMesh);
                }

                renderer = skinnedRenderer;
            }
            else
            {
                renderer = ImporterBase.GetOrAddComponent <MeshRenderer>(go);
                MeshFilter meshFilter = ImporterBase.GetOrAddComponent <MeshFilter>(go);

                if (meshFilter.sharedMesh == null)
                {
                    meshFilter.sharedMesh = Mesh.Instantiate(unityMesh);
                }
            }

            if (unityMesh.subMeshCount == 1)
            {
                renderer.sharedMaterial = mat;
            }
            else
            {
                var mats = new Material[unityMesh.subMeshCount];
                for (int i = 0; i < mats.Length; i++)
                {
                    mats[i] = mat;
                }
                renderer.sharedMaterials = mats;
            }
        }
Example #9
0
        private static void BuildMesh_(string path,
                                       MeshSample usdMesh,
                                       Mesh unityMesh,
                                       GeometrySubsets geomSubsets,
                                       GameObject go,
                                       Renderer renderer,
                                       SceneImportOptions options)
        {
            // TODO: Because this method operates on a GameObject, it must be single threaded. For this
            // reason, it should be extremely light weight. All computation should be completed prior to
            // this step, allowing heavy computations to happen in parallel. This is not currently the
            // case, triangulation and change of basis are non-trivial operations. Computing the mesh
            // bounds, normals and tangents should similarly be moved out of this function and should not
            // rely on the UnityEngine.Mesh API.

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

            //
            // Points.
            //

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

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

            //
            // Purpose.
            //

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

            //
            // Mesh Topology.
            //

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

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

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

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

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

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

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

            //
            // Extent / Bounds.
            //

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

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

            //
            // Normals.
            //

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

            //
            // Tangents.
            //

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

            //
            // Display Color.
            //

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

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

            //
            // UVs / Texture Coordinates.
            //

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

            Profiler.BeginSample("Request Material Bindings");

            //
            // Materials.
            //

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

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

            //
            // Lightmap UV Unwrapping.
            //

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

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

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

                UnityEditor.Unwrapping.GenerateSecondaryUVSet(unityMesh, unwrapSettings);
                Profiler.EndSample();
            }
#else
            if (options.meshOptions.generateLightmapUVs)
            {
                Debug.LogWarning("Lightmap UVs were requested to be generated, but cannot be generated outside of the editor");
            }
#endif
        }
Example #10
0
        /// <summary>
        /// Copy cube data from USD to Unity with the given import options.
        /// </summary>
        /// <param name="skinnedMesh">
        /// Whether the Cube to build is skinned or not. This will allow to determine which Renderer to create
        /// on the GameObject (MeshRenderer or SkinnedMeshRenderer). Default value is false (not skinned).
        /// </param>
        public static void BuildCube(CubeSample usdCube,
                                     GameObject go,
                                     SceneImportOptions options,
                                     bool skinnedMesh = false)
        {
            Material mat = null;

            var cubeGo    = GameObject.CreatePrimitive(PrimitiveType.Cube);
            var unityMesh = cubeGo.GetComponent <MeshFilter>().sharedMesh;

            GameObject.DestroyImmediate(cubeGo);

            // Because Unity only handle a cube with a default size, the custom size of it is define by the localScale
            // transform. This also need to be taken into account while computing the Unity extent of the mesh (see bellow).
            // This is doable because xformable data are always handled before mesh data, so go.transform already
            // contains any transform of the geometry.
            float size = (float)usdCube.size;

            go.transform.localScale = go.transform.localScale * size;

            bool changeHandedness = options.changeHandedness == BasisTransformation.SlowAndSafe;
            bool hasBounds        = usdCube.extent.size.x > 0 ||
                                    usdCube.extent.size.y > 0 ||
                                    usdCube.extent.size.z > 0;

            if (ShouldImport(options.meshOptions.boundingBox) && hasBounds)
            {
                if (changeHandedness)
                {
                    usdCube.extent.center = UnityTypeConverter.ChangeBasis(usdCube.extent.center);

                    // Divide the extent by the size of the cube. A custom size of the extent is define by
                    // the localScale transform (see above).
                    usdCube.extent.extents = UnityTypeConverter.ChangeBasis(usdCube.extent.extents) / size;
                }

                unityMesh.bounds = usdCube.extent;
            }
            else if (ShouldCompute(options.meshOptions.boundingBox))
            {
                unityMesh.RecalculateBounds();
            }

            if (usdCube.colors != null && ShouldImport(options.meshOptions.color))
            {
                // NOTE: The following color conversion assumes PlayerSettings.ColorSpace == Linear.
                // For best performance, convert color space to linear off-line and skip conversion.

                if (usdCube.colors.Length == 1)
                {
                    // Constant color can just be set on the material.
                    mat = options.materialMap.InstantiateSolidColor(usdCube.colors[0].gamma);
                    Debug.Log("constant colors assigned");
                }
                else if (usdCube.colors.Length == 6)
                {
                    // Uniform colors to verts.
                    // Note that USD cubes have 6 uniform colors and Unity cube mesh has 24 (6*4)
                    // TODO: move the conversion to C++ and use the color management API.
                    Debug.Log(unityMesh.vertexCount);
                    for (int i = 0; i < usdCube.colors.Length; i++)
                    {
                        usdCube.colors[i] = usdCube.colors[i];
                    }

                    var unityColors = new Color[24];
                    // Front:0, Back:1, Top:2, Bottom:3, Right:4, Left:5
                    unityColors[0] = usdCube.colors[0];  // front bottom right
                    unityColors[1] = usdCube.colors[0];  // front bottom left
                    unityColors[2] = usdCube.colors[0];  // front top right
                    unityColors[3] = usdCube.colors[0];  // front top left

                    unityColors[4] = usdCube.colors[2];  // top back right
                    unityColors[5] = usdCube.colors[2];  // top back left
                    unityColors[6] = usdCube.colors[1];  // back bottom right
                    unityColors[7] = usdCube.colors[1];  // back bottom left

                    unityColors[8]  = usdCube.colors[2]; // top front right
                    unityColors[9]  = usdCube.colors[2]; // top front left
                    unityColors[10] = usdCube.colors[1]; // back top right
                    unityColors[11] = usdCube.colors[1]; // back top left

                    unityColors[12] = usdCube.colors[3]; // Bottom back right
                    unityColors[13] = usdCube.colors[3]; // Bottom front right
                    unityColors[14] = usdCube.colors[3]; // Bottom front left
                    unityColors[15] = usdCube.colors[3]; // Bottom back left

                    unityColors[16] = usdCube.colors[5]; // left front bottom
                    unityColors[17] = usdCube.colors[5]; // left front top
                    unityColors[18] = usdCube.colors[5]; // left back top
                    unityColors[19] = usdCube.colors[5]; // left back bottom

                    unityColors[20] = usdCube.colors[4]; // right back bottom
                    unityColors[21] = usdCube.colors[4]; // right back top
                    unityColors[22] = usdCube.colors[4]; // right front top
                    unityColors[23] = usdCube.colors[4]; // right front bottom

                    unityMesh.colors = unityColors;
                }
                else if (usdCube.colors.Length == 24)
                {
                    // Face varying colors to verts.
                    // Note that USD cubes have 24 face varying colors and Unity cube mesh has 24 (6*4)
                    // TODO: move the conversion to C++ and use the color management API.
                    Debug.Log(unityMesh.vertexCount);
                    for (int i = 0; i < usdCube.colors.Length; i++)
                    {
                        usdCube.colors[i] = usdCube.colors[i];
                    }

                    // USD order: front, back, top, bottom, right, left
                    var unityColors = new Color[24];
                    unityColors[0] = usdCube.colors[3];       // front bottom right
                    unityColors[1] = usdCube.colors[2];       // front bottom left
                    unityColors[2] = usdCube.colors[0];       // front top right
                    unityColors[3] = usdCube.colors[1];       // front top left

                    unityColors[4] = usdCube.colors[8 + 1];   // top back right
                    unityColors[5] = usdCube.colors[8 + 2];   // top back left
                    unityColors[6] = usdCube.colors[4 + 3];   // back bottom right
                    unityColors[7] = usdCube.colors[4 + 0];   // back bottom left

                    unityColors[8]  = usdCube.colors[8 + 0];  // top front right
                    unityColors[9]  = usdCube.colors[8 + 3];  // top front left
                    unityColors[10] = usdCube.colors[4 + 2];  // back top right
                    unityColors[11] = usdCube.colors[4 + 1];  // back top left

                    unityColors[12] = usdCube.colors[12 + 1]; // Bottom back right
                    unityColors[13] = usdCube.colors[12 + 2]; // Bottom front right
                    unityColors[14] = usdCube.colors[12 + 3]; // Bottom front left
                    unityColors[15] = usdCube.colors[12 + 0]; // Bottom back left

                    unityColors[16] = usdCube.colors[20 + 1]; // left front bottom
                    unityColors[17] = usdCube.colors[20 + 2]; // left front top
                    unityColors[18] = usdCube.colors[20 + 3]; // left back top
                    unityColors[19] = usdCube.colors[20 + 0]; // left back bottom

                    unityColors[20] = usdCube.colors[16 + 2]; // right back bottom
                    unityColors[21] = usdCube.colors[16 + 3]; // right back top
                    unityColors[22] = usdCube.colors[16 + 0]; // right front top
                    unityColors[23] = usdCube.colors[16 + 1]; // right front bottom

                    unityMesh.colors = unityColors;
                }
                else if (usdCube.colors.Length == 8)
                {
                    // Vertex colors map on to verts.
                    // Note that USD cubes have 8 verts but Unity cube mesh has 24 (6*4)
                    // TODO: move the conversion to C++ and use the color management API.
                    Debug.Log(unityMesh.vertexCount);
                    for (int i = 0; i < usdCube.colors.Length; i++)
                    {
                        usdCube.colors[i] = usdCube.colors[i];
                    }

                    // USD order: front (top-right -> ccw)
                    //            back (bottom-left -> ccw (from back perspective))
                    var unityColors = new Color[24];
                    unityColors[0] = usdCube.colors[3];  // front bottom right
                    unityColors[1] = usdCube.colors[2];  // front bottom left
                    unityColors[2] = usdCube.colors[0];  // front top right
                    unityColors[3] = usdCube.colors[1];  // front top left

                    unityColors[4] = usdCube.colors[6];  // top back right
                    unityColors[5] = usdCube.colors[5];  // top back left
                    unityColors[6] = usdCube.colors[7];  // back bottom right
                    unityColors[7] = usdCube.colors[4];  // back bottom left

                    unityColors[8]  = usdCube.colors[0]; // top front right
                    unityColors[9]  = usdCube.colors[1]; // top front left
                    unityColors[10] = usdCube.colors[6]; // back top right
                    unityColors[11] = usdCube.colors[5]; // back top left

                    unityColors[12] = usdCube.colors[7]; // Bottom back right
                    unityColors[13] = usdCube.colors[3]; // Bottom front right
                    unityColors[14] = usdCube.colors[2]; // Bottom front left
                    unityColors[15] = usdCube.colors[4]; // Bottom back left

                    unityColors[16] = usdCube.colors[2]; // left front bottom
                    unityColors[17] = usdCube.colors[1]; // left front top
                    unityColors[18] = usdCube.colors[5]; // left back top
                    unityColors[19] = usdCube.colors[4]; // left back bottom

                    unityColors[20] = usdCube.colors[7]; // right back bottom
                    unityColors[21] = usdCube.colors[6]; // right back top
                    unityColors[22] = usdCube.colors[0]; // right front top
                    unityColors[23] = usdCube.colors[3]; // right front bottom

                    unityMesh.colors = unityColors;
                    Debug.Log("vertex colors assigned");
                }
                else
                {
                    // FaceVarying and uniform both require breaking up the mesh and are not yet handled in
                    // this example.
                    Debug.LogWarning("Uniform (color per face) and FaceVarying (color per vert per face) "
                                     + "display color not supported in this example");
                }
            }

            if (mat == null)
            {
                mat = options.materialMap.InstantiateSolidColor(Color.white);
            }

            // Create Unity mesh.
            Renderer renderer;

            if (skinnedMesh)
            {
                SkinnedMeshRenderer skinnedRenderer = ImporterBase.GetOrAddComponent <SkinnedMeshRenderer>(go);

                if (skinnedRenderer.sharedMesh == null)
                {
                    skinnedRenderer.sharedMesh = Mesh.Instantiate(unityMesh);
                }

                renderer = skinnedRenderer;
            }
            else
            {
                renderer = ImporterBase.GetOrAddComponent <MeshRenderer>(go);
                MeshFilter meshFilter = ImporterBase.GetOrAddComponent <MeshFilter>(go);

                if (meshFilter.sharedMesh == null)
                {
                    meshFilter.sharedMesh = Mesh.Instantiate(unityMesh);
                }
            }

            if (unityMesh.subMeshCount == 1)
            {
                renderer.sharedMaterial = mat;
            }
            else
            {
                var mats = new Material[unityMesh.subMeshCount];
                for (int i = 0; i < mats.Length; i++)
                {
                    mats[i] = mat;
                }

                renderer.sharedMaterials = mats;
            }
        }