Пример #1
0
    //caxo - corner A x offset
    //cbyo - corner B y offset, etc
    //should be faster to just use ints instead of passing in vec3's, might be negligicable, but isn't too much more compliated
    /// <summary>
    /// Checks the edge created beteween the two points xyz+ cornerAXYZoffset and xyz + cornerBXYZoffset for a sign change between the densities of the corners
    /// if there is a sign change the edge is added to a list and the neighbouring cells that share this edge have the edge added to their QEF solver
    /// </summary>
    public void CheckEdge(int x, int y, int z, int caxo, int cayo, int cazo, int cbxo, int cbyo, int cbzo)
    {
        GridCorner cA = corners[x + caxo, y + cayo, z + cazo];
        GridCorner cB = corners[x + cbxo, y + cbyo, z + cbzo];



        List <GridCell> facesTemp = new List <GridCell>();

        if (cA.density * cB.density <= 0f)  //different sign
        {
            GridEdge e = new GridEdge(cA.position, cB.position);

            int intersectionSteps = 8;
            if (subdivisionLevel == 1)
            {
                intersectionSteps = 16;
            }

            e.intersection = ApproximateEdgeIntersection(cA.position, cB.position, Density, intersectionSteps);
            e.normal       = CalculateSurfaceNormal(e.intersection, Density);
            //e.intersection = e.normal = Vector3.zero;

            edges.Add(e);
            #region swapping and comments
            //need to find every cell that shares this edge so we can do QEF solve stuff per cell
            //get start corner indicies, sorted by smallest corner first
            //then find the direction we're going on (offset from start to end)
            //then just manually find neighbours

            #region swapping logic
            //swap so they're ordred properly
            //could do this in the density block only when we need to
            bool swap = false;
            if (x + caxo < x + cbxo)
            {
                //good
            }
            else if (x + caxo == x + cbxo)    //same check next
            {
                if (y + cayo < y + cbyo)
                {
                    //good
                }
                else if (y + cayo == y + cbyo)    //same, check next
                {
                    if (z + cazo < z + cbzo)
                    {
                        //good
                    }
                    else if (z + cazo == z + cbzo)
                    {
                        //same...all components were the same..  This should never happen
                    }
                    else
                    {
                        swap = true;
                    }
                }
                else
                {
                    swap = true;
                }
            }
            else
            {
                swap = true;
            }
            #endregion

            int sx;
            int sy;
            int sz;
            int dx;
            int dy;
            int dz;

            if (swap)
            {
                sx = x + cbxo;
                sy = y + cbyo;
                sz = z + cbzo;

                dx = (caxo - cbxo);
                dy = (cayo - cbyo);
                dz = (cazo - cbzo);
            }
            else
            {
                sx = x + caxo;
                sy = y + cayo;
                sz = z + cazo;

                dx = (cbxo - caxo);
                dy = (cbyo - cayo);
                dz = (cbzo - cazo);
            }
            #endregion

            //we should have six directions
            if (dx == 1 && dy == 0 && dz == 0)
            {
                //cells offsets [0,0,0] [0,0,-1] [0,-1,-1] [0,-1,0]
                //check each offset to see if it exists (and doesn't cause an out of range error)
                //if it exists add to the QEF and solve and stuff
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, 0, 0, 0));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, 0, 0, -1));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, 0, -1, -1));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, 0, -1, 0));
            }
            else if (dx == -1 && dy == 0 && dz == 0)
            {
                //this case will never happen, because we sort on x first so that the direction is always+.
                //If x *was* to be -, we would have swapped corners
                //but lets just fill it in anyways
                //cells offsets [-1,0,0] [-1,0,-1] [-1,-1,-1] [-1,-1,0]
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, -1, 0, 0));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, -1, 0, -1));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, -1, -1, -1));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, -1, -1, 0));
            }
            else if (dx == 0 && dy == 1 && dz == 0)
            {
                //cell offsets [0,0,-1] [0,0,0] [-1,0,0] [-1,0,-1]
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, 0, 0, -1));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, 0, 0, 0));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, -1, 0, 0));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, -1, 0, -1));
            }
            else if (dx == 0 && dy == -1 && dz == 0)
            {
                //cell offsets [0,-1,0] [0,-1,-1] [-1,-1,-1] [-1,-1,0]
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, 0, -1, -1));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, 0, -1, 0));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, -1, -1, 0));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, -1, -1, -1));
            }
            else if (dx == 0 && dy == 0 && dz == 1)
            {
                //cell offsets [0,0,0] [0,-1,0] [-1,-1,0] [-1,0,0]
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, 0, 0, 0));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, 0, -1, 0));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, -1, -1, 0));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, -1, 0, 0));
            }
            else if (dx == 0 && dy == 0 && dz == -1)
            {
                //cell offsets [0,0,-1] [0,-1,-1] [-1,-1,-1] [-1,0,-1]
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, 0, 0, -1));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, 0, -1, -1));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, -1, -1, -1));
                facesTemp.Add(AddToCellQEF(e, sx, sy, sz, -1, 0, -1));
            }

            if (facesTemp.Count == 4)
            {
                facesTemp.Sort();
                //need to sort the faces so we don't get any duplicates
                //and then we need to check to make sure the faces list doesn't contain this face
                GridCell ft0 = facesTemp[0];
                //Debug.Log(ft0 == null);
                //Debug.Log(ft0.cellIndex == null);
                Vector3 index = new Vector3(ft0.cellIndex.x, ft0.cellIndex.y, ft0.cellIndex.z);

                List <GridFace> fl = null;
                if (faces.ContainsKey(index))
                {
                    fl = faces[index];
                }

                //we need to check every element in FL to make sure we don't already have this face

                if (fl == null)
                {
                    //cant check because this face doesn't have a spot in the list yet
                    //so we can just add it because it doesn't exist, so this will be the first one
                    fl = new List <GridFace>();
                    GridFace f = new GridFace();
                    f.faces.Add(facesTemp[0]);
                    f.faces.Add(facesTemp[1]);
                    f.faces.Add(facesTemp[2]);
                    f.faces.Add(facesTemp[3]);
                    fl.Add(f);
                    faces[index] = fl;
                }
                else
                {
                    if (fl.Count == 0)
                    {
                        //cant check.  For some reason the list was created, but never added to
                        //add because it doesn't exist yet
                        GridFace f = new GridFace();
                        f.faces.Add(facesTemp[0]);
                        f.faces.Add(facesTemp[1]);
                        f.faces.Add(facesTemp[2]);
                        f.faces.Add(facesTemp[3]);
                        fl.Add(f);
                    }
                    else
                    {
                        for (int i = 0; i < fl.Count; i++)
                        {
                            //we can skip checking facesTemp[0].cellIndex against fl[0][0] because we know they're the same
                            //because that's how we sort the main list anyways
                            if (fl[i].faces[1] == facesTemp[1])
                            {
                                //second cell is the same
                                if (fl[i].faces[2] == facesTemp[2])
                                {
                                    //third cell is the same
                                    if (fl[i].faces[3] == facesTemp[3])
                                    {
                                        //last cell is the same... all match, so we don't want to add this faceTemp to the real list
                                        //because it will be a duplicate
                                    }
                                    else
                                    {
                                        //diff
                                        GridFace f = new GridFace();
                                        f.faces.Add(facesTemp[0]);
                                        f.faces.Add(facesTemp[1]);
                                        f.faces.Add(facesTemp[2]);
                                        f.faces.Add(facesTemp[3]);
                                        fl.Add(f);
                                        break;
                                    }
                                }
                                else
                                {
                                    //diff
                                    GridFace f = new GridFace();
                                    f.faces.Add(facesTemp[0]);
                                    f.faces.Add(facesTemp[1]);
                                    f.faces.Add(facesTemp[2]);
                                    f.faces.Add(facesTemp[3]);
                                    fl.Add(f);
                                    break;
                                }
                            }
                            else
                            {
                                //diff
                                GridFace f = new GridFace();
                                f.faces.Add(facesTemp[0]);
                                f.faces.Add(facesTemp[1]);
                                f.faces.Add(facesTemp[2]);
                                f.faces.Add(facesTemp[3]);
                                fl.Add(f);
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
Пример #2
0
    public void Start()
    {
        //List<List<List<List<int>>>> t = new List<List<List<List<int>>>>();
        //t[10][10][10] = new List<int>();
        //int[,,] t = new int[,,];
        //t[0, 0, 0] = 10;

        //given three ints, we return a list of faces that have those three ints as their sorted first face.
        //We need to be able to index the list directly with those three ints quickly.
        //we cant set the size at start because we don't know the max size of the [x,y,z]
        //t[x,y,z] -> List<GridFace> where GridFace f.faces[0].cellIndex.xyz == xyz
        //t[10,1000,10] needs to work so we can put a list there if it doesn't exist
        //t[10,1001,10] needs to work

        lastSubdivions = subdivisionLevel;
        subdivisions   = (subdivisionLevel == 0) ? 1 : (int)Mathf.Pow(2, subdivisionLevel);

        cells   = new GridCell[subdivisions, subdivisions, subdivisions];
        corners = new GridCorner[subdivisions + 1, subdivisions + 1, subdivisions + 1];
        edges   = new List <GridEdge>();
        faces   = new Dictionary <Vector3, List <GridFace> >();
        for (int x = 0; x < subdivisions; x++)
        {
            for (int y = 0; y < subdivisions; y++)
            {
                for (int z = 0; z < subdivisions; z++)
                {
                    cells[x, y, z] = new GridCell(new Vector3(x, y, z), volumeSize, subdivisions, this.transform.position);
                }
            }
        }

        //should we find the corners and store them here? with refs to the gridCells so we don't get duplicates?  We need to be able to
        //access neighbours to share edges, so it would make sense to do it outside.

        //this loop could be combined with with the loop above, if we modify it to use previous entries instead of next entires, right?
        //maybe.  Try it later if it's a bottleneck
        for (int x = 0; x < subdivisions + 1; x++)
        {
            for (int y = 0; y < subdivisions + 1; y++)
            {
                for (int z = 0; z < subdivisions + 1; z++)
                {
                    //for every grid cell, create it's corners and store them in the corners[,,] list.
                    //Also grab the neighbour cells that share that corner and give them that index too
                    bool lx = (x == subdivisions  ? true : false); //last x index, we need to switch to the +1 edge
                    bool ly = (y == subdivisions  ? true : false);
                    bool lz = (z == subdivisions  ? true : false);

                    int xo = 0;
                    int yo = 0;
                    int zo = 0;

                    Vector3 cornerOff = cornerOffsets[0];

                    if (lx)
                    {
                        xo          = -1;
                        cornerOff.x = 1;
                    }
                    if (ly)
                    {
                        yo          = -1;
                        cornerOff.y = 1;
                    }
                    if (lz)
                    {
                        zo          = -1;
                        cornerOff.z = 1;
                    }

                    GridCell   cell   = cells[x + xo, y + yo, z + zo];
                    GridCorner corner = new GridCorner(new Vector3(x, y, z), cell.center + (cell.cellSize * cornerOff * 0.5f));
                    corner.density   = Density(corner.position);
                    corners[x, y, z] = corner;
                }
            }
        }
        Debug.Log("Total Corners: " + corners.Length);

        for (int x = 0; x < subdivisions; x++)
        {
            for (int y = 0; y < subdivisions; y++)
            {
                for (int z = 0; z < subdivisions; z++)
                {
                    //this is to smartly add the edges to a list, to make sure we don't have duplicates.
                    //it's gross, but it should be pretty fast


                    //far-end corner (x) and the edges created with it's neighbours (n)
                    //                n____X
                    //                /    /|
                    //               .____n |
                    //               |    | n
                    //               .____./

                    CheckEdge(x, y, z, 0, 1, 1, 1, 1, 1);
                    CheckEdge(x, y, z, 1, 0, 1, 1, 1, 1);
                    CheckEdge(x, y, z, 1, 1, 0, 1, 1, 1);

                    //need every permutation of x/y/z with any 0 component

                    //100, 010, 001
                    //110, 101,011

                    //this was all figured out with trial and error
                    //depedngin on which edge we're on (and if it's the first corner) we fill in the first half of the edges,
                    //then on the next iteration we add on the next HALF, (as shown in the diagram above)
                    //so that we don't get any duplicate edges (like in a swapped order)

                    if (x == 0 && y == 0 && z == 0)
                    {
                        CheckEdge(x, y, z, 0, 0, 0, 0, 0, 1);
                        CheckEdge(x, y, z, 0, 0, 0, 0, 1, 0);
                        CheckEdge(x, y, z, 0, 0, 0, 1, 0, 0);
                        CheckEdge(x, y, z, 0, 1, 1, 0, 0, 1);
                        CheckEdge(x, y, z, 1, 0, 1, 0, 0, 1);
                        CheckEdge(x, y, z, 0, 1, 0, 0, 1, 1);
                        CheckEdge(x, y, z, 0, 1, 0, 1, 1, 0);
                        CheckEdge(x, y, z, 1, 0, 0, 1, 0, 1);
                        CheckEdge(x, y, z, 1, 0, 0, 1, 1, 0);
                    }
                    else if (x != 0 && y == 0 && z == 0)
                    {
                        CheckEdge(x, y, z, 1, 0, 0, 1, 0, 1);
                        CheckEdge(x, y, z, 1, 0, 0, 1, 1, 0);
                        CheckEdge(x, y, z, 1, 0, 0, 0, 0, 0);
                        CheckEdge(x, y, z, 1, 0, 1, 0, 0, 1);
                        CheckEdge(x, y, z, 1, 1, 0, 0, 1, 0);
                    }
                    else if (x == 0 && y != 0 && z == 0)
                    {
                        CheckEdge(x, y, z, 0, 1, 0, 0, 0, 0);
                        CheckEdge(x, y, z, 0, 1, 1, 0, 0, 1);
                        CheckEdge(x, y, z, 0, 1, 0, 0, 1, 1);
                        CheckEdge(x, y, z, 0, 1, 0, 1, 1, 0);
                        CheckEdge(x, y, z, 1, 0, 0, 1, 1, 0);
                    }
                    else if (x == 0 && y == 0 && z != 0)
                    {
                        CheckEdge(x, y, z, 0, 0, 0, 0, 0, 1);
                        CheckEdge(x, y, z, 0, 1, 1, 0, 0, 1);
                        CheckEdge(x, y, z, 1, 0, 1, 0, 0, 1);
                        CheckEdge(x, y, z, 0, 1, 0, 0, 1, 1);
                        CheckEdge(x, y, z, 1, 0, 0, 1, 0, 1);
                    }
                    else if (x != 0 && y == 0 & z != 0)
                    {
                        CheckEdge(x, y, z, 1, 0, 0, 1, 0, 1);
                        CheckEdge(x, y, z, 1, 0, 1, 0, 0, 1);
                    }
                    else if (x != 0 && y != 0 && z == 0)
                    {
                        CheckEdge(x, y, z, 1, 1, 0, 1, 0, 0);
                        CheckEdge(x, y, z, 0, 1, 0, 1, 1, 0);
                    }
                    else if (x == 0 && y != 0 && z != 0)
                    {
                        CheckEdge(x, y, z, 0, 1, 1, 0, 0, 1);
                        CheckEdge(x, y, z, 0, 1, 0, 0, 1, 1);
                    }
                }
            }
        }

        Debug.Log("Total Edges: " + edges.Count);

        //create corners for every cell.  Find it's neighbours in the direction of the corner and assign

        //create edges for sign changes.
        //in order to find sign changes we need to check each corners neighbours and see if they have a sign change
        //if we do, we actually creat the edge, and add the intersection to neighour cells QEF's

        //an edge is two corners.  We can get corners by indicies.
        //we need to make sure we don't have duplicates though

        //we need to know the cells that share an edge, not verticies really

        //when we create the mesh we store neighbouring edges by faces.  Can we presort the cells (the four cells used to construct the face)
        //so that they are always in some predetermined order, so when we add a new face we can check if it already exists before
        //adding it to the face list.  This will prevent us from getting duplicates in the face list

        //shoudl we keep a shortlist of faces that have verts instead of searching through all the grid cells again?
        int     vertcount = 0;
        Vector3 vertex    = Vector3.zero;

        for (int x = 0; x < subdivisions; x++)
        {
            for (int y = 0; y < subdivisions; y++)
            {
                for (int z = 0; z < subdivisions; z++)
                {
                    if (cells[x, y, z].hasVertex)
                    {
                        vertcount++;
                        GridCell cell = cells[x, y, z];
                        cell.vertex = Vector3.zero;
                        cell.qef.solve(cell.vertex, 1e-6f, 4, 1e-6f);

                        Vector3 min = (cell.center - (Vector3.one * (cell.cellSize / 2f)));
                        Vector3 max = (cell.center + (Vector3.one * (cell.cellSize / 2f)));

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

                        //cell.vertex = cell.qef.getMassPoint();

                        //Debug.Log("xyz:" + x + " : " + y + " : " + z);
                        //Debug.Log("Vertex: " + cell.vertex.ToString());
                        //Debug.Log("Edges: " + cell.edgeCount);
                    }
                }
            }
        }

        Debug.Log("Total Verts: " + vertcount);
        Debug.Log("Total Faces: " + faces.Count);

        //construct the mesh
        MeshRenderer mr = this.gameObject.GetComponent <MeshRenderer>();
        MeshFilter   mf = null;

        if (mr == null)
        {
            mr = this.gameObject.AddComponent <MeshRenderer>();
            mf = this.gameObject.AddComponent <MeshFilter>();
        }
        else
        {
            mf = this.gameObject.GetComponent <MeshFilter>();
        }

        mf.mesh.Clear();
        mr.sharedMaterial = Resources.Load("Default") as Material;

        Vector3[]      vertArray = new Vector3[faces.Count * 4];
        Vector3[]      normArray = new Vector3[faces.Count * 4];
        List <int>     triList   = new List <int>();
        List <Vector3> verts     = new List <Vector3>();
        List <Vector3> norms     = new List <Vector3>();

        foreach (List <GridFace> c in faces.Values)
        {
            for (int i = 0; i < c.Count; i++)
            {
                GridFace f = c[i];

                GridCell c0 = f.faces[0];
                GridCell c1 = f.faces[1];
                GridCell c2 = f.faces[2];
                GridCell c3 = f.faces[3];

                if (c0.vertexIndex == -1)
                {
                    verts.Add(c0.vertex);
                    c0.vertexIndex = verts.Count - 1;
                    norms.Add((c0.normal / c0.edgeCount).normalized);
                }

                if (c1.vertexIndex == -1)
                {
                    verts.Add(c1.vertex);
                    norms.Add((c1.normal / c1.edgeCount).normalized);
                    c1.vertexIndex = verts.Count - 1;
                }

                if (c2.vertexIndex == -1)
                {
                    verts.Add(c2.vertex);
                    norms.Add((c2.normal / c2.edgeCount).normalized);
                    c2.vertexIndex = verts.Count - 1;
                }

                if (c3.vertexIndex == -1)
                {
                    verts.Add(c3.vertex);
                    norms.Add((c3.normal / c3.edgeCount).normalized);
                    c3.vertexIndex = verts.Count - 1;
                }

                triList.Add(c0.vertexIndex);
                triList.Add(c1.vertexIndex);
                triList.Add(c3.vertexIndex);

                triList.Add(c3.vertexIndex);
                triList.Add(c2.vertexIndex);
                triList.Add(c0.vertexIndex);

                //every time we add a vertex to the list, we assign it's vertex index,
                //we don't want to add the vertex if it's vertex index is NOT -1, it means we've already assigned it.
                //will we get winding order issues? YEP.  This entire system won't work with the way we've tried to smartly sort stuff because
                //sorting messes with the winding order!
                //can we do some magic with the winding order here? order the triangles differently based on...something?
            }
        }

        mf.mesh.vertices  = verts.ToArray();
        mf.mesh.triangles = triList.ToArray();
        mf.mesh.normals   = norms.ToArray();
    }