Пример #1
0
        // ------------------------------------------------------------------------------------------ //
        // Create a USD scene for testing
        // ------------------------------------------------------------------------------------------ //
        static private Scene CreateSceneWithShading()
        {
            var scene = Scene.Create();

            var cubePath     = kCubePath;
            var materialPath = "/Model/Materials/SimpleMat";
            var shaderPath   = "/Model/Materials/SimpleMat/StandardShader";

            var albedoMapPath       = "/Model/Materials/SimpleMat/AlbedoMap";
            var normalMapPath       = "/Model/Materials/SimpleMat/NormalMap";
            var emissionMapPath     = "/Model/Materials/SimpleMat/EmissionMap";
            var metallicMapPath     = "/Model/Materials/SimpleMat/MetallicMap";
            var occlusionMapPath    = "/Model/Materials/SimpleMat/OcclusionMap";
            var parallaxMapPath     = "/Model/Materials/SimpleMat/ParallaxMap";
            var detailNormalMapPath = "/Model/Materials/SimpleMat/DetailNormalMap";
            var detailMaskPath      = "/Model/Materials/SimpleMat/DetailMask";

            var textureFilePath = @"Assets/Samples/USD/1.0.3-preview.1/ImportMaterials/Textures/";

            var cube = new CubeSample();

            cube.size      = 7;
            cube.transform = Matrix4x4.identity;

            //
            // Setup Material.
            //
            var material = new MaterialSample();

            material.surface.SetConnectedPath(shaderPath, "outputs:out");

            // Various shader keywords are required to enable the standard shader to work as intended,
            // while these can be encoded as part of the schema, often they require some logic (e.g. is
            // emission color != black?).
            material.requiredKeywords = new string[] { "_EMISSION" };

            //
            // Setup Shader.
            //
            var shader = new StandardShaderSample();

            shader.id = new pxr.TfToken("Unity.Standard");

            // Note that USD requires all connections target attributes, hence "outputs:out" appended to
            // the paths below. Think of this as a shader network graph, where a texture object could
            // be executed per pixel with a sampler,  "outputs:out" would be the sampled color.

            shader.smoothnessScale.defaultValue = 0.99f;

            shader.enableSpecularHighlights.defaultValue = true;
            shader.enableGlossyReflections.defaultValue  = true;

            shader.albedo.defaultValue = Color.white;
            shader.albedoMap.SetConnectedPath(albedoMapPath, "outputs:out");

            shader.normalMapScale.defaultValue = 0.76f;
            shader.normalMap.SetConnectedPath(normalMapPath, "outputs:out");

            shader.emission.defaultValue = Color.white * 0.3f;
            shader.emissionMap.SetConnectedPath(emissionMapPath, "outputs:out");

            shader.metallicMap.SetConnectedPath(metallicMapPath, "outputs:out");

            shader.occlusionMapScale.defaultValue = 0.65f;
            shader.occlusionMap.SetConnectedPath(occlusionMapPath, "outputs:out");

            shader.parallaxMapScale.defaultValue = 0.1f;
            shader.parallaxMap.SetConnectedPath(parallaxMapPath, "outputs:out");

            shader.detailMask.SetConnectedPath(detailMaskPath);
            shader.detailNormalMapScale.defaultValue = .05f;
            shader.detailNormalMap.SetConnectedPath(detailNormalMapPath, "outputs:out");

            //
            // Setup Textures.
            //
            var albedoTexture = new Texture2DSample();

            albedoTexture.sourceFile.defaultValue = textureFilePath + "albedoMap.png";
            albedoTexture.sRgb = true;

            var normalTexture = new Texture2DSample();

            normalTexture.sourceFile.defaultValue = textureFilePath + "normalMap.png";
            normalTexture.sRgb = true;

            var emissionTexture = new Texture2DSample();

            emissionTexture.sourceFile.defaultValue = textureFilePath + "emissionMap.png";
            emissionTexture.sRgb = true;

            var metallicTexture = new Texture2DSample();

            metallicTexture.sourceFile.defaultValue = textureFilePath + "metallicMap.png";
            metallicTexture.sRgb = true;

            var occlusionTexture = new Texture2DSample();

            occlusionTexture.sourceFile.defaultValue = textureFilePath + "occlusionMap.png";
            occlusionTexture.sRgb = true;

            var parallaxTexture = new Texture2DSample();

            parallaxTexture.sourceFile.defaultValue = textureFilePath + "parallaxMap.png";
            parallaxTexture.sRgb = true;

            var detailNormalTexture = new Texture2DSample();

            detailNormalTexture.sourceFile.defaultValue = textureFilePath + "detailMap.png";
            detailNormalTexture.sRgb = true;

            var detailMaskTexture = new Texture2DSample();

            detailMaskTexture.sourceFile.defaultValue = textureFilePath + "metallicMap.png";
            detailMaskTexture.sRgb = true;

            //
            // Write Everything.
            //
            scene.Write(cubePath, cube);
            scene.Write(materialPath, material);
            scene.Write(shaderPath, shader);

            scene.Write(albedoMapPath, albedoTexture);
            scene.Write(normalMapPath, normalTexture);
            scene.Write(emissionMapPath, emissionTexture);
            scene.Write(metallicMapPath, metallicTexture);
            scene.Write(occlusionMapPath, occlusionTexture);
            scene.Write(parallaxMapPath, parallaxTexture);
            scene.Write(detailNormalMapPath, detailNormalTexture);
            scene.Write(detailMaskPath, detailMaskTexture);

            //
            // Bind Material.
            //
            MaterialSample.Bind(scene, cubePath, materialPath);

            //
            // Write out the scene as a string, for debugging.
            //
            string s;

            scene.Stage.ExportToString(out s);
            Debug.Log("Loading:\n" + s);
            return(scene);
        }
        //
        // Start generates a USD scene procedurally, containing a single cube with a material, shader
        // and texture bound. It then inspects the cube to discover the material. A Unity material is
        // constructed and the parameters are copied in a generic way. Similarly, the texture is
        // discovered and loaded as a Unity Texture2D and bound to the material.
        //
        // Also See: https://docs.unity3d.com/Manual/MaterialsAccessingViaScript.html
        //
        void Start()
        {
            // Get the current local path
            m_localPath = PackageUtils.GetCallerRelativeToProjectFolderPath();

            // Create a scene for this test, but could also be read from disk.
            Scene usdScene = CreateSceneWithShading();

            // Read the material and shader ID.
            var    usdMaterial = new MaterialSample();
            string shaderId;

            // ReadMaterial was designed for Unity and assumes there is one "surface" shader bound.
            if (!MaterialSample.ReadMaterial(usdScene, kCubePath, usdMaterial, out shaderId))
            {
                throw new System.Exception("Failed to read material");
            }

            // Map the shader ID to the corresponding Unity/USD shader pair.
            ShaderPair shader;

            if (shaderId == null || !m_shaderMap.TryGetValue(shaderId, out shader))
            {
                throw new System.Exception("Material had no surface bound");
            }

            //
            // Read and process the shader-specific parameters.
            //

            // UsdShade requires all connections target an attribute, but we actually want to deserialize
            // the entire prim, so we get just the prim path here.
            var shaderPath = new pxr.SdfPath(usdMaterial.surface.connectedPath).GetPrimPath();

            usdScene.Read(shaderPath, shader.usdShader);

            //
            // Construct material & process the inputs, textures, and keywords.
            //

            var mat = new UnityEngine.Material(shader.unityShader);

            // Apply material keywords.
            foreach (string keyword in usdMaterial.requiredKeywords ?? new string[0])
            {
                mat.EnableKeyword(keyword);
            }

            // Iterate over all input parameters and copy values and/or construct textures.
            foreach (var param in shader.usdShader.GetInputParameters())
            {
                if (!SetMaterialParameter(mat, param.unityName, param.value))
                {
                    throw new System.Exception("Incompatible shader data type: " + param.ToString());
                }
            }

            foreach (var param in shader.usdShader.GetInputTextures())
            {
                if (string.IsNullOrEmpty(param.connectedPath))
                {
                    // Not connected to a texture.
                    continue;
                }

                // Only 2D textures are supported in this example.
                var usdTexture = new Texture2DSample();

                // Again, we want the prim path, not the attribute path.
                var texturePath = new pxr.SdfPath(param.connectedPath).GetPrimPath();
                usdScene.Read(texturePath, usdTexture);

                // This example also only supports explicit sourceFiles, they cannot be connected.
                if (string.IsNullOrEmpty(usdTexture.sourceFile.defaultValue))
                {
                    continue;
                }

                // For details, see: https://docs.unity3d.com/Manual/MaterialsAccessingViaScript.html
                foreach (string keyword in param.requiredShaderKeywords)
                {
                    mat.EnableKeyword(keyword);
                }

                var data     = System.IO.File.ReadAllBytes(usdTexture.sourceFile.defaultValue);
                var unityTex = new Texture2D(2, 2);
                unityTex.LoadImage(data);
                mat.SetTexture(param.unityName, unityTex);
                Debug.Log("Set " + param.unityName + " to " + usdTexture.sourceFile.defaultValue);

                unityTex.Apply(updateMipmaps: true, makeNoLongerReadable: false);
            }

            //
            // Create and bind the geometry.
            //

            // Create a cube and set the material.
            // Note that geometry is handled minimally here and is incomplete.
            var cubeSample = new CubeSample();

            usdScene.Read(kCubePath, cubeSample);

            var go = GameObject.CreatePrimitive(PrimitiveType.Cube);

            go.transform.SetParent(transform, worldPositionStays: false);
            go.transform.localScale = Vector3.one * (float)cubeSample.size;
            m_cube = transform;

            go.GetComponent <MeshRenderer>().material = mat;
        }
Пример #3
0
        public static void ReadWriteTest()
        {
            // Game plan:
            //   1. Create a cube
            //   2. Create a material
            //   3. Create a shader
            //   4. Create a texture
            //   5. Connect the material to the shader's output
            //   6. Connect the shader's albedo parameter to the texture's output
            //   7. Connect the texture to the source file on disk
            //   8. Write all values
            //   9. Bind the cube to the material
            var scene = Scene.Create();

            var cubePath          = "/Model/Geom/Cube";
            var materialPath      = "/Model/Materials/SimpleMat";
            var shaderPath        = "/Model/Materials/SimpleMat/PreviewMaterial";
            var texturePath       = "/Model/Materials/SimpleMat/TextureReader";
            var primvarReaderPath = "/Model/Materials/SimpleMat/UvReader";

            var cube = new CubeSample();

            cube.size = 1;

            var material = new MaterialSample();

            material.surface.SetConnectedPath(shaderPath, "outputs:result");

            var shader = new PreviewSurfaceSample();

            shader.diffuseColor.defaultValue = Vector3.one;
            shader.diffuseColor.SetConnectedPath(texturePath, "outputs:rgb");

            var texture = new TextureReaderSample();

            texture.file.defaultValue = new SdfAssetPath(@"C:\A\Bogus\Texture\Path.png");

            var primvarReader = new PrimvarReaderSample <Vector2>();

            primvarReader.varname.defaultValue = new TfToken("st");

            scene.Write("/Model", new XformSample());
            scene.Write("/Model/Geom", new XformSample());
            scene.Write("/Model/Materials", new XformSample());
            scene.Write(cubePath, cube);
            scene.Write(materialPath, material);
            scene.Write(shaderPath, shader);
            scene.Write(texturePath, texture);
            scene.Write(primvarReaderPath, primvarReader);

            MaterialSample.Bind(scene, cubePath, materialPath);

            var material2      = new MaterialSample();
            var shader2        = new PreviewSurfaceSample();
            var texture2       = new TextureReaderSample();
            var primvarReader2 = new PrimvarReaderSample <Vector2>();

            scene.Read(materialPath, material2);
            scene.Read(shaderPath, shader2);
            scene.Read(texturePath, texture2);
            scene.Read(primvarReaderPath, primvarReader2);

            var param = shader2.GetInputParameters().First();

            AssertEqual(shader.diffuseColor.connectedPath, param.connectedPath);
            AssertEqual("diffuseColor", param.usdName);
            AssertEqual(shader.diffuseColor.defaultValue, param.value);
            AssertEqual("_DiffuseColor", param.unityName);

            AssertEqual(material.surface.defaultValue, material2.surface.defaultValue);
            AssertEqual(material.surface.connectedPath, material2.surface.connectedPath);
            AssertEqual(shader.diffuseColor.defaultValue, shader2.diffuseColor.defaultValue);
            AssertEqual(shader.diffuseColor.connectedPath, shader2.diffuseColor.connectedPath);
            AssertEqual(shader.id, shader2.id);
            AssertEqual(texture.file.defaultValue, texture2.file.defaultValue);
            AssertEqual(primvarReader.varname.defaultValue, primvarReader.varname.defaultValue);

            PrintScene(scene);
        }
Пример #4
0
        /// <summary>
        /// Builds a Unity Material from the given USD material sample.
        /// </summary>
        public static Material BuildMaterial(Scene scene,
                                             string materialPath,
                                             MaterialSample sample,
                                             SceneImportOptions options)
        {
            if (string.IsNullOrEmpty(sample.surface.connectedPath))
            {
                Debug.LogWarning("Material has no connected surface: <" + materialPath + ">");
                return(null);
            }
            var previewSurf = new UnityPreviewSurfaceSample();

            scene.Read(new pxr.SdfPath(sample.surface.connectedPath).GetPrimPath(), previewSurf);

            // Currently, only UsdPreviewSurface is supported.
            if (previewSurf.id == null || previewSurf.id != "UsdPreviewSurface")
            {
                Debug.LogWarning("Unknown surface type: <" + sample.surface.connectedPath + ">"
                                 + "Surface ID: " + previewSurf.id);
                return(null);
            }

            Material mat = null;

            if (options.materialMap.useOriginalShaderIfAvailable && !string.IsNullOrEmpty(previewSurf.unity.shaderName))
            {
                // We may or may not have the original shader.
                var shader = Shader.Find(previewSurf.unity.shaderName);
                if (shader)
                {
                    mat = new Material(shader);
                    mat.shaderKeywords = previewSurf.unity.shaderKeywords;
                }
                else
                {
                    Debug.LogWarning("Original shader not found: " + previewSurf.unity.shaderName);
                }
            }

            if (mat == null)
            {
                if (previewSurf.useSpecularWorkflow.defaultValue == 1)
                {
                    // Metallic workflow.
                    mat = Material.Instantiate(options.materialMap.SpecularWorkflowMaterial);
                }
                else
                {
                    // Metallic workflow.
                    mat = Material.Instantiate(options.materialMap.MetallicWorkflowMaterial);
                }
            }

            foreach (var kvp in previewSurf.unity.colorArgs)
            {
                mat.SetColor(kvp.Key, kvp.Value);
            }

            foreach (var kvp in previewSurf.unity.floatArgs)
            {
                mat.SetFloat(kvp.Key, kvp.Value);
            }

            foreach (var kvp in previewSurf.unity.vectorArgs)
            {
                mat.SetVector(kvp.Key, kvp.Value);
            }

            var pipeline = UnityEngine.Rendering.GraphicsSettings.renderPipelineAsset;

            if (!pipeline)
            {
                var matAdapter = new StandardShaderImporter(mat);
                matAdapter.ImportParametersFromUsd(scene, materialPath, sample, previewSurf, options);
                matAdapter.ImportFromUsd();
            }
            else if (pipeline.GetType().Name == "HDRenderPipelineAsset")
            {
                // Robustness: Comparing a strng ^ here is not great, but there is no other option.
                var matAdapter = new HdrpShaderImporter(mat);
                matAdapter.ImportParametersFromUsd(scene, materialPath, sample, previewSurf, options);
                matAdapter.ImportFromUsd();
            }
            else
            {
                // Fallback to the Standard importer, which may pickup some attributes by luck.
                var matAdapter = new StandardShaderImporter(mat);
                matAdapter.ImportParametersFromUsd(scene, materialPath, sample, previewSurf, options);
                matAdapter.ImportFromUsd();
            }

            return(mat);
        }
Пример #5
0
        static void ExportMesh(ObjectContext objContext,
                               ExportContext exportContext,
                               Mesh mesh,
                               Material sharedMaterial,
                               Material[] sharedMaterials,
                               bool exportMeshPose = true)
        {
            if (mesh.isReadable == false)
            {
                Debug.LogWarning("Mesh not readable: " + objContext.path);
                return;
            }
            string path = objContext.path;

            if (mesh == null)
            {
                Debug.LogWarning("Null mesh for: " + path);
                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 ahve 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]);
                        }
                    }

                    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 materialBindToken = new pxr.TfToken("materialBind");
                        var vtIndices         = UnityTypeConverter.ToVtArray(faceIndices);
                        var subset            = pxr.UsdGeomSubset.CreateUniqueGeomSubset(
                            usdGeomMesh,            // The object of which this subset belongs.
                            "subMeshes",            // An arbitrary name for the subset.
                            pxr.UsdGeomTokens.face, // Indicator that these represent face indices
                            vtIndices,              // The actual face indices.
                            materialBindToken       // familyName = "materialBind"
                            );

                        if (exportContext.exportMaterials)
                        {
                            if (si >= sharedMaterials.Length || !exportContext.matMap.TryGetValue(sharedMaterials[si], out usdMaterialPath))
                            {
                                Debug.LogError("Invalid material bound for: " + path);
                            }
                            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();
            }
        }