public ObiTriangleMeshHandle GetOrCreateTriangleMesh(Mesh source) { ObiTriangleMeshHandle handle = new ObiTriangleMeshHandle(null); if (source != null && !handles.TryGetValue(source, out handle)) { var sourceTris = source.triangles; var sourceVertices = source.vertices; // Build a bounding interval hierarchy from the triangles: IBounded[] t = new IBounded[sourceTris.Length / 3]; for (int i = 0; i < t.Length; ++i) { int t1 = sourceTris[i * 3]; int t2 = sourceTris[i * 3 + 1]; int t3 = sourceTris[i * 3 + 2]; t[i] = new Triangle(t1, t2, t3, sourceVertices[t1], sourceVertices[t2], sourceVertices[t3]); } var sourceBih = BIH.Build(ref t); Triangle[] tris = Array.ConvertAll(t, x => (Triangle)x); handle = new ObiTriangleMeshHandle(source, headers.count); handles.Add(source, handle); headers.Add(new TriangleMeshHeader(bihNodes.count, sourceBih.Length, triangles.count, tris.Length, vertices.count, sourceVertices.Length)); bihNodes.AddRange(sourceBih); triangles.AddRange(tris); vertices.AddRange(sourceVertices); } return(handle); }
public ObiEdgeMeshHandle GetOrCreateEdgeMesh(EdgeCollider2D source) { ObiEdgeMeshHandle handle; if (!handles.TryGetValue(source, out handle)) { Vector2[] sourceVertices = source.points; int[] sourceEdges = new int[source.edgeCount * 2]; for (int i = 0; i < source.edgeCount; ++i) { sourceEdges[i * 2] = i; sourceEdges[i * 2 + 1] = i + 1; } // Build a bounding interval hierarchy from the edges: IBounded[] t = new IBounded[source.edgeCount]; for (int i = 0; i < source.edgeCount; ++i) { t[i] = new Edge(i, i + 1, sourceVertices[i], sourceVertices[i + 1]); } var sourceBih = BIH.Build(ref t); Edge[] edgs = Array.ConvertAll(t, x => (Edge)x); handle = new ObiEdgeMeshHandle(source, headers.count); handles.Add(source, handle); headers.Add(new EdgeMeshHeader(bihNodes.count, sourceBih.Length, edges.count, edgs.Length, vertices.count, sourceVertices.Length)); bihNodes.AddRange(sourceBih); edges.AddRange(edgs); vertices.AddRange(sourceVertices); } return(handle); }
public IEnumerator Generate() { nodes.Clear(); if (mesh == null) yield break; vertices = mesh.vertices; int[] triangles = mesh.triangles; float size = Mathf.Max(mesh.bounds.size.x,mesh.bounds.size.y,mesh.bounds.size.z) + boundsPadding; Bounds bounds = new Bounds(mesh.bounds.center,Vector3.one*size); // Use the half-edge structure to generate angle-weighted normals: HalfEdge he = new HalfEdge(mesh); if (Application.isPlaying){ CoroutineJob.RunSynchronously(he.Generate()); //While playing, do this synchronously. We don't want to wait too much. }else{ CoroutineJob generateHalfEdge = new CoroutineJob(); generateHalfEdge.asyncThreshold = 2000; //If this takes more than 2 seconds in the editor, do it asynchronously. EditorCoroutine.StartCoroutine(generateHalfEdge.Start(he.Generate())); //Wait for the half-edge generation to complete. CoroutineJob.ProgressInfo progress = null; while(!generateHalfEdge.IsDone){ try{ progress = generateHalfEdge.Result as CoroutineJob.ProgressInfo; }catch(Exception e){ Debug.LogException(e); yield break; } yield return progress; } } //Calculate angle weighted normals, for correct inside/outside determination. Vector3[] normals = he.AngleWeightedNormals(); yield return new CoroutineJob.ProgressInfo("Building BIH...",0.1f); // Generate BIH to speed up NN triangle queries. bih = new BIH(); bih.Generate(bounds,vertices,normals,triangles,8,0.8f); // Breadth first construction: Queue<ADFNode> queue = new Queue<ADFNode>(); ADFNode root = new ADFNode(bounds); queue.Enqueue(root); nodes.Add(root); int counter = 0; while(queue.Count > 0) { ADFNode node = queue.Dequeue(); // Here provide an upper-bound estimation of remaining time. Note that in some cases (high maxDepth and high maxError) the process will probably finish sooner than predicted. if (counter % 10 == 0) yield return new CoroutineJob.ProgressInfo("Generating distance field level "+node.depth+"...",0.1f + (node.depth/(float)maxDepth)*0.9f); node.distances = new float[8]; if (highQualityGradient) node.gradients = new Vector3[8]; // Sample distance at the 8 node corners: for (int i = 0; i < 8; i++){ BIH.SurfaceInfo si = bih.DistanceToSurface(node.bounds.center + Vector3.Scale(node.bounds.extents,corners[i])); node.distances[i] = si.signedDistance; if (highQualityGradient) node.gradients[i] = si.vectorToSurface; } if (node.depth >= maxDepth) continue; // Measure distances at the 6 node faces, and the center of the node. float[] realDistances = new float[7]; for (int i = 0; i < 7; i++) realDistances[i] = bih.DistanceToSurface(node.bounds.center + Vector3.Scale(node.bounds.extents,errorSamples[i] * 0.5f)).signedDistance; // Get interpolated estimation of distance at the center of possible child nodes: float[] interpolatedDistances = new float[7]; for (int i = 0; i < 7; i++) interpolatedDistances[i] = node.SampleDistanceAt(node.bounds.center + Vector3.Scale(node.bounds.extents,errorSamples[i] * 0.5f)); // Calculate mean squared error between measured distances and interpolated ones: float mse = 0; for (int i = 0; i < 7; i++){ float d = realDistances[i] - interpolatedDistances[i]; mse += d*d; } mse /= 7f; // If error > threshold, subdivide the node. if (mse > maxError){ node.children = new int[8]; for (int i = 0; i < 8; i++){ // Calculate child bounds and create the node: Vector3 childCenter = node.bounds.center + Vector3.Scale(node.bounds.extents,corners[i] * 0.5f); Vector3 childSize = node.bounds.size*0.5f; ADFNode child = new ADFNode(new Bounds(childCenter,childSize)); child.depth = node.depth+1; // Set our children index. node.children[i] = nodes.Count; // Add it to nodes list and store it for evaluation. nodes.Add(child); queue.Enqueue(child); } } counter++; } // Get rid of octree. bih = null; }
public static IEnumerator Build(float maxError, int maxDepth, Vector3[] vertexPositions, int[] triangleIndices, List <DFNode> nodes) { // Empty vertex or triangle lists, return. if (maxDepth <= 0 || nodes == null || vertexPositions == null || vertexPositions.Length == 0 || triangleIndices == null || triangleIndices.Length == 0) { yield break; } // Build a bounding interval hierarchy from the triangles, to speed up distance queries: IBounded[] t = new IBounded[triangleIndices.Length / 3]; for (int i = 0; i < t.Length; ++i) { int t1 = triangleIndices[i * 3]; int t2 = triangleIndices[i * 3 + 1]; int t3 = triangleIndices[i * 3 + 2]; t[i] = new Triangle(t1, t2, t3, vertexPositions[t1], vertexPositions[t2], vertexPositions[t3]); } var bih = BIH.Build(ref t); // Copy reordered triangles over to a new array: Triangle[] tris = Array.ConvertAll(t, x => (Triangle)x); // Build angle weighted normals, used to determine the sign of the distance field. Vector3[] angleNormals = ObiUtils.CalculateAngleWeightedNormals(vertexPositions, triangleIndices); // Calculate bounding box of the mesh: Bounds bounds = new Bounds(vertexPositions[0], Vector3.zero); for (int i = 1; i < vertexPositions.Length; ++i) { bounds.Encapsulate(vertexPositions[i]); } bounds.Expand(0.1f); // Auxiliar variables to keep track of current tree depth: int depth = 0; int nodesToNextLevel = 1; // Initialize node list: Vector4 center = bounds.center; Vector3 boundsExtents = bounds.extents; center[3] = Mathf.Max(boundsExtents[0], Math.Max(boundsExtents[1], boundsExtents[2])); nodes.Clear(); nodes.Add(new DFNode(center)); var queue = new Queue <int>(); queue.Enqueue(0); while (queue.Count > 0) { // get current node: int index = queue.Dequeue(); var node = nodes[index]; // measure distance at the 8 node corners: for (int i = 0; i < 8; ++i) { Vector4 point = node.center + corners[i] * node.center[3]; point[3] = 0; float distance = BIH.DistanceToSurface(bih, tris, vertexPositions, angleNormals, point); if (i < 4) { node.distancesA[i] = distance; } else { node.distancesB[i - 4] = distance; } } // only subdivide those nodes intersecting the surface: if (depth < maxDepth && Mathf.Abs(BIH.DistanceToSurface(bih, tris, vertexPositions, angleNormals, node.center)) < node.center[3] * sqrt3) { // calculate mean squared error between measured distances and interpolated ones: float mse = 0; for (int i = 0; i < samples.Length; ++i) { Vector4 point = node.center + samples[i] * node.center[3]; float d = BIH.DistanceToSurface(bih, tris, vertexPositions, angleNormals, point) - node.Sample(point); mse += d * d; } mse /= (float)samples.Length; // if error > threshold, subdivide the node: if (mse > maxError) { node.firstChild = nodes.Count; for (int i = 0; i < 8; ++i) { queue.Enqueue(nodes.Count); nodes.Add(new DFNode(node.center + corners[i] * node.center[3] * 0.5f)); } } // keep track of current depth: if (--nodesToNextLevel == 0) { depth++; nodesToNextLevel = queue.Count; } } // feed the modified node back: nodes[index] = node; yield return(0); } }