public VoxelizingOctreeCell(VoxelizingOctree tree, VoxelizingOctreeCell root, Vector3 center, float length, int level) { Tree = tree; Root = root; Center = center; Length = length; Level = level; float half_length = length / 2.0f; Bounds = new AABBf(); Bounds.MinX = center.X - half_length; Bounds.MinY = center.Y - half_length; Bounds.MinZ = center.Z - half_length; Bounds.MaxX = center.X + half_length; Bounds.MaxY = center.Y + half_length; Bounds.MaxZ = center.Z + half_length; VoxelBounds = new AABBi( (int)Math.Round((Bounds.MinX - tree.VoxelBounds.MinX) / tree.SmallestVoxelSideLength, MidpointRounding.AwayFromZero), (int)Math.Round((Bounds.MinY - tree.VoxelBounds.MinY) / tree.SmallestVoxelSideLength, MidpointRounding.AwayFromZero), (int)Math.Round((Bounds.MinZ - tree.VoxelBounds.MinZ) / tree.SmallestVoxelSideLength, MidpointRounding.AwayFromZero), (int)Math.Round((Bounds.MaxX - tree.VoxelBounds.MinX) / tree.SmallestVoxelSideLength, MidpointRounding.AwayFromZero), (int)Math.Round((Bounds.MaxY - tree.VoxelBounds.MinY) / tree.SmallestVoxelSideLength, MidpointRounding.AwayFromZero), (int)Math.Round((Bounds.MaxZ - tree.VoxelBounds.MinZ) / tree.SmallestVoxelSideLength, MidpointRounding.AwayFromZero)); }
// computes the OBB for this set of points relative to this transform matrix. public static void ComputeOBB(Vector3[] points, ref Matrix4 matrix, out float[] sides) { AABBf aabb = new AABBf(); Matrix4 matrixInverse = matrix; matrixInverse.Invert(); for (int i = 0; i < points.Length; i++) { Vector3 t = Vector3.Transform(points[i], matrixInverse); // inverse rotate translate aabb.Add(t); } sides = new float[3]; sides[0] = aabb.MaxX - aabb.MinX; sides[1] = aabb.MaxY - aabb.MinY; sides[2] = aabb.MaxZ - aabb.MinZ; Vector3 center = new Vector3(); center.X = sides[0] * 0.5f + aabb.MinX; center.Y = sides[1] * 0.5f + aabb.MinY; center.Z = sides[2] * 0.5f + aabb.MinZ; Vector3 ocenter = Vector3.Transform(center, matrix); matrix = matrix * Matrix4.CreateTranslation(ocenter); }
public static Mesh BuildMesh(List <AABBf> boxList) { Mesh mesh = new Mesh(); if (boxList.Count == 0) { mesh.Vertices = new Vector4[0]; mesh.Indicies = new int[0]; return(mesh); } List <CSGNode> nodes = new List <CSGNode>(); for (int i = 0; i < boxList.Count; i++) { AABBf box = boxList[i]; CSGNode node = new CSGNode(new Plane[] { new Plane(0, -1, 0, 0), new Plane(-1, 0, 0, 0), new Plane(0, 0, -1, 0), new Plane(0, 0, 1, box.MaxZ - box.MinZ), new Plane(1, 0, 0, box.MaxX - box.MinX), new Plane(0, 1, 0, box.MaxY - box.MinY) }); node.Translation = new Vector3(box.MinX, box.MinY, box.MinZ); nodes.Add(node); } return(BuildMesh(nodes)); }
public bool Intersects(AABBf other) { return ((MaxX > other.MinX && MaxX < other.MaxX) || (MinX > other.MinX && MinX < other.MaxX) || (MaxY > other.MinY && MaxY < other.MaxY) || (MinY > other.MinY && MinY < other.MaxY) || (MaxZ > other.MinZ && MaxZ < other.MaxZ) || (MinZ > other.MinZ && MinZ < other.MaxZ)); }
public void ComputeCoverage(RenderableMesh mesh, AABBf meshBounds, out long sideCoverage, out long topCoverage) { float longestSide = Math.Max(Math.Max(meshBounds.MaxX - meshBounds.MinX, meshBounds.MaxY - meshBounds.MinY), meshBounds.MaxZ - meshBounds.MinZ); float farPlane = longestSide * 2.0f; float x = (meshBounds.MinX + meshBounds.MaxX) / 2.0f; float y = meshBounds.MinY; float z = (meshBounds.MinZ + meshBounds.MaxZ) / 2.0f; Vector3 origin = new Vector3(x, y, z); Vector3 up = Vector3.UnitY; Vector3 look = -Vector3.UnitX; for (int i = 0; i < m_occlusionQueries.Length; i++) { Matrix4 orbit = Matrix4.CreateRotationY(MathHelper.DegreesToRadians((365 / 64.0f) * i)); Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(90), 1.0f, 0.1f, farPlane); Matrix4 view = Matrix4.LookAt(origin + Vector3.Transform(new Vector3(longestSide, 0, 0), orbit), origin, Vector3.TransformNormal(up, orbit)); RenderView(view, projection, mesh, m_occlusionQueries[i]); //Bitmap bmp = GLEx.BitmapColorBuffer(m_pixelWidth, m_pixelHeight); //bmp.Save("C:\\test_" + i + ".bmp"); } // Gather all the occlusion queries we performed long[] m_occlusionQueryResults = new long[64]; for (int i = 0; i < m_occlusionQueries.Length; i++) { int ready = 0; while (ready == 0) { GL.GetQueryObject(m_occlusionQueries[i], GetQueryObjectParam.QueryResultAvailable, out ready); } GL.GetQueryObject(m_occlusionQueries[i], GetQueryObjectParam.QueryResult, out m_occlusionQueryResults[i]); } // Reset the current frame buffer. GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0); long totalSidePixels = 0; long totalTopPixels = 0; for (int i = 0; i < m_occlusionQueries.Length; i++) { totalSidePixels += m_occlusionQueryResults[i]; } sideCoverage = totalSidePixels; topCoverage = totalTopPixels; }
public AABBf Clone() { AABBf clone = new AABBf(); clone.MaxX = this.MaxX; clone.MaxY = this.MaxY; clone.MaxZ = this.MaxZ; clone.MinX = this.MinX; clone.MinY = this.MinY; clone.MinZ = this.MinZ; return(clone); }
public static AABBf CreateBoundingBox(IEnumerable <Triangle> triangles) { AABBf box = new AABBf(); foreach (Triangle t in triangles) { box.Add(t.v0); box.Add(t.v1); box.Add(t.v2); } return(box); }
public static Mesh BuildMesh(AABBf aabb, Vector3 delta_p, List <AABBi> boxList) { Mesh mesh = BuildMesh(boxList); Vector4 aabb_min = new Vector4(aabb.MinX, aabb.MinY, aabb.MinZ, 0); Vector4 delta_p4 = new Vector4(delta_p, 1); for (int i = 0; i < mesh.Vertices.Length; i++) { mesh.Vertices[i] = aabb_min + Vector4.Multiply(mesh.Vertices[i], delta_p4); } return(mesh); }
private void PropagateStatus(VoxelizingOctreeCell seed, CellStatus status, VoxelizingOctreeCell current) { Vector4[] cell_bounds_verts = new Vector4[8]; AABBf bounds = current.Bounds; cell_bounds_verts[0] = new Vector4(bounds.MaxX, bounds.MaxY, bounds.MaxZ, 1); cell_bounds_verts[1] = new Vector4(bounds.MaxX, bounds.MaxY, bounds.MinZ, 1); cell_bounds_verts[2] = new Vector4(bounds.MinX, bounds.MaxY, bounds.MinZ, 1); cell_bounds_verts[3] = new Vector4(bounds.MinX, bounds.MaxY, bounds.MaxZ, 1); cell_bounds_verts[4] = new Vector4(bounds.MaxX, bounds.MinY, bounds.MaxZ, 1); cell_bounds_verts[5] = new Vector4(bounds.MaxX, bounds.MinY, bounds.MinZ, 1); cell_bounds_verts[6] = new Vector4(bounds.MinX, bounds.MinY, bounds.MinZ, 1); cell_bounds_verts[7] = new Vector4(bounds.MinX, bounds.MinY, bounds.MaxZ, 1); // Loop over the number of cube faces for (int i = 0; i < 6; i++) { // Loop over the 8 vertices that make up the corners of a voxel cell. If any of the corners is visible // the voxel will have the status accumulated into it. for (int n = 0; n < 8; n++) { Vector4 result; Vector4.Transform(ref cell_bounds_verts[n], ref cubemapProjections[i], out result); float x = result.X / result.W; float y = result.Y / result.W; float z = result.Z / result.W; if (x >= -1.0f && x <= 1.0f && y >= -1.0f && y <= 1.0f) { int depthBufferX = (int)(((x + 1.0f) / 2.0f) * (CubemapWidth - 1)); int depthBufferY = (int)(((y + 1.0f) / 2.0f) * (CubemapHeight - 1)); float sampledDepth = cubemapDepthBuffers[i][depthBufferY * CubemapWidth + depthBufferX]; float ndc_sampledDepth = ((sampledDepth * 2.0f) - 1.0f); if (z > -1.0f && z < ndc_sampledDepth) { // Accumulate the status on the cell must overcome threshold to be confirmed as inside or outside. // If enough other voxels propagate the same status to the cell, it becomes a cell of that type. if (current.AccumulateStatus(status)) { return; } } } } } }
private void CreateUniformBoundingBox(List <Triangle> triangles, out AABBf originalBounds, out AABBf voxelBounds, out Vector3 center, out float length) { originalBounds = Triangle.CreateBoundingBox(triangles); Vector3 size = new Vector3(originalBounds.MaxX - originalBounds.MinX, originalBounds.MaxY - originalBounds.MinY, originalBounds.MaxZ - originalBounds.MinZ); float maxSize = Math.Max(size.X, Math.Max(size.Y, size.Z)); center = new Vector3( originalBounds.MinX + (size.X / 2.0f), originalBounds.MinY + (size.Y / 2.0f), originalBounds.MinZ + (size.Z / 2.0f)); length = maxSize; voxelBounds = new AABBf(); voxelBounds.MinX = center.X - (length * 0.5f); voxelBounds.MinY = center.Y - (length * 0.5f); voxelBounds.MinZ = center.Z - (length * 0.5f); voxelBounds.MaxX = center.X + (length * 0.5f); voxelBounds.MaxY = center.Y + (length * 0.5f); voxelBounds.MaxZ = center.Z + (length * 0.5f); }
private void RenderCubeSide(int cubemapIndex, VoxelizingOctreeCell cell, Vector3 look, Vector3 up) { AABBf root_bounds = cell.Root.Bounds; float root_length = root_bounds.MaxX - root_bounds.MinX; float s_div2 = (cell.Bounds.MaxX - cell.Bounds.MinX) * 0.5f; GL.MatrixMode(MatrixMode.Projection); Matrix4 perspective = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(90.0f), 1.0f, s_div2, root_length); GL.LoadMatrix(ref perspective); Matrix4 modelView = Matrix4.LookAt(cell.Center, cell.Center + look, up); GL.MatrixMode(MatrixMode.Modelview); GL.LoadMatrix(ref modelView); //cubemapProjections[cubemapIndex] = perspective * modelView; cubemapProjections[cubemapIndex] = modelView * perspective; GL.PushMatrix(); GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, CubemapFboHandle[cubemapIndex]); // since there's only 1 Color buffer attached this is not explicitly required GL.DrawBuffer((DrawBufferMode)FramebufferAttachment.ColorAttachment0Ext); GL.ClearColor(0.0f, 0.0f, 0.0f, 0f); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D, CubemapColorTexture[cubemapIndex], 0); GL.Ext.FramebufferRenderbuffer(FramebufferTarget.FramebufferExt, FramebufferAttachment.DepthAttachmentExt, RenderbufferTarget.RenderbufferExt, CubemapDepthRenderbuffer[cubemapIndex]); GL.PushAttrib(AttribMask.ViewportBit); GL.Viewport(0, 0, CubemapWidth, CubemapHeight); DrawTwoSidedOriginalMesh(); GL.PopAttrib(); GL.PopMatrix(); }
public static void ComputeBestFitOBB(Vector3[] points, out float[] sides, out Matrix4 matrix, FitStrategy strategy) { matrix = Matrix4.Identity; AABBf aabb = new AABBf(); for (int i = 0; i < points.Length; i++) { aabb.Add(points[i]); } float avolume = (aabb.MaxX - aabb.MinX) * (aabb.MaxY - aabb.MinY) * (aabb.MaxZ - aabb.MinZ); Plane plane; ComputeBestFitPlane(points, out plane); plane.ToMatrix(ref matrix); ComputeOBB(points, ref matrix, out sides); Matrix4 refmatrix = new Matrix4(); refmatrix = matrix; float volume = sides[0] * sides[1] * sides[2]; float stepSize = 5; switch (strategy) { case BestFit.FitStrategy.FS_FAST_FIT: stepSize = 13; // 15 degree increments break; case BestFit.FitStrategy.FS_MEDIUM_FIT: stepSize = 7; // 10 degree increments break; case BestFit.FitStrategy.FS_SLOW_FIT: stepSize = 3; // 5 degree increments break; case BestFit.FitStrategy.FS_SLOWEST_FIT: stepSize = 1; // 1 degree increments break; } Quaternion quat = new Quaternion(); for (float a = 0; a < 180; a += stepSize) { Matrix4 temp; Matrix4.CreateRotationY(MathHelper.DegreesToRadians(a), out temp); QuaternionEx.CreateFromMatrix(ref temp, ref quat); Matrix4 pmatrix; Matrix4.Mult(ref temp, ref refmatrix, out pmatrix); float[] psides; ComputeOBB(points, ref pmatrix, out psides); float v = psides[0] * psides[1] * psides[2]; if (v < volume) { volume = v; matrix = pmatrix; sides[0] = psides[0]; sides[1] = psides[1]; sides[2] = psides[2]; } } if (avolume < volume) { matrix = Matrix4.CreateTranslation( (aabb.MinX + aabb.MaxX) * 0.5f, (aabb.MinY + aabb.MaxY) * 0.5f, (aabb.MinZ + aabb.MaxZ) * 0.5f); sides[0] = aabb.MaxX - aabb.MinX; sides[1] = aabb.MaxY - aabb.MinY; sides[2] = aabb.MaxZ - aabb.MinZ; } }
public bool IsOutside(AABBf other) { return((MaxX - other.MinX) < 0 || (MinX - other.MaxX) > 0 || (MaxY - other.MinY) < 0 || (MinY - other.MaxY) > 0 || (MaxZ - other.MinZ) < 0 || (MinZ - other.MaxZ) > 0); }
public VoxelizationOutput Generate(VoxelizationInput input, Action <VoxelizationProgress> progress) { this.input = input; VoxelizationOutput output = new VoxelizationOutput(); output.Octree = input.Octree; List <List <VoxelizingOctreeCell> > cellList = new List <List <VoxelizingOctreeCell> >(); input.Octree.AccumulateChildren(out cellList); VolumeAccumulator volume = new VolumeAccumulator(); VolumeAccumulator[] volumeAtLevel = new VolumeAccumulator[input.Octree.MaxLevels]; for (int i = 0; i < input.Octree.MaxLevels; i++) { List <VoxelizingOctreeCell> childernAtDepth = cellList[i]; VolumeAccumulator levelVolumeTotal = new VolumeAccumulator(); Parallel.For(0, childernAtDepth.Count, () => new VolumeAccumulator(), (n, loop, partial) => { VoxelizingOctreeCell cell = childernAtDepth[n]; float sideLength = cell.Length; switch (cell.Status) { case CellStatus.Inside: partial.InsideTotal += (sideLength * sideLength * sideLength); break; case CellStatus.Outside: partial.OutsideTotal += (sideLength * sideLength * sideLength); break; case CellStatus.Intersecting: case CellStatus.IntersectingBounds: if (cell.IsLeaf) { partial.IntersectingTotal += (sideLength * sideLength * sideLength); } break; } return(partial); }, partial => { lock (levelVolumeTotal) { levelVolumeTotal.InsideTotal += partial.InsideTotal; levelVolumeTotal.OutsideTotal += partial.OutsideTotal; levelVolumeTotal.IntersectingTotal += partial.IntersectingTotal; } }); volume.InsideTotal += levelVolumeTotal.InsideTotal; volume.OutsideTotal += levelVolumeTotal.OutsideTotal; volume.IntersectingTotal += levelVolumeTotal.IntersectingTotal; volumeAtLevel[i] = levelVolumeTotal; } Debug.WriteLine("Percentage of inner volume at each octree level"); for (int i = 0; i < input.Octree.MaxLevels; i++) { Debug.WriteLine("Level {0}: Inner Volume {1}%", i, (volumeAtLevel[i].InsideTotal / volume.InsideTotal) * 100); } // A good check to perform is to compare the ratio of intersecting volume leaf nodes to the total volume // we've determined is inside. A tool could use this ratio to automatically determine a good octree level // by iterative optimization. If a mesh for example fails to get at least a 1 : 0.5 ratio of intersecting:inner // volume ratio it's a good bet that the octree does not subdivide enough levels in order to find enough inner volume // to meet our occlusion needs. If further subdivision up to some maximum, lets say 8 fails to ever meet this ratio // one could say the mesh is not a good candidate for automating occluder generation. Debug.WriteLine(""); float intersecting_inside_ratio = volume.InsideTotal / volume.IntersectingTotal; Debug.WriteLine("Intersecting : Inner = 1:{0}", intersecting_inside_ratio); Debug.WriteLine("Inner / (Inner + Intersecting) = {0}", volume.InsideTotal / (volume.InsideTotal + volume.IntersectingTotal)); const float MINIMUM_INTERSECTING_TO_INSIDE_RATIO = 0.25f; AABBf meshBounds = input.Octree.MeshBounds; double dX = meshBounds.MaxX - meshBounds.MinX; double dY = meshBounds.MaxY - meshBounds.MinY; double dZ = meshBounds.MaxZ - meshBounds.MinZ; double reduction = 0.5; for (int i = 0; i <= input.Octree.MaxLevels * 2; i++) { reduction *= 0.5; } dX = dX * reduction; dY = dY * reduction; dZ = dZ * reduction; if (intersecting_inside_ratio > MINIMUM_INTERSECTING_TO_INSIDE_RATIO) { List <AABBi> innerBounds = new List <AABBi>(); float innerVolumeGathered = 0.0f; for (int i = 0; i < input.Octree.MaxLevels; i++) { for (int n = 0; n < cellList[i].Count; n++) { if (cellList[i][n].Status == CellStatus.Inside) { AABBf bound = cellList[i][n].Bounds; AABBi bi = new AABBi(); bi.MaxX = (int)Math.Round(((double)bound.MaxX - (double)meshBounds.MinX) / dX, MidpointRounding.AwayFromZero); bi.MaxY = (int)Math.Round(((double)bound.MaxY - (double)meshBounds.MinY) / dY, MidpointRounding.AwayFromZero); bi.MaxZ = (int)Math.Round(((double)bound.MaxZ - (double)meshBounds.MinZ) / dZ, MidpointRounding.AwayFromZero); bi.MinX = (int)Math.Round(((double)bound.MinX - (double)meshBounds.MinX) / dX, MidpointRounding.AwayFromZero); bi.MinY = (int)Math.Round(((double)bound.MinY - (double)meshBounds.MinY) / dY, MidpointRounding.AwayFromZero); bi.MinZ = (int)Math.Round(((double)bound.MinZ - (double)meshBounds.MinZ) / dZ, MidpointRounding.AwayFromZero); innerBounds.Add(bi); } } innerVolumeGathered += volumeAtLevel[i].InsideTotal / volume.InsideTotal; if (innerVolumeGathered > input.MinimumVolume) { break; } } Debug.WriteLine("Enough inner volume found {0}%", innerVolumeGathered * 100.0f); Mesh mesh = MeshBuilder.BuildMesh(innerBounds); for (int i = 0; i < mesh.Vertices.Length; i++) { mesh.Vertices[i].X = (float)(((double)meshBounds.MinX) + (mesh.Vertices[i].X * dX)); mesh.Vertices[i].Y = (float)(((double)meshBounds.MinY) + (mesh.Vertices[i].Y * dY)); mesh.Vertices[i].Z = (float)(((double)meshBounds.MinZ) + (mesh.Vertices[i].Z * dZ)); } if (input.Retriangulate) { Mesh triangulatedMesh = MeshOptimizer.Retriangulate(input, mesh, out output.DebugLines); if (triangulatedMesh != null) { mesh = triangulatedMesh; } } mesh = PolygonFilter.Filter(input, mesh); output.OccluderMesh = new RenderableMesh(mesh, true); } else { Debug.WriteLine("Not enough inner volume found to continue."); } return(output); }