Example #1
0
        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);
        }
Example #2
0
        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);
        }
Example #3
0
        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;
        }
Example #4
0
        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);
            }
        }