private void test() { tree = new VoxelOctree <Vector3>(depth, new Bounds(Vector3.zero, Vector3.one * size)); addTestData(); //showTraversals(); }
//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 }
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; }
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); }
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); } } } } }
/* 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); }
/// <summary> /// /// </summary> /// <param name="maxDepth"></param> /// <param name="voxelSizeThreshold"></param> public void GenerateOctree(int maxDepth, float voxelSizeThreshold) { Octree = VoxelOctree.Create(this, maxDepth, voxelSizeThreshold); }
/// <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 }
/* * 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); } } } }
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); } } } }