/// <summary> /// Copy mesh data from USD to Unity with the given import options. /// </summary> public static void BuildMesh(string path, MeshSample usdMesh, GeometrySubsets geomSubsets, GameObject go, SceneImportOptions options, bool isDynamic) { var mf = ImporterBase.GetOrAddComponent <MeshFilter>(go); var mr = ImporterBase.GetOrAddComponent <MeshRenderer>(go); if (mf.sharedMesh == null) { mf.sharedMesh = new Mesh { name = UniqueMeshName(go.name) }; } // We only check if a mesh is dynamic when scene.IsPopulatingAccessMask is True. It only happens when a playable is // created, potentially way after mesh creation. if (isDynamic) { mf.sharedMesh.MarkDynamic(); } BuildMesh_(path, usdMesh, mf.sharedMesh, geomSubsets, go, mr, options); }
private void UpdateArcs() { FingerArc.UpdateArcLights(); int maxActiveArcs = surfaceArcs.Length - numFingersEngaged; for (int i = 0; i < surfaceArcs.Length; i++) { surfaceArcs[i].gameObject.SetActive(i < maxActiveArcs); } if (Random.value < randomSurfaceArcChange) { SurfaceArc arc = surfaceArcs[Random.Range(0, surfaceArcs.Length)]; if (Random.value > 0.5f) { arc.gameObject.SetActive(true); MeshSample point1 = sampler.Samples[Random.Range(0, sampler.Samples.Length)]; MeshSample point2 = sampler.Samples[Random.Range(0, sampler.Samples.Length)]; arc.SetArc(point1, point2, transform.position, SurfaceRadius); } else { arc.gameObject.SetActive(false); } } }
public override void Initialize(Vector3 surfacePosition) { base.Initialize(surfacePosition); sampler.SampleMesh(false); meshBounds = sampler.Bounds; boids = new Boid[numBoids]; surfaceBoids = new SurfaceBoid[numBoids]; boidMatrices = new Matrix4x4[numBoids]; for (int i = 0; i < numBoids; i++) { Boid b = new Boid(); MeshSample s = sampler.Samples[Random.Range(0, sampler.Samples.Length)]; b.Position = s.UV; b.Rotation = Random.Range(0, 360); boids[i] = b; SurfaceBoid sb = new SurfaceBoid(); sb.Offset = Random.value; surfaceBoids[i] = sb; } forces = new Force[fingers.Length]; }
public override void Initialize(Vector3 surfacePosition) { base.Initialize(surfacePosition); emptyBlock = new MaterialPropertyBlock(); sampler.SampleMesh(); blorbs = new Blorb[sampler.Samples.Length]; matrixes = new Matrix4x4[sampler.Samples.Length]; fingerPositions = new Vector3[fingers.Length]; for (int i = 0; i < sampler.Samples.Length; i++) { MeshSample sample = sampler.Samples[i]; Blorb e = new Blorb(); e.Point = sample.Point; e.Dir = e.Point.normalized; e.Radius = Random.Range(minRadius, maxRadius); e.Agitation = Random.value; e.Goop = GameObject.Instantiate(goopBlorbPrefabs[Random.Range(0, goopBlorbPrefabs.Length)], transform).GetComponent <GoopBlorb>(); e.Rotation = Random.Range(0, 360); e.RotationSpeed = Random.Range(-rotationSpeed, rotationSpeed); blorbs[i] = e; } timeLastPopped = -100; updateBlorbs = true; Task updateTask = UpdateBlorbsLoop(); }
public void Import(string filename) { Scene scene = Scene.Open(filename); CommandGroup group = new CommandGroup(); var cameras = scene.ReadAll <CameraControllerSample>(); foreach (var camera in cameras) { CameraControllerSample sample = camera.sample; GameObject cameraPrefab = ResourceManager.GetPrefab(PrefabID.Camera); GameObject instance = SceneManager.InstantiateUnityPrefab(cameraPrefab); GameObject newObject = SceneManager.AddObject(instance); newObject.name = camera.path.GetName(); sample.CopyToCamera(newObject.GetComponent <CameraController>()); Maths.DecomposeMatrix(sample.transform, out Vector3 position, out Quaternion rotation, out Vector3 scale); newObject.transform.localPosition = position; newObject.transform.localRotation = rotation; newObject.transform.localScale = scale; } foreach (var m in scene.ReadAll <MeshSample>()) { MeshSample sample = m.sample; GameObject gobject = new GameObject(); gobject.name = m.path.GetName(); Maths.DecomposeMatrix(sample.transform, out Vector3 position, out Quaternion rotation, out Vector3 scale); gobject.transform.localPosition = position; gobject.transform.localRotation = rotation; gobject.transform.localScale = scale; Mesh mesh = new Mesh(); mesh.SetVertices(sample.points); mesh.SetNormals(sample.normals); mesh.SetUVs(0, sample.st as Vector2[]); mesh.SetTriangles(sample.faceVertexIndices, 0); MeshFilter meshFilter = gobject.AddComponent <MeshFilter>(); meshFilter.sharedMesh = mesh; MeshRenderer mr = gobject.AddComponent <MeshRenderer>(); mr.sharedMaterial = ResourceManager.GetMaterial(MaterialID.ObjectOpaque); gobject.AddComponent <MeshCollider>(); SceneManager.AddObject(gobject); } group.Submit(); scene.Close(); }
public void SetArc(MeshSample point1, MeshSample point2, Vector3 gravityOrigin, float surfaceRadius) { this.point1.position = point1.Point; this.point2.position = point2.Point; this.point1.forward = point1.Normal; this.point2.forward = point2.Normal; this.gravityOrigin = gravityOrigin; this.surfaceRadius = surfaceRadius; timeLastSet = Time.time; randomWidth = Random.Range(minWidth, maxWidth); }
void InitExportableObjects(GameObject go) { SampleBase sample = null; ExportFunction exportFunc = null; if (go.GetComponent <MeshFilter>() != null && go.GetComponent <MeshRenderer>() != null) { sample = new MeshSample(); exportFunc = ExportMesh; foreach (var mat in go.GetComponent <MeshRenderer>().materials) { if (!m_materialMap.ContainsKey(mat)) { string usdPath = "/World/Materials/" + pxr.UsdCs.TfMakeValidIdentifier(mat.name); m_materialMap.Add(mat, usdPath); } } } else if (go.GetComponent <Camera>()) { sample = new CameraSample(); exportFunc = ExportCamera; } else { return; } // This is an exportable object. string path = Unity.UnityTypeConverter.GetPath(go.transform); m_primMap.Add(go, new ExportPlan { path = path, sample = sample, exportFunc = exportFunc }); Debug.Log(path + " " + sample.GetType().Name); // Include the parent xform hierarchy. // Note that the parent hierarchy is memoised, so despite looking expensive, the time // complexity is linear. Transform xf = go.transform.parent; while (xf) { if (!InitExportableParents(xf.gameObject)) { break; } xf = xf.parent; } }
private void UpdateFingers() { numFingersEngaged = 0; for (int i = 0; i < fingers.Length; i++) { Transform finger = fingers[i]; FingerArc arc = fingerArcs[i]; if (!finger.gameObject.activeSelf) { arc.gameObject.SetActive(false); continue; } if (arc.gameObject.activeSelf) { // See if we're too far away MeshSample sample = sampler.Samples[arc.Point1SampleIndex]; if (Vector3.Distance(sample.Point, finger.position) > fingerDisengageDistance) { // If we are, disable the arc and move on arc.gameObject.SetActive(false); continue; } else { // If we aren't, see if it's time to zap to a different position if (Random.value < randomFingerArcChange) { // Get the closest point on the sphere MeshSample point1 = sampler.ClosestSample(finger.position); // Then get a random sample somewhere nearby point1 = sampler.RandomSample(point1.Point, fingerRandomPosRadius); arc.SetArc(point1, fingerArcSources[i]); } numFingersEngaged++; } } else { // See if we're close enough to any samples to start // Get the closest point on the sphere MeshSample point1 = sampler.ClosestSample(finger.position); if (Vector3.Distance(point1.Point, finger.position) < fingerEngageDistance) { // Then get a random sample somewhere nearby point1 = sampler.RandomSample(point1.Point, fingerRandomPosRadius); arc.gameObject.SetActive(true); arc.SetArc(point1, fingerArcSources[i]); numFingersEngaged++; } } } }
public override void Initialize(Vector3 surfacePosition) { base.Initialize(surfacePosition); sampler.SampleMesh(true); for (int i = 0; i < surfaceArcs.Length; i++) { MeshSample point1 = sampler.Samples[Random.Range(0, sampler.Samples.Length)]; MeshSample point2 = sampler.Samples[Random.Range(0, sampler.Samples.Length)]; surfaceArcs[i].SetArc(point1, point2, Vector3.zero, SurfaceRadius); } buzzAudio.pitch = 0; }
/// <summary> /// Copy mesh data from USD to Unity with the given import options, setup for skinning. /// </summary> public static void BuildSkinnedMesh(string path, MeshSample usdMesh, GeometrySubsets geomSubsets, GameObject go, SceneImportOptions options) { var smr = ImporterBase.GetOrAddComponent <SkinnedMeshRenderer>(go); if (smr.sharedMesh == null) { smr.sharedMesh = new Mesh(); } BuildMesh_(path, usdMesh, smr.sharedMesh, geomSubsets, go, smr, options); }
internal MeshSample RandomSample(Vector3 point, float searchRadius) { float sqrSearchRadius = searchRadius * searchRadius; List <int> samplesInRange = new List <int>(); for (int i = 0; i < samples.Length; i++) { MeshSample sample = samples[i]; float sqrDist = (sample.Point - point).sqrMagnitude; if (sqrDist < sqrSearchRadius) { samplesInRange.Add(i); } } return(samples[samplesInRange[Random.Range(0, samplesInRange.Count)]]); }
internal MeshSample ClosestSample(Vector3 point) { float closestDistSoFar = Mathf.Infinity; int closestIndex = 0; for (int i = 0; i < samples.Length; i++) { MeshSample sample = samples[i]; float sqrDist = (sample.Point - point).sqrMagnitude; if (sqrDist < closestDistSoFar) { closestDistSoFar = sqrDist; closestIndex = i; } } return(samples[closestIndex]); }
public void SetArc(MeshSample point1, Transform fingerTarget) { Point1SampleIndex = point1.Index; this.point1.position = point1.Point; this.point1.forward = point1.Normal; this.fingerTarget = fingerTarget; this.point2.position = fingerTarget.position; this.point2.forward = fingerTarget.forward; randomWidth = Random.Range(minWidth, maxWidth); fingerAudio.pitch = Random.Range(0.7f, 1.3f); timeLastSet = Time.time; }
private void UpdateGoopSlime() { for (int i = 0; i < fingers.Length; i++) { if (!fingers[i].gameObject.activeSelf) { goopSlimes[i].gameObject.SetActive(false); continue; } if (Vector3.Distance(fingers[i].position, transform.position) < SurfaceRadius + goopDistance) { MeshSample closestSample = sampler.ClosestSample(fingers[i].localPosition); goopSlimes[i].SetGoop(closestSample, fingers[i]); } } }
/// Converts a GeometryPool into a MeshSample. static void GetMeshSample(GeometryPool geomPool, Matrix4x4 mat44, MeshSample sample) { var vertexLayout = geomPool.Layout; // Used for shader binding. sample.doubleSided = true; sample.orientation = USD.NET.Orientation.LeftHanded; sample.points = geomPool.m_Vertices.ToArray(); sample.faceVertexIndices = geomPool.m_Tris.ToArray(); sample.transform = mat44; sample.extent = new Bounds(sample.points[0], Vector3.zero); for (int i = 0; i < sample.points.Length; i++) { sample.extent.Encapsulate(sample.points[i]); } // Yuck. Perhaps push this down to a lower layer. sample.faceVertexCounts = new int[sample.faceVertexIndices.Length / 3]; for (int i = 0; i < sample.faceVertexCounts.Length; i++) { sample.faceVertexCounts[i] = 3; } if (vertexLayout.bUseNormals) { sample.normals = geomPool.m_Normals.ToArray(); } if (vertexLayout.bUseTangents) { sample.tangents = geomPool.m_Tangents.ToArray(); } if (vertexLayout.bUseColors) { sample.colors = geomPool.m_Colors.Select( c => (new Color(c.r, c.g, c.b, c.a) / 255.0f).linear).ToArray(); } sample.uv = GetUv(0, vertexLayout.texcoord0.size, geomPool); sample.uv2 = GetUv(1, vertexLayout.texcoord1.size, geomPool); }
MeshSample SampleRandomPoint(int index, bool transformPoint = false) { MeshSample sample = default(MeshSample); sample.Index = index; sample.TriangleIndex = GetRandomTriangleIndex(); Vector3 BC = Random.insideUnitSphere.normalized; BC.x = Random.value; BC.y = 1f - BC.x; BC.z = 1f - BC.x - BC.z; sample.BarycentricCoordinate = BC; Vector3 P1 = verts[tris[sample.TriangleIndex + 0]]; Vector3 P2 = verts[tris[sample.TriangleIndex + 1]]; Vector3 P3 = verts[tris[sample.TriangleIndex + 2]]; Vector2 UV1 = uvs[tris[sample.TriangleIndex + 0]]; Vector2 UV2 = uvs[tris[sample.TriangleIndex + 0]]; Vector2 UV3 = uvs[tris[sample.TriangleIndex + 0]]; UV1 = Vector2.Lerp(UV1, UV2, BC.x); UV2 = Vector2.Lerp(UV2, UV3, BC.y); P1 = Vector3.Lerp(P1, P2, BC.x); P1 = Vector3.Lerp(P1, P3, BC.y); sample.Point = P1; sample.UV = UV1; if (transformPoint) { sample.Point = transform.TransformPoint(sample.Point); } // Interpolated vertex normal Vector3 N1 = normals[tris[sample.TriangleIndex + 0]]; Vector3 N2 = normals[tris[sample.TriangleIndex + 1]]; Vector3 N3 = normals[tris[sample.TriangleIndex + 2]]; N1 = Vector3.Lerp(N1, N2, BC.x); N1 = Vector3.Lerp(N1, N3, BC.y); sample.Normal = N1; return(sample); }
public void SetGoop(MeshSample origin, Transform target) { this.target = target; this.origin = origin; if (droop == null) { droop = new float[lineRenderer.positionCount]; normalizedTimes = new float[lineRenderer.positionCount]; follow = new float[lineRenderer.positionCount]; for (int i = 0; i < lineRenderer.positionCount; i++) { normalizedTimes[i] = (float)i / (lineRenderer.positionCount - 1); droop[i] = droopCurve.Evaluate(normalizedTimes[i]); follow[i] = followCurve.Evaluate(normalizedTimes[i]); } } if (positions.Length != lineRenderer.positionCount) { positions = new Vector3[lineRenderer.positionCount]; targetPositions = new Vector3[lineRenderer.positionCount]; prevPositions = new Vector3[lineRenderer.positionCount]; inertialVelocities = new Vector3[lineRenderer.positionCount]; } for (int i = 0; i < lineRenderer.positionCount; i++) { lineRenderer.SetPosition(i, origin.Point); positions[i] = transform.TransformPoint(origin.Point); targetPositions[i] = transform.TransformPoint(origin.Point); prevPositions[i] = transform.TransformPoint(origin.Point); inertialVelocities[i] = Vector3.zero; } gameObject.SetActive(true); }
private Vector2 ProjectFingerPosition(Vector3 position) { MeshSample sample = sampler.ClosestSample(surfaceTransform.InverseTransformPoint(position)); return(sample.UV); }
public static void BasicTest() { var cubeSample = new CubeSample(); var meshSample = new MeshSample(); meshSample.visibility = Visibility.Invisible; var scene = Scene.Create(); scene.Write("/Root/Cube", cubeSample); scene.Write("/Root/Mesh", meshSample); scene.Write("/Root/Mesh2", meshSample); foreach (var mesh in scene.ReadAll <XformableQuery>(rootPath:"/Root")) { Console.WriteLine("ReadAll Test: " + mesh.path); } foreach (var path in scene.Find <XformableQuery>(rootPath: "/Root")) { Console.WriteLine("Find Test: " + path); } try { Util.DiagnosticHandler.Instance.LastError = null; foreach (var mesh in scene.ReadAll <XformableQuery>(rootPath: "/Bogus/Root/Path")) { Console.WriteLine("Query Test: " + mesh.path); } // TODO: for some reason, these stop working after the first exception is thrown, // but it seems like *only* this code path is affected. if (!string.IsNullOrEmpty(Util.DiagnosticHandler.Instance.LastError)) { throw new ApplicationException(Util.DiagnosticHandler.Instance.LastError); } else { throw new Exception("Expected exception but was not thrown."); } } catch (ApplicationException ex) { Console.WriteLine("Caught expected exception: " + ex.Message); } try { Util.DiagnosticHandler.Instance.LastError = null; foreach (var mesh in scene.ReadAll <BadBaseTypeQuery>(rootPath: "/Root")) { Console.WriteLine("Query Test: " + mesh.path); } // TODO: for some reason, these stop working after the first exception is thrown, // but it seems like *only* this code path is affected. if (!string.IsNullOrEmpty(Util.DiagnosticHandler.Instance.LastError)) { throw new ApplicationException(Util.DiagnosticHandler.Instance.LastError); } else { throw new Exception("Expected exception but was not thrown."); } } catch (ApplicationException ex) { Console.WriteLine("Caught expected exception: " + ex.Message); } }
// -------------------------------------------------------------------------------------------- // // Export Logic // -------------------------------------------------------------------------------------------- // /// Exports either all brush strokes or the given selection to the specified file. static public void ExportPayload(string outputFile) { // Would be nice to find a way to kick this off automatically. // Redundant calls are ignored. if (!InitUsd.Initialize()) { return; } // Unity is left handed (DX), USD is right handed (GL) var payload = ExportCollector.GetExportPayload(AxisConvention.kUsd); var brushCatalog = BrushCatalog.m_Instance; // The Scene object provids serialization methods arbitrary C# objects to USD. USD.NET.Scene scene = USD.NET.Scene.Create(outputFile); // The target time at which samples will be written. // // In this case, all data is being written to the "default" time, which means it can be // overridden by animated values later. scene.Time = null; // Bracketing times to specify the valid animation range. scene.StartTime = 1.0; scene.EndTime = 1.0; const string kGeomName = "/Geom"; const string kCurvesName = "/Curves"; string path = ""; AddSketchRoot(scene, GetSketchPath()); // Create: </Sketch> CreateXform(scene, GetStrokesPath()); // Create: </Sketch/Strokes> CreateXform(scene, GetModelsPath()); // Create: </Sketch/Models> // Main export loop. try { foreach (ExportUtils.GroupPayload group in payload.groups) { // Example: </Sketch/Strokes/Group_0> path = GetGroupPath(group.id); CreateXform(scene, path); // Example: </Sketch/Strokes/Group_0/Geom> CreateXform(scene, path + kGeomName); // Example: </Sketch/Strokes/Group_0/Curves> CreateXform(scene, path + kCurvesName); int iBrushMeshPayload = -1; foreach (var brushMeshPayload in group.brushMeshes) { ++iBrushMeshPayload; // Conditionally moves Normal into Texcoord1 so that the normal semantic is respected. // This only has an effect when layout.bFbxExportNormalAsTexcoord1 == true. // Note that this modifies the GeometryPool in place. FbxUtils.ApplyFbxTexcoordHack(brushMeshPayload.geometry); // Brushes are expected to be batched by type/GUID. Guid brushGuid = brushMeshPayload.strokes[0].m_BrushGuid; string brushName = "/" + SanitizeIdentifier(brushCatalog.GetBrush(brushGuid).DurableName) + "_"; // Example: </Sketch/Strokes/Group_0/Geom/Marker_0> string meshPath = path + kGeomName + brushName; // Example: </Sketch/Strokes/Group_0/Curves/Marker_0> string curvePath = path + kCurvesName + brushName; var geomPool = brushMeshPayload.geometry; var strokes = brushMeshPayload.strokes; var mat44 = Matrix4x4.identity; var meshPrimPath = new pxr.SdfPath(meshPath + iBrushMeshPayload.ToString()); var curvesPrimPath = new pxr.SdfPath(curvePath + iBrushMeshPayload.ToString()); // // Geometry // BrushSample brushSample = GetBrushSample(geomPool, strokes, mat44); // Write the BrushSample to the same point in the scenegraph at which it exists in Tilt // Brush. Notice this method is Async, it is queued to a background thread to perform I/O // which means it is not safe to read from the scene until WaitForWrites() is called. scene.Write(meshPrimPath, brushSample); // // Stroke Curves // var curvesSample = GetCurvesSample(payload, strokes, Matrix4x4.identity); scene.Write(curvesPrimPath, curvesSample); // // Materials // double?oldTime = scene.Time; scene.Time = null; string materialPath = CreateMaterialNetwork( scene, brushMeshPayload.exportableMaterial, GetStrokesPath()); BindMaterial(scene, meshPrimPath.ToString(), materialPath); BindMaterial(scene, curvesPrimPath.ToString(), materialPath); scene.Time = oldTime; } } // // Models // var knownModels = new Dictionary <Model, string>(); int iModelMeshPayload = -1; foreach (var modelMeshPayload in payload.modelMeshes) { ++iModelMeshPayload; var modelId = modelMeshPayload.modelId; var modelNamePrefix = "/Model_" + SanitizeIdentifier(modelMeshPayload.model.GetExportName()) + "_"; var modelName = modelNamePrefix + modelId; var xf = modelMeshPayload.xform; // Geometry pools may be repeated and should be turned into references. var geomPool = modelMeshPayload.geometry; var modelRootPath = new pxr.SdfPath(GetModelsPath() + modelName); // Example: </Sketch/Models/Model_Andy_0> CreateXform(scene, modelRootPath, xf); // Example: </Sketch/Models/Model_Andy_0/Geom> CreateXform(scene, modelRootPath + kGeomName); string modelPathToReference; if (knownModels.TryGetValue(modelMeshPayload.model, out modelPathToReference) && modelPathToReference != modelRootPath) { // Create an Xform, note that the world transform here will override the referenced model. var meshXf = new MeshXformSample(); meshXf.transform = xf; scene.Write(modelRootPath, meshXf); // Add a USD reference to previously created model. var prim = scene.Stage.GetPrimAtPath(modelRootPath); prim.GetReferences().AddReference("", new pxr.SdfPath(modelPathToReference)); continue; } // Example: </Sketch/Models/Geom/Model_Andy_0/Mesh_0> path = modelRootPath + kGeomName + "/Mesh_" + iModelMeshPayload.ToString(); var meshPrimPath = new pxr.SdfPath(path); var meshSample = new MeshSample(); GetMeshSample(geomPool, Matrix4x4.identity, meshSample); scene.Write(path, meshSample); scene.Stage.GetPrimAtPath(new pxr.SdfPath(path)).SetInstanceable(true); // // Materials // // Author at default time. double?oldTime = scene.Time; scene.Time = null; // Model materials must live under the model root, since we will reference the model. string materialPath = CreateMaterialNetwork( scene, modelMeshPayload.exportableMaterial, modelRootPath); BindMaterial(scene, meshPrimPath.ToString(), materialPath); // Continue authoring at the desired time index. scene.Time = oldTime; // // Setup to be referenced. // if (!knownModels.ContainsKey(modelMeshPayload.model)) { knownModels.Add(modelMeshPayload.model, modelRootPath); } } } catch { scene.Save(); scene.Close(); throw; } // Save will force a sync with all async reads and writes. scene.Save(); scene.Close(); }
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 }