Ejemplo n.º 1
0
        private void test()
        {
            tree = new VoxelOctree <Vector3>(depth, new Bounds(Vector3.zero, Vector3.one * size));
            addTestData();

            //showTraversals();
        }
Ejemplo n.º 2
0
        //CONSIDER: we will want to define this class's relationship to Chunk at some point

        public void Init(IntVector3 chunkPos, uint[] voxels)
        {
            int depth = vGenConfig.hilbertBits;

            Vector3 center = vGenConfig.ChunkPosToPos(chunkPos) + vGenConfig.ChunkSize / 2f;
            var     bounds = new Bounds(center, vGenConfig.ChunkSize);

            tree = new VoxelOctree <uint>(depth, bounds);

            foreach (uint vox in voxels)
            {
                IntVector3 pos = IntVector3.FromUint256(vox);
                tree.Set(vox, pos.ToVector3());
            }

            // instantiate or just set up raycast display
            rayDisplay = Instantiate(raycastDisplayPrefab);
            rayDisplay.transform.SetParent(transform);
            rayDisplay.transform.localPosition = vGenConfig.ChunkPosToPos(chunkPos);

            //ComputeBuffer b = BufferUtil.CreateWith(voxels);

            rayDisplay.initialize(null);
            //ResetBufferData(voxels); //test


            StartCoroutine(Scan()); //want
        }
Ejemplo n.º 3
0
    public void CreateAndDisplayMesh()
    {
        //Func<Vector3, float> Source = pt => -NoisePlane(pt + offset, 10.0f);

        Func <Vector3, float> Source = pt => - Torus(pt + offset, new Vector2(25.0f, 10.0f));

        VoxelOctree octree = VoxelOctree.CreateBasedOnFunction(Source, Vector3.zero, Vector3.one * 100.0f, 5);

        Mesh mesh = MeshGenerator.CreateMeshFromVoxels(octree, Source, 5, 0);

        meshFilter.mesh = mesh;
    }
Ejemplo n.º 4
0
        public override (Voxel target, Voxel moveTarget) ChooseNextTarget()
        {
            if (Octree == null)
            {
                Octree = new VoxelOctree(0, 0, 0, MatterSystem.Matrix.Resolution, MatterSystem.Matrix.Resolution, MatterSystem.Matrix.Resolution);
                foreach (var v in RemainingVoxels)
                {
                    Octree.Add(v);
                }
            }

            var             pos         = MatterSystem.Bots[1].Position;
            HashSet <Voxel> unreachable = new HashSet <Voxel>();

            for (var i = 4; i < MatterSystem.Matrix.Resolution; i++)
            {
                var target = Octree.GetNearby(pos, i)
                             .Where(x => !unreachable.Contains(x))
                             .Where(x => x.Grounded)
                             .OrderBy(x => x.Y)
                             .ThenBy(x => x.dv(pos));

                foreach (var v in target)
                {
                    // see if we can navigate to this
                    var moveTargets = v.Nearby
                                      .Where(x => !x.Filled)
                                      .OrderByDescending(x => x.Y)
                                      .ThenByDescending(x => x.Nearby.Count(y => y.Filled != y.Target && y.Grounded))
                                      .ThenBy(x => x.dv(pos));

                    foreach (var mt in moveTargets)
                    {
                        if (AstarMatrixPather.GetRouteTo(pos, mt) == null)
                        {
                            continue;
                        }

                        return(v, mt);
                    }

                    unreachable.Add(v);
                }
            }

            return(null, null);
        }
Ejemplo n.º 5
0
    private static OctreeVertexData PositionVerticesInOctree(VoxelOctree fromOctree, Func <Vector3, float> Source, int traversalDepth)
    {
        Vector3 min      = fromOctree.rootAABB.min;
        int     n        = NumSamplesToEdge(traversalDepth);
        Vector3 leafSize = (fromOctree.rootAABB.size / n);

        List <Vector3> vertices          = new List <Vector3>();
        List <int>     vertexNodeIndices = new List <int>();

        Traverse(0, 0);

        return(new OctreeVertexData
        {
            positions = vertices.ToArray(),
            nodeIndices = vertexNodeIndices.ToArray()
        });

        void Traverse(int cell, int depth)
        {
            VoxelOctree.Node node = fromOctree.nodes[cell];

            if (node.isLeaf && node.filled != 255) // || depth > traversalDepth)
            {
                VoxelOctree.CellData data = fromOctree.cells[node.firstChild];
                Vector3 pos = PositionVertexInDCCell(Source, data.cellMin, data.cellSize, node);
                //UnityEngine.Debug.Log(pos + "");
                vertices.Add(pos);
                vertexNodeIndices.Add(cell);
                return;
            }
            else if (node.isLeaf)
            {
                return;
            }
            else
            {
                for (int i = 0; i < 8; i++)
                {
                    int childIndex;
                    if ((childIndex = node.getIndexOfChild(i)) != -1)
                    {
                        Traverse(childIndex, depth + 1);
                    }
                }
            }
        }
    }
Ejemplo n.º 6
0
    /* Traverses through a VoxelOctree and creates a Mesh in two steps:
     *
     * 1. Find the connected cells of the octree and populate the index list - done?
     *
     * 2. Traverse and position each vertex within the cell
     */
    public static Mesh CreateMeshFromVoxels(VoxelOctree fromOctree, Func <Vector3, float> Source, int traversalDepth, int startCell)
    {
        List <int> faceData = FindFacesInOctree(fromOctree, startCell, traversalDepth);

        OctreeVertexData vertexData = PositionVerticesInOctree(fromOctree, Source, fromOctree.maxDepth);

        int[] triangles = new int[faceData.Count];

        for (int i = 0; i < triangles.Length; i++)
        {
            for (int j = 0; j < vertexData.nodeIndices.Length; j++)
            {
                if (vertexData.nodeIndices[j] == faceData[i])
                {
                    triangles[i] = j;
                    break;
                }
            }
        }

        for (int i = 0; i < 12; i++)
        {
            Vector3 pt0 = fromOctree.rootAABB.min + Vector3.Scale(VoxelOctree.octantOrder[edgeTraversalOrder[i, 0]], fromOctree.rootAABB.size);
            Vector3 pt1 = fromOctree.rootAABB.min + Vector3.Scale(VoxelOctree.octantOrder[edgeTraversalOrder[i, 1]], fromOctree.rootAABB.size);
            UnityEngine.Debug.DrawLine(pt0, pt1, Color.black, 1.0f);
        }

        Mesh mesh = new Mesh
        {
            vertices  = vertexData.positions,
            triangles = triangles
        };

        mesh.RecalculateNormals();

        return(mesh);
    }
Ejemplo n.º 7
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="maxDepth"></param>
 /// <param name="voxelSizeThreshold"></param>
 public void GenerateOctree(int maxDepth, float voxelSizeThreshold)
 {
     Octree = VoxelOctree.Create(this, maxDepth, voxelSizeThreshold);
 }
Ejemplo n.º 8
0
        /// <summary>
        ///
        /// </summary>
        internal void Parse()
        {
            var configDoc = new XmlDocument();

            configDoc.LoadXml(configXmlEditBox.Text);

            #region Log

            var logNode = configDoc.SelectSingleNode("/AthenaConfig/Log");
            if (logNode != null)
            {
                Log.Instance.MinLogLevel = logNode.Attributes["minLogLevel"] != null ? (LogLevel)Enum.Parse(typeof(LogLevel), logNode.Attributes["minLogLevel"].Value) : LogLevel.Anything;

                if (logNode.SelectSingleNode("Filename") != null && !string.IsNullOrEmpty(logNode.SelectSingleNode("Filename").InnerText))
                {
                    Log.Instance.Filename = logNode.SelectSingleNode("Filename").InnerText;
                }
            }

            #endregion

            #region Output

            var vsyncNode = configDoc.SelectSingleNode("/AthenaConfig/Output").Attributes["vSync"];
            if (vsyncNode != null)
            {
                VSync = (VSyncMode)Enum.Parse(typeof(VSyncMode), vsyncNode.Value);
            }

            var saveAfterRenderingNode = configDoc.SelectSingleNode("/AthenaConfig/Output").Attributes["saveAfterRendering"];
            if (saveAfterRenderingNode != null)
            {
                SaveOutputAfterRendering = bool.Parse(saveAfterRenderingNode.Value);
            }

            Fullscreen = false;
            var fullscreenNode = configDoc.SelectSingleNode("/AthenaConfig/Output").Attributes["fullscreen"];
            if (fullscreenNode != null)
            {
                Fullscreen = bool.Parse(fullscreenNode.Value);
            }

            OutputSize = new Vector2i(Int32.Parse(configDoc.SelectSingleNode("/AthenaConfig/Output/Resolution").Attributes["width"].Value), Int32.Parse(configDoc.SelectSingleNode("/AthenaConfig/Output/Resolution").Attributes["height"].Value));

            OutputImageFilename = null;
            var outputImageFilenameNode = configDoc.SelectSingleNode("/AthenaConfig/Output/Filename");
            if (outputImageFilenameNode != null)
            {
                OutputImageFilename = outputImageFilenameNode.Value;
            }

            #endregion

            #region Rendering

            NumberOfThreads = null;
            if (configDoc.SelectSingleNode("/AthenaConfig/Rendering").Attributes["threads"] != null)
            {
                NumberOfThreads = Int32.Parse(configDoc.SelectSingleNode("/AthenaConfig/Rendering").Attributes["threads"].Value);
            }

            NumberOfJobs = new Vector2i(Int32.Parse(configDoc.SelectSingleNode("/AthenaConfig/Rendering/JobCount").Attributes["width"].Value), Int32.Parse(configDoc.SelectSingleNode("/AthenaConfig/Rendering/JobCount").Attributes["height"].Value));
            //JobSize = new Vector2i(Int32.Parse(configDoc.SelectSingleNode("/AthenaConfig/Rendering/JobSize").Attributes["width"].Value), Int32.Parse(configDoc.SelectSingleNode("/AthenaConfig/Rendering/JobSize").Attributes["height"].Value));

            FramesToRender = null;
            var framesToRenderNode = configDoc.SelectSingleNode("/AthenaConfig/Rendering").Attributes["framesToRender"];
            if (framesToRenderNode != null)
            {
                FramesToRender = Int32.Parse(framesToRenderNode.Value);
            }

            if (FramesToRender == 0)
            {
                FramesToRender = null;
            }

            WaitForOutputRedraw = false;
            var waitForOutputRedrawNode = configDoc.SelectSingleNode("/AthenaConfig/Rendering").Attributes["waitForOutputRedraw"];
            if (waitForOutputRedrawNode != null)
            {
                WaitForOutputRedraw = bool.Parse(waitForOutputRedrawNode.Value);
            }

            RayTracer.BackgroundColor = null;

            var backgroundNode = configDoc.SelectSingleNode("/AthenaConfig/Output/Background");
            if (backgroundNode != null)
            {
                if (backgroundNode.Attributes["useRayDirectionVector"] != null && bool.Parse(backgroundNode.Attributes["useRayDirectionVector"].Value))
                {
                    RayTracer.BackgroundColor = null;
                }
                else if (backgroundNode.SelectSingleNode("Color") != null)
                {
                    RayTracer.BackgroundColor = ParseColorRGBNode(backgroundNode.SelectSingleNode("Color"));
                }
                else
                {
                    RayTracer.BackgroundColor = ColorRGB.Black;
                }
            }

            #region RayTracer

            RayTracer.MaxOctreeDepth = 8;

            var rayTracerNode = configDoc.SelectSingleNode("/AthenaConfig/Rendering/RayTracer");
            if (rayTracerNode != null)
            {
                if (rayTracerNode.Attributes["maxOctreeDepth"] != null)
                {
                    RayTracer.MaxOctreeDepth = Int32.Parse(rayTracerNode.Attributes["maxOctreeDepth"].Value);
                }
            }

            #endregion

            #endregion

            #region Scene

            Scene.Current = null;

            var sceneNode = configDoc.SelectSingleNode("/AthenaConfig/Scene");
            if (sceneNode == null)
            {
                throw new Exception("No scene found in configuration");
            }

            Scene.Current = new Scene(ParseName(sceneNode));

            var sceneMaxDepth           = 8;
            var sceneVoxelSizeThreshold = 1f;
            ParseVoxelOctreeNode(sceneNode.SelectSingleNode("VoxelOctree"), ref sceneMaxDepth, ref sceneVoxelSizeThreshold);

            VoxelOctree.GenerateOnMultipleThreads = false;
            var voxelOctreeNode = sceneNode.SelectSingleNode("VoxelOctree");
            if (voxelOctreeNode != null && voxelOctreeNode.Attributes["generateOnMultipleThreads"] != null)
            {
                VoxelOctree.GenerateOnMultipleThreads = bool.Parse(voxelOctreeNode.Attributes["generateOnMultipleThreads"].Value);
            }

            #region Camera

            var cameraNode = configDoc.SelectSingleNode("/AthenaConfig/Scene/Camera");
            if (cameraNode != null)
            {
                Scene.Current.Camera = new Camera(ParseVector3Node(cameraNode.SelectSingleNode("Position")), ParseVector3Node(cameraNode.SelectSingleNode("Target")));

                if (cameraNode.Attributes["fov"] != null)
                {
                    Scene.Current.Camera.FieldOfVision = float.Parse(cameraNode.Attributes["fov"].Value);
                }

                Scene.Current.Camera.MovementSpeed = cameraNode.SelectSingleNode("MovementSpeed") != null?ParseVector3Node(cameraNode.SelectSingleNode("MovementSpeed")) : new Athena.Core.DataTypes.Vector3(1);

                Scene.Current.Camera.RotationSpeed = cameraNode.SelectSingleNode("RotationSpeed") != null?ParseVector3Node(cameraNode.SelectSingleNode("RotationSpeed")) : new Athena.Core.DataTypes.Vector3(1);
            }

            #endregion

            #region Objects

            foreach (XmlNode objNode in sceneNode.SelectSingleNode("Objects").ChildNodes)
            {
                if (objNode is XmlComment)
                {
                    continue;
                }

                var position = ParseVector3Node(objNode.SelectSingleNode("Position"));
                var name     = ParseName(objNode);

                if (objNode.Name == "WavefrontObjMesh")
                {
                    #region Mesh

                    var mesh = new WavefrontObjMesh(objNode.SelectSingleNode("Filename").InnerText)
                    {
                        Position = position, Name = name
                    };

                    if (objNode.Attributes["voxelize"] == null || bool.Parse(objNode.Attributes["voxelize"].Value))
                    {
                        var maxDepth           = sceneMaxDepth;
                        var voxelSizeThreshold = sceneVoxelSizeThreshold;
                        ParseVoxelOctreeNode(objNode.SelectSingleNode("VoxelOctree"), ref maxDepth, ref voxelSizeThreshold);

                        mesh.GenerateOctree(maxDepth, voxelSizeThreshold);
                    }

                    #endregion

                    Scene.Current.AddObject(mesh);
                }

                if (objNode.Name == "Terrain")
                {
                    #region Terrain

                    var heightMap = null as float[];
                    if (objNode.Attributes["generationMethod"].Value == "MidPointDisplacement")
                    {
                        var midPointDisplacementNode = objNode.SelectSingleNode("MidPointDisplacement");
                        var heightMapSize            = int.Parse(midPointDisplacementNode.Attributes["size"].Value);
                        var roughness = float.Parse(midPointDisplacementNode.Attributes["roughness"].Value);

                        int?seed = null;
                        if (midPointDisplacementNode.Attributes["seed"] != null)
                        {
                            seed = int.Parse(midPointDisplacementNode.Attributes["seed"].Value);
                        }

                        heightMap = HeightMapGenerator.GenerateWithMidPointDisplacement(heightMapSize, roughness, seed);
                    }

                    var size      = float.Parse(objNode.Attributes["size"].Value);
                    var maxHeight = float.Parse(objNode.Attributes["maxHeight"].Value);

                    var terrain = new Terrain(size, maxHeight, heightMap)
                    {
                        Position = position, Name = name
                    };

                    if (objNode.Attributes["voxelize"] == null || bool.Parse(objNode.Attributes["voxelize"].Value))
                    {
                        var maxDepth           = sceneMaxDepth;
                        var voxelSizeThreshold = sceneVoxelSizeThreshold;
                        ParseVoxelOctreeNode(objNode.SelectSingleNode("VoxelOctree"), ref maxDepth, ref voxelSizeThreshold);

                        terrain.GenerateOctree(maxDepth, voxelSizeThreshold);
                    }

                    #endregion

                    Scene.Current.AddObject(terrain);
                }

                else if (objNode.Name == "Sphere")
                {
                    #region Sphere

                    var sphere = new Sphere()
                    {
                        Position = position, Name = name
                    };

                    sphere.Radius = float.Parse(objNode.Attributes["radius"].Value);

                    if (objNode.Attributes["voxelize"] == null || bool.Parse(objNode.Attributes["voxelize"].Value))
                    {
                        var maxDepth           = sceneMaxDepth;
                        var voxelSizeThreshold = sceneVoxelSizeThreshold;
                        ParseVoxelOctreeNode(objNode.SelectSingleNode("VoxelOctree"), ref maxDepth, ref voxelSizeThreshold);

                        sphere.Octree = VoxelOctree.Create(sphere, maxDepth, voxelSizeThreshold);
                    }

                    #endregion

                    Scene.Current.AddObject(sphere);
                }

                else if (objNode.Name == "OmniLight")
                {
                    #region OmniLight

                    var omniLight = new OmniLight(float.Parse(objNode.Attributes["radius"].Value))
                    {
                        Position = position, Name = name
                    };

                    omniLight.Color = ParseColorRGBNode(objNode.SelectSingleNode("Color"));

                    if (objNode.Attributes["intensity"] != null)
                    {
                        omniLight.Intensity = float.Parse(objNode.Attributes["intensity"].Value);
                    }

                    #endregion

                    Scene.Current.AddObject(omniLight);
                }
            }

            #endregion

            #endregion
        }
Ejemplo n.º 9
0
    /*
     * static void PopulateOctreeBasedOnFunctionDC(Func<Vector3, float> Source, float[,,] map, Octree<ContourElement> octree, int cellIndex, int depth, Bounds cell)
     * {
     *  float[] samples = new float[8];
     *  Vector3 min = cell.min;
     *  Vector3 size = cell.size;
     *  // sample all 8 corner points of the cell (TODO: test caching these values, each unique point gets sampled like 4 times at least)
     *  for (int i = 0; i < 8; i++)
     *  {
     *      samples[i] = Source(min + Vector3.Scale(Octree<ContourElement>.octantOrder[i], size));
     *  }
     *
     *  Octree<ContourElement>.Node node = octree.nodes[cellIndex];
     *
     *  int sign = 0;
     *  for (int i = 0; i < 8; i++)
     *  {
     *      sign += (int)Mathf.Sign(samples[i]);
     *  }
     *
     *  sign *= depth < minDepth ? 0 : 1;
     *
     *  if (sign == 8) // this cell is completely outside the solid and does not need subdividing further
     *  {
     *      node.isLeaf = true;
     *      node.child = LEAF_EMPTY;
     *      octree.nodes[cellIndex] = node;
     *  }
     *  else if (sign == -8) // this cell is completely inside the solid and does not need subdividing further
     *  {
     *      node.isLeaf = true;
     *      node.child = LEAF_FILLED;
     *      octree.nodes[cellIndex] = node;
     *  }
     *  else  // this cell contains both empty space and part of the solid --
     *  {
     *      if (depth <= maxDepth) // so divide it into 8 children
     *      {
     *          node.isLeaf = false;
     *          node.child = octree.nodes.Count;
     *          octree.nodes[cellIndex] = node;
     *
     *          Vector3 ctr = cell.center - (cell.extents / 2.0f);
     *
     *          for (int i = 0; i < 8; i++)
     *          {
     *              var newNode = new Octree<ContourElement>.Node();
     *              octree.nodes.Add(newNode);
     *          }
     *
     *          for (int i = 0; i < 8; i++)
     *          {
     *              Bounds aabb = new Bounds(ctr + Vector3.Scale(Octree<ContourElement>.octantOrder[i], cell.extents), cell.extents);
     *
     *              //PopulateOctreeBasedOnFunctionDC(Source, octree, node.child + i, depth + 1, aabb);
     *          }
     *      }
     *      else // but we are at the maximum allowed detail level, so put a vertex in this cell
     *      {
     *          node.isLeaf = true;
     *
     *          ContourElement contour = PositionVertexInDCCell(Source, samples, cell);
     *
     *          node.child = octree.elements.Insert(contour);
     *          octree.nodes[cellIndex] = node;
     *      }
     *  }
     *
     * }
     */
    // recursively construct the actual mesh from the octree of vertices
    private static List <int> FindFacesInOctree(VoxelOctree octree, int startingCell, int traversalDepth)
    {
        /* these lookup tables just hold the variations between the different traversal routes when
         * looking through the octree for vertices to connect into faces.
         *
         * o - offsets for looking at the children of nodes next to larger leaf nodes. depending on
         * the orientation of the four cells EdgeProc looks at, it needs to select the adjacent child
         *
         * l - CellProc calls each traversal method twice for each level; the way the indexes are
         * selected for the octree means going from the lesser valued variant axis side to the
         * higher valued one can always be done by adding a constant, stored in this index
         *
         * a - when deciding whether to draw a face, EdgeProc tests two points of the function in the
         * group of cells in question, varied along one axis depending on which orientation the four
         * cells are. as the variable names suggest, the test for the cells along the XZ plane involve
         * varying axis 2, or Y. then; 1 = X and 3 = Z
         */

        int[][] lookEdge =
        {
            //              |    o     | l| a|
            new int[] { 3, 2, 0, 1, 4, 2 },  // XZ
            new int[] { 5, 1, 0, 4, 2, 3 },  // XY
            new int[] { 6, 4, 0, 2, 1, 1 },  // ZY
        };

        // e, i - FaceProc must also call EdgeProc, but it has to use the correct lookEdge array for the
        // FaceProc call. the last two groups here show which children nodes to select for those calls,
        // as well as which lookEdge to use

        int[][] lookFace =
        {
            //              | o   |  l  |       e1      |       e2     |   i  |
            new int[] { 1, 0, 4, 2, -1,  0, 2, -3, -1, -5, 4,  0, 0, 1 },    // X
            new int[] { 4, 0, 2, 1, -4,  0, 1, -5, -4, -6, 2,  0, 1, 2 },    // Y
            new int[] { 2, 0, 4, 1, -2, -3, 1,  0, -2,  0, 4, -6, 0, 2 },    // Z
        };

        List <int> faces = new List <int>();

        CellProc(0, startingCell);

        return(faces);

        void CellProc(int depth, int node)
        {
            VoxelOctree.Node currentNode = octree.nodes[node];

            if (currentNode.isLeaf || depth > traversalDepth)
            {
                return;
            }

            for (int i = 0; i < 8; i++)
            {
                int ind;
                if ((ind = currentNode.getIndexOfChild(i)) != -1)
                {
                    CellProc(depth + 1, ind);
                }
            }

            for (int i = 0; i < 12; i++)
            {
                int ind1, ind2;
                if ((ind1 = currentNode.getIndexOfChild(edgeTraversalOrder[i, 0])) != -1 &&
                    (ind2 = currentNode.getIndexOfChild(edgeTraversalOrder[i, 1])) != -1)
                {
                    FaceProc(depth + 1, ind1, ind2, lookFace[i / 4]);
                }
            }

            for (int i = 0; i < 6; i++)
            {
                int ind1, ind2, ind3, ind4;
                if ((ind1 = currentNode.getIndexOfChild(faceTraversalOrder[i, 0])) != -1 &&
                    (ind2 = currentNode.getIndexOfChild(faceTraversalOrder[i, 1])) != -1 &&
                    (ind3 = currentNode.getIndexOfChild(faceTraversalOrder[i, 2])) != -1 &&
                    (ind4 = currentNode.getIndexOfChild(faceTraversalOrder[i, 3])) != -1)
                {
                    EdgeProc(depth + 1, ind1, ind2, ind3, ind4, lookEdge[i / 2]);
                }
            }
        }

        void FaceProc(int depth, int node0, int node1, int[] look)
        {
            VoxelOctree.Node[] n = { octree.nodes[node0], octree.nodes[node1] };

            if (n[0].isLeaf || n[1].isLeaf || depth > traversalDepth)
            {
                return;
            }

            int[,] nextFaces = new int[2, 4];

            for (int i = 0; i < 2; i++)
            {
                nextFaces[i, 0] = look[i];
                nextFaces[i, 1] = look[i] + look[2];
                nextFaces[i, 2] = look[i] + look[3];
                nextFaces[i, 3] = look[i] + look[2] + look[3];
            }

            for (int i = 0; i < 4; i++)
            {
                int ind1, ind2;
                if ((ind1 = n[0].getIndexOfChild(nextFaces[0, i])) != -1 &&
                    (ind2 = n[1].getIndexOfChild(nextFaces[1, i])) != -1)
                {
                    FaceProc(depth + 1, ind1, ind2, look);
                }
            }

            nextFaces = new int[4, 4];

            for (int i = 0; i < 4; i++)
            {
                int nextNode1 = Math.Sign(look[i + 4]) == -1 ? 0 : 1;
                int nextNode2 = Math.Sign(look[i + 8]) == -1 ? 0 : 1;

                nextFaces[i, 0] = n[nextNode1].getIndexOfChild(Math.Abs(look[i + 4]));
                nextFaces[i, 1] = n[nextNode1].getIndexOfChild(Math.Abs(look[i + 4]) + look[2]);
                nextFaces[i, 2] = n[nextNode2].getIndexOfChild(Math.Abs(look[i + 8]));
                nextFaces[i, 3] = n[nextNode2].getIndexOfChild(Math.Abs(look[i + 8]) + look[3]);
            }

            for (int i = 0; i < 4; i++)
            {
                if (nextFaces[0, i] != -1 &&
                    nextFaces[1, i] != -1 &&
                    nextFaces[2, i] != -1 &&
                    nextFaces[3, i] != -1)
                {
                    EdgeProc(depth + 1, nextFaces[0, i], nextFaces[1, i], nextFaces[2, i], nextFaces[3, i], lookEdge[look[12 + i / 2]]);
                }
            }
        }

        void EdgeProc(int depth, int node0, int node1, int node2, int node3, int[] look)
        {
            VoxelOctree.Node[] n = { octree.nodes[node0], octree.nodes[node1], octree.nodes[node2], octree.nodes[node3] };

            if (/*depth > traversalDepth ||*/ (n[0].isLeaf && n[1].isLeaf && n[2].isLeaf && n[3].isLeaf))
            {
                foreach (VoxelOctree.Node node in n)
                {
                    if (node.filled == 255)
                    {
                        // one of the leaves does not contain a contour
                        return;
                    }
                }
                // test for a sign change along the edge shared by these four cells
                int[] signChangeTests = { 1, 4, 2 };
                bool  flipFace;

                /*
                 * StringBuilder sb = new StringBuilder();
                 * sb.Append(look[0]);
                 * sb.Append(": ");
                 * for (int i = 0; i < 8; i++)
                 * {
                 *  sb.Append(n[2].isCornerFilled(i) ? 1 : 0);
                 * }
                 * sb.Append("\n");
                 * sb.Append(octree.cells[n[2].firstChild].cellMin);
                 * sb.Append(depth);
                 * UnityEngine.Debug.Log(sb.ToString());
                 */

                if (n[2].isCornerFilled(0) != (flipFace = n[2].isCornerFilled(signChangeTests[look[5] - 1])))
                {
                    List <int> face = new List <int> {
                        node0, node1, node2, node3
                    };

                    if (flipFace)
                    {
                        face.Reverse();
                    }

                    faces.Add(face[0]);
                    faces.Add(face[1]);
                    faces.Add(face[2]);
                    faces.Add(face[2]);
                    faces.Add(face[3]);
                    faces.Add(face[0]);
                }
            }
            else
            {
                // one or more of the children of this node are subdivided further, so we need to recurse twice.
                int[] nextNodes = { node0, node1, node2, node3, node0, node1, node2, node3 };

                for (int i = 0; i < 4; i++)
                {
                    if (!n[i].isLeaf)
                    {
                        nextNodes[i]     = n[i].getIndexOfChild(look[i]);
                        nextNodes[i + 4] = n[i].getIndexOfChild(look[i] + look[4]);
                    }
                }
                if (nextNodes[0] != -1 && nextNodes[1] != -1 && nextNodes[2] != -1 && nextNodes[3] != -1)
                {
                    EdgeProc(depth + 1, nextNodes[0], nextNodes[1], nextNodes[2], nextNodes[3], look);
                }
                if (nextNodes[4] != -1 && nextNodes[5] != -1 && nextNodes[6] != -1 && nextNodes[7] != -1)
                {
                    EdgeProc(depth + 1, nextNodes[4], nextNodes[5], nextNodes[6], nextNodes[7], look);
                }
            }
        }
    }
Ejemplo n.º 10
0
    public static VoxelOctree CreateBasedOnFunction(Func <Vector3, float> Source, Vector3 center, Vector3 size, int maximumDepth)
    {
        int numSamplesToEdge(int d)
        {
            return(1 << d);
        }

        Dictionary <Vector3Int, float> samples = new Dictionary <Vector3Int, float>();

        VoxelOctree octree = new VoxelOctree(center, size, maximumDepth);

        octree.nodes.Add(new Node());

        Vector3 min      = octree.rootAABB.min;
        int     n        = numSamplesToEdge(maximumDepth);
        Vector3 leafSize = (size / n);

        Populate(0, Vector3Int.zero, 0, 0, TestCell(Vector3Int.zero, 0));

        return(octree);

        byte TestCell(Vector3Int cellPosition, int depth)
        {
            int s = numSamplesToEdge(maximumDepth - depth);

            float[] sampleValues = new float[8];

            for (int i = 0; i < 8; i++)
            {
                Vector3Int samplePos = cellPosition + s * octantOrder[i];

                if (!samples.TryGetValue(samplePos, out sampleValues[i]))
                {
                    Vector3 pos = min + Vector3.Scale(leafSize, samplePos);
                    float   val = Source(pos);
                    samples.Add(samplePos, val);
                    sampleValues[i] = val;
                }
            }

            byte sign = 0;

            for (int i = 0; i < 8; i++)
            {
                sign <<= 1;
                if (sampleValues[i] < 0.0)
                {
                    sign += 1;
                }
            }
            return(sign);
        }

        void Populate(int cell, Vector3Int cellPosition, int depth, int octant, byte sign)
        {
            if (depth > minDepth && sign == 0) // this cell is completely outside the solid and does not need subdividing further
            {
                return;
            }
            else if (depth > minDepth && sign == 255) // this cell is completely inside the solid and does not need subdividing further
            {
                octree.nodes[cell] = Node.Leaf(octant, -1, Byte.MaxValue);
            }
            else  // this cell contains both empty space and part of the solid --
            {
                if (depth < maximumDepth) // so divide it into 8 children
                {
                    byte branchFilled = 0;
                    int  children     = 0;
                    int  firstChild   = octree.nodes.Count;

                    byte[] signs = new byte[8];

                    for (int i = 0; i < 8; i++)
                    {
                        Vector3Int testPosition = cellPosition + (numSamplesToEdge(maximumDepth - depth) / 2) * octantOrder[i];
                        signs[i] = TestCell(testPosition, depth + 1);

                        branchFilled <<= 1;

                        if (signs[i] > 0)
                        {
                            octree.nodes.Add(new Node());
                            children++;
                            branchFilled++;
                        }
                    }

                    octree.nodes[cell] = Node.Branch(octant, firstChild, children, branchFilled);

                    int c = 0;

                    for (int i = 0; i < 8; i++)
                    {
                        Vector3Int testPosition = cellPosition + (numSamplesToEdge(maximumDepth - depth) / 2) * octantOrder[i];
                        if ((branchFilled >> (7 - i)) % 2 == 1)
                        {
                            Populate(firstChild + c, testPosition, depth + 1, i, signs[i]);
                            c++;
                        }
                    }
                }
                else // but we are at the maximum allowed detail level, so put a vertex in this cell
                {
                    octree.nodes[cell] = Node.Leaf(octant, octree.cells.Count, sign);

                    CellData cellData = new CellData
                    {
                        cellMin  = min + Vector3.Scale(leafSize, cellPosition),
                        cellSize = leafSize.x
                    };
                    octree.cells.Add(cellData);
                }
            }
        }
    }