// // 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() { // Create a scene for this test, but could also be read from disk. USD.NET.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; }
public static GameObject Import( USD.NET.Scene scene, GameObject rootObj, Dictionary <SdfPath, GameObject> objectMap, UpdateMask mask, out List <string> warnings, List <string> pathsToUpdate = null) { // TODO: generalize this to avoid having to dig down into USD for sparse reads. TfToken brushToken = new pxr.TfToken("brush"); TfToken faceVertexIndicesToken = new pxr.TfToken("faceVertexIndices"); warnings = new List <string>(); // Would be nice to find a way to kick this off automatically. // Redundant calls are ignored. if (!InitUsd.Initialize()) { return(null); } // PLAN: Process any UsdStage either constructing or updating GameObjects as needed. // This should include analysis of the time samples to see what attributes are // actually varying so they are updated minimally. UsdPrimVector prims = null; if (pathsToUpdate == null) { prims = scene.Stage.GetAllPrims(); } else { prims = new UsdPrimVector(); foreach (var path in pathsToUpdate) { prims.Add(scene.Stage.GetPrimAtPath(new pxr.SdfPath(path))); } } for (int p = 0; p < prims.Count; p++) { // TODO: prims[p] generates garbage. UsdPrim usdPrim = prims[p]; UsdGeomMesh usdMesh = new UsdGeomMesh(usdPrim); if (!usdMesh) { continue; } ExportUsd.BrushSample sample = new ExportUsd.BrushSample(); if (mask == UpdateMask.All) { scene.Read(usdPrim.GetPath(), sample); } else { // TODO: Generalize this as a reusable mechanism for sparse reads. if (mask == UpdateMask.Topology) { sample.brush = new Guid((string)usdPrim.GetCustomDataByKey(brushToken)); var fv = usdPrim.GetAttribute(faceVertexIndicesToken).Get(scene.Time); sample.faceVertexIndices = USD.NET.IntrinsicTypeConverter.FromVtArray((VtIntArray)fv); } else { throw new NotImplementedException(); } } GameObject strokeObj; Mesh unityMesh; // // Construct the GameObject if needed. // if (!objectMap.TryGetValue(usdPrim.GetPath(), out strokeObj)) { // On first import, we need to pull in all the data, regardless of what was requested. mask = UpdateMask.All; BrushDescriptor brush = BrushCatalog.m_Instance.GetBrush(sample.brush); if (brush == null) { Debug.LogWarningFormat("Invalid brush GUID at path: <{0}> guid: {1}", usdPrim.GetPath(), sample.brush); continue; } strokeObj = UnityEngine.Object.Instantiate(brush.m_BrushPrefab); // Register the Prim/Object mapping. objectMap.Add(usdPrim.GetPath(), strokeObj); // Init the game object. strokeObj.transform.parent = rootObj.transform; strokeObj.GetComponent <MeshRenderer>().material = brush.Material; strokeObj.GetComponent <MeshFilter>().sharedMesh = new Mesh(); strokeObj.AddComponent <BoxCollider>(); unityMesh = strokeObj.GetComponent <MeshFilter>().sharedMesh; } else { unityMesh = strokeObj.GetComponent <MeshFilter>().sharedMesh; } // // Points // Note that points must come first, before all other mesh data. // if ((mask & UpdateMask.Points) == UpdateMask.Points) { unityMesh.vertices = sample.points; } // // Bounds // if ((mask & UpdateMask.Bounds) == UpdateMask.Bounds) { var bc = strokeObj.GetComponent <BoxCollider>(); bc.center = sample.extent.center; bc.size = sample.extent.size; unityMesh.bounds = bc.bounds; } // // Topology // if ((mask & UpdateMask.Topology) == UpdateMask.Topology) { unityMesh.triangles = sample.faceVertexIndices; } // // Normals // if ((mask & UpdateMask.Normals) == UpdateMask.Normals) { unityMesh.normals = sample.normals; } // // Color & Opacity // if ((mask & UpdateMask.Colors) == UpdateMask.Colors && sample.colors != null) { unityMesh.colors = sample.colors; } // // Tangents // if ((mask & UpdateMask.Tangents) == UpdateMask.Tangents && sample.tangents != null) { unityMesh.tangents = sample.tangents; } // // UVs // if ((mask & UpdateMask.UVs) == UpdateMask.UVs) { SetUv(unityMesh, 0, sample.uv); SetUv(unityMesh, 1, sample.uv2); SetUv(unityMesh, 2, sample.uv3); SetUv(unityMesh, 3, sample.uv4); } } // For each prim return(rootObj); }