예제 #1
0
 public void AddQEF(Vector3 position, Vector3 normal)
 {
     if (qef == null)
     {
         qef = new QefSolver();
     }
     qef.add(position, normal);
     this.normal += normal;
     edgeCount++;
     hasVertex = true;
 }
예제 #2
0
    public static OctreeNode ConstructLeaf(OctreeNode leaf)
    {
        if (leaf == null || leaf.size != 1)
        {
            return(null);
        }

        int corners = 0;

        for (int i = 0; i < 8; i++)
        {
            Vector3 cornerPos = leaf.min + CHILD_MIN_OFFSETS[i];
            float   density   = glm.Density_Func(cornerPos);
            int     material  = density < 0.0f ? MATERIAL_SOLID : MATERIAL_AIR;
            corners |= (material << i);
        }

        if (corners == 0 || corners == 255)
        {
            // voxel is full inside or outside the volume
            //delete leaf
            //setting as null isn't required by the GC in C#... but its in the original, so why not!
            leaf = null;
            return(null);
        }

        // otherwise the voxel contains the surface, so find the edge intersections
        const int MAX_CROSSINGS = 6;
        int       edgeCount     = 0;
        Vector3   averageNormal = Vector3.zero;
        QefSolver qef           = new QefSolver();

        for (int i = 0; i < 12 && edgeCount < MAX_CROSSINGS; i++)
        {
            int c1 = edgevmap[i][0];
            int c2 = edgevmap[i][1];

            int m1 = (corners >> c1) & 1;
            int m2 = (corners >> c2) & 1;

            if ((m1 == MATERIAL_AIR && m2 == MATERIAL_AIR) || (m1 == MATERIAL_SOLID && m2 == MATERIAL_SOLID))
            {
                // no zero crossing on this edge
                continue;
            }

            Vector3 p1 = leaf.min + CHILD_MIN_OFFSETS[c1];
            Vector3 p2 = leaf.min + CHILD_MIN_OFFSETS[c2];
            Vector3 p  = ApproximateZeroCrossingPosition(p1, p2);
            Vector3 n  = CalculateSurfaceNormal(p);
            qef.add(p.x, p.y, p.z, n.x, n.y, n.z);

            averageNormal += n;

            edgeCount++;
        }

        Vector3 qefPosition = Vector3.zero;

        qef.solve(qefPosition, QEF_ERROR, QEF_SWEEPS, QEF_ERROR);

        OctreeDrawInfo drawInfo = new OctreeDrawInfo();

        drawInfo.corners  = 0;
        drawInfo.index    = -1;
        drawInfo.position = new Vector3(qefPosition.x, qefPosition.y, qefPosition.z);
        drawInfo.qef      = qef.getData();

        Vector3 min = leaf.min;
        Vector3 max = new Vector3(leaf.min.x + leaf.size, leaf.min.y + leaf.size, leaf.min.z + leaf.size);

        if (drawInfo.position.x < min.x || drawInfo.position.x > max.x ||
            drawInfo.position.y < min.y || drawInfo.position.y > max.y ||
            drawInfo.position.z < min.z || drawInfo.position.z > max.z)
        {
            drawInfo.position = qef.getMassPoint();
        }

        drawInfo.averageNormal = Vector3.Normalize(averageNormal / (float)edgeCount);
        drawInfo.corners       = corners;

        leaf.Type     = OctreeNodeType.Node_Leaf;
        leaf.drawInfo = drawInfo;

        return(leaf);
    }
예제 #3
0
    public static OctreeNode SimplifyOctree(OctreeNode node, float threshold)
    {
        if (node == null)
        {
            return(null);
        }

        if (node.Type != OctreeNodeType.Node_Internal)
        {
            // can't simplify!
            return(node);
        }

        QefSolver qef = new QefSolver();

        int[] signs = new int[8] {
            -1, -1, -1, -1, -1, -1, -1, -1
        };
        int  midsign       = -1;
        int  edgeCount     = 0;
        bool isCollapsible = true;

        for (int i = 0; i < 8; i++)
        {
            node.children[i] = SimplifyOctree(node.children[i], threshold);

            if (node.children[i] != null)
            {
                OctreeNode child = node.children[i];

                if (child.Type == OctreeNodeType.Node_Internal)
                {
                    isCollapsible = false;
                }
                else
                {
                    qef.add(child.drawInfo.qef);

                    midsign  = (child.drawInfo.corners >> (7 - i)) & 1;
                    signs[i] = (child.drawInfo.corners >> i) & 1;

                    edgeCount++;
                }
            }
        }

        if (!isCollapsible)
        {
            // at least one child is an internal node, can't collapse
            return(node);
        }

        Vector3 qefPosition = Vector3.zero;

        qef.solve(qefPosition, QEF_ERROR, QEF_SWEEPS, QEF_ERROR);
        float error = qef.getError();

        // convert to glm vec3 for ease of use
        Vector3 position = new Vector3(qefPosition.x, qefPosition.y, qefPosition.z);

        // at this point the masspoint will actually be a sum, so divide to make it the average
        if (error > threshold)
        {
            // this collapse breaches the threshold
            return(node);
        }

        if (position.x < node.min.x || position.x > (node.min.x + node.size) ||
            position.y < node.min.y || position.y > (node.min.y + node.size) ||
            position.z < node.min.z || position.z > (node.min.z + node.size))
        {
            position = qef.getMassPoint();
        }

        // change the node from an internal node to a 'psuedo leaf' node
        OctreeDrawInfo drawInfo = new OctreeDrawInfo();

        drawInfo.corners = 0;
        drawInfo.index   = -1;

        for (int i = 0; i < 8; i++)
        {
            if (signs[i] == -1)
            {
                // Undetermined, use centre sign instead
                drawInfo.corners |= (midsign << i);
            }
            else
            {
                drawInfo.corners |= (signs[i] << i);
            }
        }

        drawInfo.averageNormal = Vector3.zero;
        for (int i = 0; i < 8; i++)
        {
            if (node.children[i] != null)
            {
                OctreeNode child = node.children[i];
                if (child.Type == OctreeNodeType.Node_Psuedo ||
                    child.Type == OctreeNodeType.Node_Leaf)
                {
                    drawInfo.averageNormal += child.drawInfo.averageNormal;
                }
            }
        }

        drawInfo.averageNormal = drawInfo.averageNormal.normalized;
        drawInfo.position      = position;
        drawInfo.qef           = qef.getData();

        for (int i = 0; i < 8; i++)
        {
            DestroyOctree(node.children[i]);
            node.children[i] = null;
        }

        node.Type     = OctreeNodeType.Node_Psuedo;
        node.drawInfo = drawInfo;

        return(node);
    }
        public GridCell(Vector3 center, float gridScale = 1f)
        {
            QefSolver qef = new QefSolver();

            //check each corner against the density function
            //and generate SOMETHING at that position to see if our density function is right
            //we can cache the results from each corner, as we need to check every one anyways
            //is there a smarter way
            //we can have up to 4 sign changes per cell..
            // 0 ---- 1
            // |      |
            // 1------0  <---- opposite for the top face = 4 total


            //get corner positions
            //take the center (int coords), convert them to world positions, then +/- on every axis
            this.gridScale = gridScale;
            worldPos       = center * gridScale;

            //need to do corners in the same order every time
            //assign the corners to their density value at that corner position
            for (int i = 0; i < 8; i++)
            {
                float v = DualContouring1.density(worldPos + DualContouring1.cornerOffsets[i] * 0.5f * gridScale);
                //thresholding here to turn the density into an ID
                corners[i] = v >= 0 ? 1 : 0;
            }

            //check if any edges have sign changes
            //I guess only store the edges that are important, like the ones that have a sign change.
            //we need to check every corner against it's neighbour cells, so we need to know which ones are it's neighbours.
            //Each corner has three neighbours,
            for (int i = 0; i < 4; i++)  //but we only want to check half of them, otherwise we have doubles?
            {
                for (int j = 0; j < 3; j++)
                {
                    int neighbour = cornerNeighbours[i, j];
                    if (corners[i] != corners[neighbour])  //if this corner has a different density value than any of it's neighbours
                    //we need to be able to find neighbours based on edges...
                    //not sure how
                    //make a naive implimentation first
                    {
                        GridEdge e = new GridEdge(i, neighbour, center);

                        edges.Add(e);
                        DualContouring1.edges.Add(e);

                        Vector3 edgeCornerA = worldPos + DualContouring1.cornerOffsets[e.corners[0]] * 0.5f * gridScale;
                        Vector3 edgeCornerB = worldPos + DualContouring1.cornerOffsets[e.corners[1]] * 0.5f * gridScale;
                        //need these for meshing to find shared edges I guess
                        e.cornersOffset[0] = DualContouring1.cornerOffsets[e.corners[0]];
                        e.cornersOffset[1] = DualContouring1.cornerOffsets[e.corners[1]];
                        //computing the intersection position and normal of this edge against the density function
                        e.position = DualContouring1.ApproximateEdgeIntersection(edgeCornerA, edgeCornerB, DualContouring1.density);
                        e.normal   = DualContouring1.CalculateSurfaceNormal(e.position, DualContouring1.density);

                        //from here we have to get the actual position of the vertex for this cell
                        //we do this using the QefSolver.  For every edge intersection add the position and normal
                        qef.add(e.position, e.normal);
                        //add the normal so we can grab it later (summed, so we have to grab normal/edges.Count
                        normal += e.normal;
                        Debug.Log("1");
                    }
                }
            }

            if (edges.Count != 0)
            {
                vertex = Vector3.zero;
                qef.solve(vertex, QEF_ERROR, QEF_SWEEPS, QEF_ERROR);
                Vector3 min = center + DualContouring1.cornerOffsets[0] * 0.5f * gridScale;
                Vector3 max = center + DualContouring1.cornerOffsets[6] * 0.5f * gridScale;

                if (vertex.x < min.x || vertex.x > max.x ||
                    vertex.y < min.y || vertex.y > max.y ||
                    vertex.z < min.z || vertex.z > max.z)
                {
                    vertex = qef.getMassPoint();
                }
                DualContouring1.verticies.Add(new MeshVertex(vertex, (normal / edges.Count).normalized));
                vertexIndex = DualContouring1.verticies.Count - 1;
                if (vertex == Vector3.zero)
                {
                    Debug.Log("Zero");
                }
                Debug.Log("2");
                hasVertex = true;
            }
        }