/// <summary> /// /// </summary> /// <param name="filename"></param> /// <param name="desiredDepth"></param> private bool LoadFromFile(string filename, int desiredDepth) { var array = File.ReadAllBytes(filename); var index = 0; var depth = BitConverter.ToInt32(array, index); index += sizeof(int); if (depth < desiredDepth) { return(false); } maxDepth = depth; Log.Instance.AddMsg(LogLevel.Info, string.Format("Loading VoxelOctree from '{0}.octree' ...", filename)); voxelSizeThreshold = BitConverter.ToSingle(array, index); index += sizeof(float); RootCell = new AACell(array, index); index += AACell.SizeOf; RootNode = new OctreeNode(array, index); index += OctreeNode.SizeOf; LoadNodes(array, index, RootNode); return(true); }
/// <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; }
public OmniLight(float radius) { BoundingBox = new AACell { Min = new Vector3(-radius), Max = new Vector3(radius) }; Color = new ColorRGB { R = 1f, G = 1f, B = 1f }; Intensity = 1f; }
/// <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="ray"></param> /// <param name="node"></param> /// <param name="maxDepth"></param> /// <returns></returns> private static IntersectionResult RayAndVoxelNode(Ray ray, OctreeNode node, AACell cell, int maxDepth, ref Vector3 normal) { var intersectionResult = new IntersectionResult { Voxel = node.Voxel, Point = cell.Center }; if (!Collision.RayAndAACell(ray, cell)) { return(intersectionResult); } var nodeInfo = node.Info; if (nodeInfo == OctreeNodeInfo.Empty || maxDepth == 0) { intersectionResult.Distance = Intersection.RayAndAACell(ray, cell, ref normal); return(intersectionResult); } var resultNormal = normal; maxDepth--; #region check child nodes for intersection var cellHalfSize = cell.Size.X * .5f; var cellCenter = cell.Center; var nextChildNodeId = 0; var childCell = cell; #region 000 if (nodeInfo.HasFlag(OctreeNodeInfo.x0y0z0)) { childCell = cell; childCell.Max = cellCenter; var tmpResult = RayAndVoxelNode(ray, node.OctreeNodes[nextChildNodeId], childCell, maxDepth, ref normal); if (tmpResult.Distance < intersectionResult.Distance) { resultNormal = normal; intersectionResult = tmpResult; } nextChildNodeId++; } #endregion #region 100 if (nodeInfo.HasFlag(OctreeNodeInfo.x1y0z0)) { childCell = cell; childCell.Max = cellCenter; childCell.Min.X += cellHalfSize; childCell.Max.X += cellHalfSize; var tmpResult = RayAndVoxelNode(ray, node.OctreeNodes[nextChildNodeId], childCell, maxDepth, ref normal); if (tmpResult.Distance < intersectionResult.Distance) { resultNormal = normal; intersectionResult = tmpResult; } nextChildNodeId++; } #endregion #region 110 if (nodeInfo.HasFlag(OctreeNodeInfo.x1y1z0)) { childCell = cell; childCell.Min = cellCenter; childCell.Min.Z -= cellHalfSize; childCell.Max.Z -= cellHalfSize; var tmpResult = RayAndVoxelNode(ray, node.OctreeNodes[nextChildNodeId], childCell, maxDepth, ref normal); if (tmpResult.Distance < intersectionResult.Distance) { resultNormal = normal; intersectionResult = tmpResult; } nextChildNodeId++; } #endregion #region 010 if (nodeInfo.HasFlag(OctreeNodeInfo.x0y1z0)) { childCell = cell; childCell.Max = cellCenter; childCell.Min.Y += cellHalfSize; childCell.Max.Y += cellHalfSize; var tmpResult = RayAndVoxelNode(ray, node.OctreeNodes[nextChildNodeId], childCell, maxDepth, ref normal); if (tmpResult.Distance < intersectionResult.Distance) { resultNormal = normal; intersectionResult = tmpResult; } nextChildNodeId++; } #endregion #region 001 if (nodeInfo.HasFlag(OctreeNodeInfo.x0y0z1)) { childCell = cell; childCell.Max = cellCenter; childCell.Min.Z += cellHalfSize; childCell.Max.Z += cellHalfSize; var tmpResult = RayAndVoxelNode(ray, node.OctreeNodes[nextChildNodeId], childCell, maxDepth, ref normal); if (tmpResult.Distance < intersectionResult.Distance) { resultNormal = normal; intersectionResult = tmpResult; } nextChildNodeId++; } #endregion #region 101 if (nodeInfo.HasFlag(OctreeNodeInfo.x1y0z1)) { childCell = cell; childCell.Min = cellCenter; childCell.Min.Y -= cellHalfSize; childCell.Max.Y -= cellHalfSize; var tmpResult = RayAndVoxelNode(ray, node.OctreeNodes[nextChildNodeId], childCell, maxDepth, ref normal); if (tmpResult.Distance < intersectionResult.Distance) { resultNormal = normal; intersectionResult = tmpResult; } nextChildNodeId++; } #endregion #region 111 if (nodeInfo.HasFlag(OctreeNodeInfo.x1y1z1)) { childCell = cell; childCell.Min = cellCenter; var tmpResult = RayAndVoxelNode(ray, node.OctreeNodes[nextChildNodeId], childCell, maxDepth, ref normal); if (tmpResult.Distance < intersectionResult.Distance) { resultNormal = normal; intersectionResult = tmpResult; } nextChildNodeId++; } #endregion #region 011 if (nodeInfo.HasFlag(OctreeNodeInfo.x0y1z1)) { childCell = cell; childCell.Min = cellCenter; childCell.Min.X -= cellHalfSize; childCell.Max.X -= cellHalfSize; var tmpResult = RayAndVoxelNode(ray, node.OctreeNodes[nextChildNodeId], childCell, maxDepth, ref normal); if (tmpResult.Distance < intersectionResult.Distance) { resultNormal = normal; intersectionResult = tmpResult; } nextChildNodeId++; } #endregion #endregion if (intersectionResult.Distance < float.MaxValue) { normal = resultNormal; } return(intersectionResult); }
/// <summary> /// /// </summary> /// <param name="ray"></param> /// <param name="cell"></param> /// <param name="normal"></param> /// <returns></returns> private static float RayAndAACell(Ray ray, AACell cell, ref Vector3 normal) { var tMin = float.MaxValue; // ray from outside if (!Collision.PointInAACell(ray.Origin, cell)) { #region --> cell.Min.X, cell.Max.X <-- if (ray.Origin.X <= cell.Min.X) { var t = Intersection.RayAndPlane(ray, Vector3.UnitXNegative, System.Math.Abs(cell.Min.X)); if (t < tMin) { var hit = ray.Origin.Y + ray.Direction.Y * t; if (hit >= cell.Min.Y && hit <= cell.Max.Y) { hit = ray.Origin.Z + ray.Direction.Z * t; if (hit >= cell.Min.Z && hit <= cell.Max.Z) { tMin = t; normal = Vector3.UnitXNegative; } } } } else if (ray.Origin.X >= cell.Max.X) { var t = Intersection.RayAndPlane(ray, Vector3.UnitX, System.Math.Abs(cell.Max.X)); if (t < tMin) { var hit = ray.Origin.Y + ray.Direction.Y * t; if (hit >= cell.Min.Y && hit <= cell.Max.Y) { hit = ray.Origin.Z + ray.Direction.Z * t; if (hit >= cell.Min.Z && hit <= cell.Max.Z) { tMin = t; normal = Vector3.UnitX; } } } } #endregion #region --> cell.Min.Y, cell.Max.Y <-- if (ray.Origin.Y <= cell.Min.Y) { var t = Intersection.RayAndPlane(ray, Vector3.UnitYNegative, System.Math.Abs(cell.Min.Y)); if (t < tMin) { var hit = ray.Origin.X + ray.Direction.X * t; if (hit > cell.Min.X && hit < cell.Max.X) { hit = ray.Origin.Z + ray.Direction.Z * t; if (hit > cell.Min.Z && hit < cell.Max.Z) { tMin = t; normal = Vector3.UnitYNegative; } } } } else if (ray.Origin.Y >= cell.Max.Y) { var t = Intersection.RayAndPlane(ray, Vector3.UnitY, System.Math.Abs(cell.Max.Y)); if (t < tMin) { var hit = ray.Origin.X + ray.Direction.X * t; if (hit > cell.Min.X && hit < cell.Max.X) { hit = ray.Origin.Z + ray.Direction.Z * t; if (hit > cell.Min.Z && hit < cell.Max.Z) { tMin = t; normal = Vector3.UnitY; } } } } #endregion #region --> cell.Min.Z, cell.Max.Z <-- if (ray.Origin.Z <= cell.Min.Z) { var t = Intersection.RayAndPlane(ray, Vector3.UnitZNegative, System.Math.Abs(cell.Min.Z)); if (t < tMin) { var hit = ray.Origin.X + ray.Direction.X * t; if (hit > cell.Min.X && hit < cell.Max.X) { hit = ray.Origin.Y + ray.Direction.Y * t; if (hit > cell.Min.Y && hit < cell.Max.Y) { tMin = t; normal = Vector3.UnitZNegative; } } } } else if (ray.Origin.Z >= cell.Max.Z) { var t = Intersection.RayAndPlane(ray, Vector3.UnitZ, System.Math.Abs(cell.Max.Z)); if (t < tMin) { var hit = ray.Origin.X + ray.Direction.X * t; if (hit > cell.Min.X && hit < cell.Max.X) { hit = ray.Origin.Y + ray.Direction.Y * t; if (hit > cell.Min.Y && hit < cell.Max.Y) { tMin = t; normal = Vector3.UnitZ; } } } } #endregion } // ray from inside else { #region cell.Min.X <-- --> cell.Max.X if (ray.Direction.X > 0f) { var t = Intersection.RayAndPlane(ray, Vector3.UnitX, cell.Max.X); if (t < tMin) { var hit = ray.Origin.Y + ray.Direction.Y * t; if (hit > cell.Min.Y && hit < cell.Max.Y) { hit = ray.Origin.Z + ray.Direction.Z * t; if (hit > cell.Min.Z && hit < cell.Max.Z) { tMin = t; normal = Vector3.UnitXNegative; } } } } else { var t = Intersection.RayAndPlane(ray, Vector3.UnitXNegative, cell.Min.X); if (t < tMin) { var hit = ray.Origin.Y + ray.Direction.Y * t; if (hit > cell.Min.Y && hit < cell.Max.Y) { hit = ray.Origin.Z + ray.Direction.Z * t; if (hit > cell.Min.Z && hit < cell.Max.Z) { tMin = t; normal = Vector3.UnitX; } } } } #endregion #region cell.Min.Y <-- --> cell.Max.Y if (ray.Direction.Y > 0) { var t = Intersection.RayAndPlane(ray, Vector3.UnitY, cell.Max.Y); if (t < tMin) { var hit = ray.Origin.X + ray.Direction.X * t; if (hit > cell.Min.X && hit < cell.Max.X) { hit = ray.Origin.Z + ray.Direction.Z * t; if (hit > cell.Min.Z && hit < cell.Max.Z) { tMin = t; normal = Vector3.UnitYNegative; } } } } else { var t = Intersection.RayAndPlane(ray, Vector3.UnitYNegative, cell.Min.Y); if (t < tMin) { var hit = ray.Origin.X + ray.Direction.X * t; if (hit > cell.Min.X && hit < cell.Max.X) { hit = ray.Origin.Z + ray.Direction.Z * t; if (hit > cell.Min.Z && hit < cell.Max.Z) { tMin = t; normal = Vector3.UnitY; } } } } #endregion #region cell.Min.Z <-- --> cell.Max.Z if (ray.Direction.Z > 0) { var t = Intersection.RayAndPlane(ray, Vector3.UnitZ, cell.Max.Z); if (t < tMin) { var hit = ray.Origin.X + ray.Direction.X * t; if (hit > cell.Min.X && hit < cell.Max.X) { hit = ray.Origin.Y + ray.Direction.Y * t; if (hit > cell.Min.Y && hit < cell.Max.Y) { tMin = t; normal = Vector3.UnitZNegative; } } } } else { var t = Intersection.RayAndPlane(ray, Vector3.UnitZNegative, cell.Min.Z); if (t < tMin) { var hit = ray.Origin.X + ray.Direction.X * t; if (hit > cell.Min.X && hit < cell.Max.X) { hit = ray.Origin.Y + ray.Direction.Y * t; if (hit > cell.Min.Y && hit < cell.Max.Y) { tMin = t; normal = Vector3.UnitZ; } } } } #endregion } return(tMin); }
/// <summary> /// /// </summary> private void LoadFromFile(string objFilename) { Filename = objFilename; // TODO read material file *.mtl var lines = File.ReadAllLines(objFilename); var timer = new HighPerformanceTimer(); timer.Start(); var min = new Vector3(float.MaxValue); var max = new Vector3(float.MinValue); for (var i = 0; i < lines.Length; i++) { var line = lines[i]; try { if (line.StartsWith("# object")) { if (string.IsNullOrEmpty(Name)) { Name = line.Substring("# object ".Length); } } // vertex else if (line.StartsWith("v ")) { var parts = line.Substring("v ".Length).Replace('.', ',').Split(' '); var vertex = new Vector3(float.Parse(parts[0], System.Globalization.NumberStyles.Float), float.Parse(parts[1]), float.Parse(parts[2])); if (vertex.X < min.X) { min.X = vertex.X; } if (vertex.Y < min.Y) { min.Y = vertex.Y; } if (vertex.Z < min.Z) { min.Z = vertex.Z; } if (vertex.X > max.X) { max.X = vertex.X; } if (vertex.Y > max.Y) { max.Y = vertex.Y; } if (vertex.Z > max.Z) { max.Z = vertex.Z; } Vertices.Add(vertex); } // vertex normal else if (line.StartsWith("vn ")) { var parts = line.Substring("vn ".Length).Replace('.', ',').Split(' '); Normals.Add(new Vector3(float.Parse(parts[0]), float.Parse(parts[1]), float.Parse(parts[2]))); } // vertex texture coordinates else if (line.StartsWith("vt ")) { var parts = line.Substring("vt ".Length).Replace('.', ',').Split(' '); TextureCoords.Add(new Vector2(float.Parse(parts[0]), float.Parse(parts[1]))); } // face indices else if (line.StartsWith("f ")) { var parts = line.Substring("f ".Length).Split(' '); var v1Parts = parts[0].Split('/'); var v2Parts = parts[1].Split('/'); var v3Parts = parts[2].Split('/'); Faces.Add( new Face( this, int.Parse(v1Parts[0]) - 1, int.Parse(v2Parts[0]) - 1, int.Parse(v3Parts[0]) - 1, int.Parse(v1Parts[1]) - 1, int.Parse(v2Parts[1]) - 1, int.Parse(v3Parts[1]) - 1, int.Parse(v1Parts[2]) - 1, int.Parse(v2Parts[2]) - 1, int.Parse(v3Parts[2]) - 1 ) ); } } catch (Exception ex) { throw new Exception(string.Format("ObjImporter failed at line='{0}'", line), ex); } } BoundingBox = new AACell { Min = min, Max = max }; timer.Stop(); Log.Instance.AddMsg(LogLevel.Info, string.Format("WavefrontObjMesh '{0}' [vertices: {1}; faces: {2}; size: {3}] loaded in {4}", Name, Vertices.Count, Faces.Count, BoundingBox.Size.ToString(), FormatString.GetDuration((int)timer.Duration))); }
/// <summary> /// /// </summary> /// <param name="terrainSize"></param> /// <param name="maxTerrainHeight"></param> /// <param name="heightMap"></param> /// <param name="textureMap"></param> /// <returns></returns> public void CreateFromHeightMap(float terrainSize, float maxTerrainHeight, float[] heightMap, ColorRGB[] textureMap) { var heightMapSize = (int)System.Math.Sqrt(heightMap.Length); var triangleSize = terrainSize / heightMapSize; var mapIndex = 0; var terrainSizeLimit = terrainSize * .5f; var actualMaxHeight = 0f; // if no texture map is specified, color of vertex height is used if (textureMap == null) { textureMap = new ColorRGB[heightMap.Length]; for (var i = 0; i < heightMap.Length; i++) { textureMap[i] = new ColorRGB(heightMap[i]); } } #region generate vertices for (var z = -terrainSizeLimit + triangleSize * .5f; z < terrainSizeLimit; z += triangleSize) { for (var x = -terrainSizeLimit + triangleSize * .5f; x < terrainSizeLimit; x += triangleSize, mapIndex++) { var vertexHeight = heightMap[mapIndex] * maxTerrainHeight; if (vertexHeight > actualMaxHeight) { actualMaxHeight = vertexHeight; } Vertices.Add(new Vector3(x, vertexHeight, z)); } } #endregion // set bounding box (center will be at [0,0,0] actualMaxHeight *= .5f; BoundingBox = new AACell { Min = new Vector3(-terrainSizeLimit + triangleSize * .5f, -actualMaxHeight, -terrainSizeLimit + triangleSize * .5f), Max = new Vector3(terrainSizeLimit - triangleSize * .5f, actualMaxHeight, terrainSizeLimit - triangleSize * .5f) }; // update vertex heights(Y) (so bounding box center will be at [0,0,0]) for (var i = 0; i < Vertices.Count; i++) { var vertex = Vertices[i]; vertex.Y -= actualMaxHeight; Vertices[i] = vertex; } #region generate faces for (var y = 0; y < heightMapSize - 1; y++) { for (var x = 0; x < heightMapSize - 1; x++) { var lowerLeft = x + y * heightMapSize; var lowerRight = (x + 1) + y * heightMapSize; var topLeft = x + (y + 1) * heightMapSize; var topRight = (x + 1) + (y + 1) * heightMapSize; Faces.Add(new Face(this, topLeft, lowerRight, lowerLeft, -1, -1, -1, -1, -1, -1)); Faces.Add(new Face(this, topLeft, topRight, lowerRight, -1, -1, -1, -1, -1, -1)); } } #endregion }
/// <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(); } }