/// <summary> /// /// </summary> /// <param name="obj"></param> /// <param name="nodeCell"></param> /// <param name="node"></param> /// <param name="processFinishedEvent"></param> public ProessNodeInfo(IVoxelizable obj, AACell nodeCell, OctreeNode node, CountdownEvent processFinishedEvent) { Obj = obj; NodeCell = nodeCell; Node = node; ProcessFinishedEvent = processFinishedEvent; }
/// <summary> /// /// </summary> /// <param name="obj"></param> /// <param name="nodeCell"></param> /// <returns></returns> private static bool IsNodeEmpty(IVoxelizable obj, AACell nodeCell) { if (obj is Mesh) { var faces = (obj as Mesh).Faces; foreach (var face in faces) { if (Collision.AACellAndFace(nodeCell, face)) { return(false); } } } else if (obj is Sphere) { return(!Collision.AACellAndSphere(nodeCell, (obj as Sphere).Radius)); } return(true); }
/// <summary> /// /// </summary> /// <param name="obj"></param> /// <param name="nodeCell"></param> /// <returns></returns> private static Voxel GetVoxel(IVoxelizable obj, AACell nodeCell) { // TODO get color/normal/material of voxel at collision point //var color = new ColorRGB((int)DateTime.Now.Ticks); var color = new ColorRGB((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); var voxel = new Voxel(color, Vector3.Zero); if (obj is Sphere) { voxel.Normal = nodeCell.Center.Normalize(); } else { // temporary, not accurate! voxel.Normal = nodeCell.Center.Normalize(); } return(voxel); }
/// <summary> /// /// </summary> /// <param name="obj"></param> /// <param name="nodeCell"></param> /// <param name="node"></param> /// <returns></returns> private void ProcessNodeMultithreaded(IVoxelizable obj, AACell nodeCell, OctreeNode node) { var center = nodeCell.Center; var childNodeSize = (nodeCell.Max.X - nodeCell.Min.X) * .5f; var nodeInfo = OctreeNodeInfo.Empty; var childCell = nodeCell; if (childNodeSize > voxelSizeThreshold && maxDepth > 0) { #region check child nodes // xyz childCell.Max = center; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x0y0z0; } // Xyz childCell.Min.X += childNodeSize; childCell.Max.X += childNodeSize; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x1y0z0; } // XYz childCell.Min.Y += childNodeSize; childCell.Max.Y += childNodeSize; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x1y1z0; } // xYz childCell.Min.X -= childNodeSize; childCell.Max.X -= childNodeSize; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x0y1z0; } // xyZ childCell.Min.Y -= childNodeSize; childCell.Max.Y -= childNodeSize; childCell.Min.Z += childNodeSize; childCell.Max.Z += childNodeSize; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x0y0z1; } // XyZ childCell.Min.X += childNodeSize; childCell.Max.X += childNodeSize; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x1y0z1; } // XYZ childCell.Min.Y += childNodeSize; childCell.Max.Y += childNodeSize; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x1y1z1; } // xYZ childCell.Min.X -= childNodeSize; childCell.Max.X -= childNodeSize; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x0y1z1; } #endregion } node.Info = nodeInfo; if (nodeInfo == OctreeNodeInfo.Empty) { node.Voxel = GetVoxel(obj, nodeCell); } else { var childNodes = new OctreeNode[8]; var childNodesProcessEvent = new CountdownEvent(8); #region process child nodes // xyz childCell.Min = nodeCell.Min; childCell.Max = center; if (nodeInfo.HasFlag(OctreeNodeInfo.x0y0z0)) { childNodes[0] = new OctreeNode(); ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessNodeOnThread), new ProessNodeInfo(obj, childCell, childNodes[0], childNodesProcessEvent)); } // Xyz childCell.Min.X += childNodeSize; childCell.Max.X += childNodeSize; if (nodeInfo.HasFlag(OctreeNodeInfo.x1y0z0)) { childNodes[1] = new OctreeNode(); ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessNodeOnThread), new ProessNodeInfo(obj, childCell, childNodes[1], childNodesProcessEvent)); } // XYz childCell.Min.Y += childNodeSize; childCell.Max.Y += childNodeSize; if (nodeInfo.HasFlag(OctreeNodeInfo.x1y1z0)) { childNodes[2] = new OctreeNode(); ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessNodeOnThread), new ProessNodeInfo(obj, childCell, childNodes[2], childNodesProcessEvent)); } // xYz childCell.Min.X -= childNodeSize; childCell.Max.X -= childNodeSize; if (nodeInfo.HasFlag(OctreeNodeInfo.x0y1z0)) { childNodes[3] = new OctreeNode(); ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessNodeOnThread), new ProessNodeInfo(obj, childCell, childNodes[3], childNodesProcessEvent)); } // xyZ childCell.Min.Y -= childNodeSize; childCell.Max.Y -= childNodeSize; childCell.Min.Z += childNodeSize; childCell.Max.Z += childNodeSize; if (nodeInfo.HasFlag(OctreeNodeInfo.x0y0z1)) { childNodes[4] = new OctreeNode(); ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessNodeOnThread), new ProessNodeInfo(obj, childCell, childNodes[4], childNodesProcessEvent)); } // XyZ childCell.Min.X += childNodeSize; childCell.Max.X += childNodeSize; if (nodeInfo.HasFlag(OctreeNodeInfo.x1y0z1)) { childNodes[5] = new OctreeNode(); ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessNodeOnThread), new ProessNodeInfo(obj, childCell, childNodes[5], childNodesProcessEvent)); } // XYZ childCell.Min.Y += childNodeSize; childCell.Max.Y += childNodeSize; if (nodeInfo.HasFlag(OctreeNodeInfo.x1y1z1)) { childNodes[6] = new OctreeNode(); ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessNodeOnThread), new ProessNodeInfo(obj, childCell, childNodes[6], childNodesProcessEvent)); } // xYZ childCell.Min.X -= childNodeSize; childCell.Max.X -= childNodeSize; if (nodeInfo.HasFlag(OctreeNodeInfo.x0y1z1)) { childNodes[7] = new OctreeNode(); ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessNodeOnThread), new ProessNodeInfo(obj, childCell, childNodes[7], childNodesProcessEvent)); } #endregion // wait for threads to end childNodesProcessEvent.Wait(); node.OctreeNodes = (from n in childNodes where n != null select n).ToList(); node.SetVoxelAsAverage(); } }
/// <summary> /// /// </summary> /// <param name="mesh"></param> /// <param name="nodeCell"></param> /// <param name="node"></param> /// <returns></returns> private void ProcessNode(IVoxelizable obj, AACell nodeCell, OctreeNode node, int nodeDepth) { var center = nodeCell.Center; var childNodeSize = (nodeCell.Max.X - nodeCell.Min.X) * .5f; var nodeInfo = OctreeNodeInfo.Empty; var childCell = nodeCell; if (childNodeSize > voxelSizeThreshold && maxDepth > nodeDepth) { #region check child nodes // xyz childCell.Max = center; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x0y0z0; } // Xyz childCell.Min.X += childNodeSize; childCell.Max.X += childNodeSize; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x1y0z0; } // XYz childCell.Min.Y += childNodeSize; childCell.Max.Y += childNodeSize; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x1y1z0; } // xYz childCell.Min.X -= childNodeSize; childCell.Max.X -= childNodeSize; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x0y1z0; } // xyZ childCell.Min.Y -= childNodeSize; childCell.Max.Y -= childNodeSize; childCell.Min.Z += childNodeSize; childCell.Max.Z += childNodeSize; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x0y0z1; } // XyZ childCell.Min.X += childNodeSize; childCell.Max.X += childNodeSize; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x1y0z1; } // XYZ childCell.Min.Y += childNodeSize; childCell.Max.Y += childNodeSize; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x1y1z1; } // xYZ childCell.Min.X -= childNodeSize; childCell.Max.X -= childNodeSize; if (!IsNodeEmpty(obj, childCell)) { nodeInfo |= OctreeNodeInfo.x0y1z1; } #endregion } node.Info = nodeInfo; if (nodeInfo == OctreeNodeInfo.Empty) { node.Voxel = GetVoxel(obj, nodeCell); } else { nodeDepth++; var childNodes = new OctreeNode[8]; #region process child nodes // xyz childCell.Min = nodeCell.Min; childCell.Max = center; if (nodeInfo.HasFlag(OctreeNodeInfo.x0y0z0)) { childNodes[0] = new OctreeNode(); ProcessNode(obj, childCell, childNodes[0], nodeDepth); } // Xyz childCell.Min.X += childNodeSize; childCell.Max.X += childNodeSize; if (nodeInfo.HasFlag(OctreeNodeInfo.x1y0z0)) { childNodes[1] = new OctreeNode(); ProcessNode(obj, childCell, childNodes[1], nodeDepth); } // XYz childCell.Min.Y += childNodeSize; childCell.Max.Y += childNodeSize; if (nodeInfo.HasFlag(OctreeNodeInfo.x1y1z0)) { childNodes[2] = new OctreeNode(); ProcessNode(obj, childCell, childNodes[2], nodeDepth); } // xYz childCell.Min.X -= childNodeSize; childCell.Max.X -= childNodeSize; if (nodeInfo.HasFlag(OctreeNodeInfo.x0y1z0)) { childNodes[3] = new OctreeNode(); ProcessNode(obj, childCell, childNodes[3], nodeDepth); } // xyZ childCell.Min.Y -= childNodeSize; childCell.Max.Y -= childNodeSize; childCell.Min.Z += childNodeSize; childCell.Max.Z += childNodeSize; if (nodeInfo.HasFlag(OctreeNodeInfo.x0y0z1)) { childNodes[4] = new OctreeNode(); ProcessNode(obj, childCell, childNodes[4], nodeDepth); } // XyZ childCell.Min.X += childNodeSize; childCell.Max.X += childNodeSize; if (nodeInfo.HasFlag(OctreeNodeInfo.x1y0z1)) { childNodes[5] = new OctreeNode(); ProcessNode(obj, childCell, childNodes[5], nodeDepth); } // XYZ childCell.Min.Y += childNodeSize; childCell.Max.Y += childNodeSize; if (nodeInfo.HasFlag(OctreeNodeInfo.x1y1z1)) { childNodes[6] = new OctreeNode(); ProcessNode(obj, childCell, childNodes[6], nodeDepth); } // xYZ childCell.Min.X -= childNodeSize; childCell.Max.X -= childNodeSize; if (nodeInfo.HasFlag(OctreeNodeInfo.x0y1z1)) { childNodes[7] = new OctreeNode(); ProcessNode(obj, childCell, childNodes[7], nodeDepth); } #endregion node.OctreeNodes = (from n in childNodes where n != null select n).ToList(); node.SetVoxelAsAverage(); } }
/// <summary> /// /// </summary> /// <param name="obj"></param> /// <param name="maxDepth"></param> /// <param name="voxelSizeThreshold"></param> /// <returns></returns> public static VoxelOctree Create(IVoxelizable obj, int maxDepth, float voxelSizeThreshold) { var octree = new VoxelOctree(maxDepth, voxelSizeThreshold); var timer = new HighPerformanceTimer(); timer.Start(); var cellSize = obj.BoundingBox.Size; var newCellSize = (cellSize.X > cellSize.Y ? (cellSize.X > cellSize.Z ? cellSize.X : cellSize.Z) : (cellSize.Y > cellSize.Z ? cellSize.Y : cellSize.Z)) * 0.5f; var generateNewOctree = true; if (obj is WavefrontObjMesh && File.Exists((obj as WavefrontObjMesh).Filename + ".octree")) { generateNewOctree = !octree.LoadFromFile((obj as WavefrontObjMesh).Filename + ".octree", maxDepth); } if (generateNewOctree) { #region calculate RootCell (equal edge sizes) var center = obj.BoundingBox.Center; octree.RootCell = new AACell { Min = new Vector3(center.X - newCellSize, center.Y - newCellSize, center.Z - newCellSize), Max = new Vector3(center.X + newCellSize, center.Y + newCellSize, center.Z + newCellSize) }; #endregion // create octree recursively octree.RootNode = new OctreeNode(); if (VoxelOctree.GenerateOnMultipleThreads) { Log.Instance.AddMsg(LogLevel.Info, string.Format("Generating VoxelOctree/MT [maxDepth: {0}; voxelSizeThreshold: {1}] ...", maxDepth, voxelSizeThreshold)); octree.ProcessNodeMultithreaded(obj, octree.RootCell, octree.RootNode); } else { Log.Instance.AddMsg(LogLevel.Info, string.Format("Generating VoxelOctree/ST [maxDepth: {0}; voxelSizeThreshold: {1}] ...", maxDepth, voxelSizeThreshold)); octree.ProcessNode(obj, octree.RootCell, octree.RootNode, 0); } } timer.Stop(); // count all nodes var nodeCount = CountNodesRecursive(octree.RootNode) + 1; // get actual tree depth octree.maxDepth = VoxelOctree.GetMaxDepth(octree.RootNode); #region calculate smallest voxel size var smallestVoxelSize = newCellSize * 2f; for (var i = 0; i < octree.maxDepth; i++) { smallestVoxelSize *= .5f; } #endregion Log.Instance.AddMsg(LogLevel.Info, string.Format("VoxelOctree [size: {0}; nodes: {1}; depth: {2}; voxelSize: {3}] generated in {4}", newCellSize * 2f, nodeCount, octree.maxDepth, smallestVoxelSize, FormatString.GetDuration((int)timer.Duration))); if (obj is WavefrontObjMesh && generateNewOctree) { octree.SaveToFile((obj as WavefrontObjMesh).Filename + ".octree"); } return(octree); }