Exemple #1
0
        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));
        }
Exemple #2
0
        public static Mesh BuildMesh(List <AABBi> 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++)
            {
                AABBi   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 CSGMesh(Plane[] planes, List<Polygon> polygons, List<HalfEdge> edges, List<Vector3> vertices, AABBi bounds)
 {
     this.Planes = planes;
     this.Polygons = polygons;
     this.Edges = edges;
     this.Vertices = vertices;
     this.Bounds.Set(bounds);
 }
Exemple #4
0
        public PlaneSideResult OnSide(AABBi bounds)
        {
            var x = A >= 0 ? bounds.MinX : bounds.MaxX;
            var y = B >= 0 ? bounds.MinY : bounds.MaxY;
            var z = C >= 0 ? bounds.MinZ : bounds.MaxZ;

            return(OnSide(SignedDistance(x, y, z)));
        }
Exemple #5
0
 public void Clone(AABBi clone)
 {
     clone.MaxX = this.MaxX;
     clone.MaxY = this.MaxY;
     clone.MaxZ = this.MaxZ;
     clone.MinX = this.MinX;
     clone.MinY = this.MinY;
     clone.MinZ = this.MinZ;
 }
Exemple #6
0
        public void Set(AABBi from, Vector3 translation)
        {
            MinX = (int)Math.Floor(from.MinX + translation.X);
            MinY = (int)Math.Floor(from.MinY + translation.Y);
            MinZ = (int)Math.Floor(from.MinZ + translation.Z);

            MaxX = (int)Math.Ceiling(from.MaxX + translation.X);
            MaxY = (int)Math.Ceiling(from.MaxY + translation.Y);
            MaxZ = (int)Math.Ceiling(from.MaxZ + translation.Z);
        }
Exemple #7
0
        public void Set(AABBi bounds)
        {
            this.MinX = bounds.MinX;
            this.MinY = bounds.MinY;
            this.MinZ = bounds.MinZ;

            this.MaxX = bounds.MaxX;
            this.MaxY = bounds.MaxY;
            this.MaxZ = bounds.MaxZ;
        }
Exemple #8
0
        public void Add(AABBi bounds)
        {
            MinX = Math.Min(MinX, bounds.MinX);
            MinY = Math.Min(MinY, bounds.MinY);
            MinZ = Math.Min(MinZ, bounds.MinZ);

            MaxX = Math.Max(MaxX, bounds.MaxX);
            MaxY = Math.Max(MaxY, bounds.MaxY);
            MaxZ = Math.Max(MaxZ, bounds.MaxZ);
        }
Exemple #9
0
 public bool Intersects(AABBi 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));
 }
Exemple #10
0
        // Creates a clone of the mesh
        #region Clone
        public CSGMesh Clone()
        {
            var newPlanes = new Plane[Planes.Length];

            for (int i = 0; i < Planes.Length; i++)
            {
                var plane = Planes[i];
                newPlanes[i] = new Plane(plane.A, plane.B, plane.C, plane.D);
            }
            var newPolygons = new List <Polygon>(Polygons.Count);

            foreach (var polygon in Polygons)
            {
                var newPolygon = new Polygon();
                newPolygon.FirstIndex = polygon.FirstIndex;
                newPolygon.Visible    = polygon.Visible;
                newPolygon.Category   = polygon.Category;
                newPolygon.PlaneIndex = polygon.PlaneIndex;
                newPolygon.Bounds.Set(polygon.Bounds);

                newPolygons.Add(newPolygon);
            }

            var newEdges = new List <HalfEdge>(Edges.Count);

            foreach (var edge in Edges)
            {
                var newEdge = new HalfEdge();
                newEdge.NextIndex    = edge.NextIndex;
                newEdge.PolygonIndex = edge.PolygonIndex;
                newEdge.TwinIndex    = edge.TwinIndex;
                newEdge.VertexIndex  = edge.VertexIndex;
                newEdges.Add(newEdge);
            }

            var newVertices = new List <Vector3>(Vertices.Count);

            foreach (var vertex in Vertices)
            {
                newVertices.Add(vertex);
            }

            var newBounds = new AABBi(Bounds);
            var newMesh   = new CSGMesh(
                newPlanes,
                newPolygons,
                newEdges,
                newVertices,
                newBounds);

            return(newMesh);
        }
Exemple #11
0
        public AABBi Clone()
        {
            AABBi clone = new AABBi();

            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);
        }
Exemple #12
0
        void ComputeNext(Random random, AABBi current, AABBi next, VoxelField volume, ref Vector3i densestVoxel, long delta, double temperature)
        {
            current.Clone(next);

            do
            {
                double probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                {
                    next.MinX = densestVoxel.X + random.Next(0 - densestVoxel.X, 0);
                }
                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                {
                    next.MinY = densestVoxel.Y + random.Next(0 - densestVoxel.Y, 0);
                }
                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                {
                    next.MinZ = densestVoxel.Z + random.Next(0 - densestVoxel.Z, 0);
                }

                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                {
                    next.MaxX = densestVoxel.X + random.Next(0, volume.VoxelSize.X - densestVoxel.X) + 1;
                }
                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                {
                    next.MaxY = densestVoxel.Y + random.Next(0, volume.VoxelSize.Y - densestVoxel.Y) + 1;
                }
                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                {
                    next.MaxZ = densestVoxel.Z + random.Next(0, volume.VoxelSize.Z - densestVoxel.Z) + 1;
                }
            } while (!TestRangeForFreeSpace(volume,
                                            new Vector3i(next.MinX, next.MinY, next.MinZ),
                                            new Vector3i(next.MaxX, next.MaxY, next.MaxZ)));
        }
Exemple #13
0
        public PlaneSideResult OnSide(AABBi bounds, Vector3 translation)
        {
            var backward_x = A <= 0 ? bounds.MinX : bounds.MaxX;
            var backward_y = B <= 0 ? bounds.MinY : bounds.MaxY;
            var backward_z = C <= 0 ? bounds.MinZ : bounds.MaxZ;
            var distance   = SignedDistance(backward_x + translation.X, backward_y + translation.Y, backward_z + translation.Z);
            var side       = OnSide(distance);

            if (side == PlaneSideResult.Inside)
            {
                return(PlaneSideResult.Inside);
            }
            var forward_x = A >= 0 ? bounds.MinX : bounds.MaxX;
            var forward_y = B >= 0 ? bounds.MinY : bounds.MaxY;
            var forward_z = C >= 0 ? bounds.MinZ : bounds.MaxZ;

            distance = SignedDistance(forward_x + translation.X, forward_y + translation.Y, forward_z + translation.Z);
            side     = OnSide(distance);
            if (side == PlaneSideResult.Outside)
            {
                return(PlaneSideResult.Outside);
            }
            return(PlaneSideResult.Intersects);
        }
 protected static bool TestRangeForFreeSpace(VoxelField volume, AABBi box)
 {
     return TestRangeForFreeSpace(volume, new Vector3i(box.MinX, box.MinY, box.MinZ), new Vector3i(box.MaxX - 1, box.MaxY - 1, box.MaxZ - 1));
 }
Exemple #15
0
        protected static AABBi ExpandAndFillBox(VoxelField volume, ref Vector3i originVoxel, Byte fillByte)
        {
            int pX, nX, pY, nY, pZ, nZ;

            pX = nX = pY = nY = pZ = nZ = 0;

            volume.SetVoxel(originVoxel.X, originVoxel.Y, originVoxel.Z, fillByte);

            bool boxGrew = false;

            do
            {
                // +Z Axis
                bool pZGrew = FillRange(volume,
                                        new Vector3i(originVoxel.X - nX, originVoxel.Y - nY, originVoxel.Z + (pZ + 1)),
                                        new Vector3i(originVoxel.X + pX, originVoxel.Y + pY, originVoxel.Z + (pZ + 1)), fillByte);
                if (pZGrew)
                {
                    pZ++;
                }

                // -Z Axis
                bool nZGrew = FillRange(volume,
                                        new Vector3i(originVoxel.X - nX, originVoxel.Y - nY, originVoxel.Z - (nZ + 1)),
                                        new Vector3i(originVoxel.X + pX, originVoxel.Y + pY, originVoxel.Z - (nZ + 1)), fillByte);
                if (nZGrew)
                {
                    nZ++;
                }

                // +Y Axis
                bool pYGrew = FillRange(volume,
                                        new Vector3i(originVoxel.X - nX, originVoxel.Y + (pY + 1), originVoxel.Z - nZ),
                                        new Vector3i(originVoxel.X + pX, originVoxel.Y + (pY + 1), originVoxel.Z + pZ), fillByte);
                if (pYGrew)
                {
                    pY++;
                }

                // -Y Axis
                bool nYGrew = FillRange(volume,
                                        new Vector3i(originVoxel.X - nX, originVoxel.Y - (nY + 1), originVoxel.Z - nZ),
                                        new Vector3i(originVoxel.X + pX, originVoxel.Y - (nY + 1), originVoxel.Z + pZ), fillByte);
                if (nYGrew)
                {
                    nY++;
                }

                // +X Axis
                bool pXGrew = FillRange(volume,
                                        new Vector3i(originVoxel.X + (pX + 1), originVoxel.Y - nY, originVoxel.Z - nZ),
                                        new Vector3i(originVoxel.X + (pX + 1), originVoxel.Y + pY, originVoxel.Z + pZ), fillByte);
                if (pXGrew)
                {
                    pX++;
                }

                // -X Axis
                bool nXGrew = FillRange(volume,
                                        new Vector3i(originVoxel.X - (nX + 1), originVoxel.Y - nY, originVoxel.Z - nZ),
                                        new Vector3i(originVoxel.X - (nX + 1), originVoxel.Y + pY, originVoxel.Z + pZ), fillByte);
                if (nXGrew)
                {
                    nX++;
                }

                boxGrew = (pZGrew || nZGrew || pYGrew || nYGrew || pXGrew || nXGrew);
            } while (boxGrew);

            AABBi box = new AABBi();

            box.MinX = originVoxel.X - nX;
            box.MinY = originVoxel.Y - nY;
            box.MinZ = originVoxel.Z - nZ;

            box.MaxX = originVoxel.X + pX + 1;
            box.MaxY = originVoxel.Y + pY + 1;
            box.MaxZ = originVoxel.Z + pZ + 1;

            return(box);
        }
Exemple #16
0
        // Categorize the given inputPolygons as being inside/outside or (reverse-)aligned
        // with the shape that is defined by the current brush or csg-branch.
        // When an inputPolygon crosses the node, it is split into pieces and every individual
        // piece is then categorized.
        #region Categorize
        public static void Categorize(CSGNode processedNode,
                                      CSGMesh mesh,

                                      CSGNode categorizationNode,

                                      List <Polygon> inputPolygons,

                                      List <Polygon> inside,
                                      List <Polygon> aligned,
                                      List <Polygon> revAligned,
                                      List <Polygon> outside)
        {
            // When you go deep enough in the tree it's possible that all categories point to the same
            // destination. So we detect that and potentially avoid a lot of wasted work.
            if (inside == revAligned &&
                inside == aligned &&
                inside == outside)
            {
                inside.AddRange(inputPolygons);
                return;
            }

Restart:
            if (processedNode == categorizationNode)
            {
                // When the currently processed node is the same node as we categorize against, then
                // we know that all our polygons are visible and we set their default category
                // (usually aligned, unless it's an instancing node in which case it's precalculated)
                foreach (var polygon in inputPolygons)
                {
                    switch (polygon.Category)
                    {
                    case PolygonCategory.Aligned: aligned.Add(polygon); break;

                    case PolygonCategory.ReverseAligned: revAligned.Add(polygon); break;

                    case PolygonCategory.Inside: inside.Add(polygon); break;

                    case PolygonCategory.Outside: outside.Add(polygon); break;
                    }

                    // When brushes overlap and they share the same surface area we only want to keep
                    // the polygons of the last brush in the tree, and skip all others.
                    // At this point in the tree we know that this polygon belongs to this brush, so
                    // we set it to visible. If the polygon is found to share the surface area with another
                    // brush further on in the tree it'll be set to invisible again in mesh.Intersect.
                    polygon.Visible = true;
                }
                return;
            }

            var leftNode  = categorizationNode.Left;
            var rightNode = categorizationNode.Right;

            switch (categorizationNode.NodeType)
            {
            case CSGNodeType.Brush:
            {
                mesh.Intersect(categorizationNode.Bounds,
                               categorizationNode.Planes,
                               categorizationNode.Translation,
                               processedNode.Translation,

                               inputPolygons,

                               inside, aligned, revAligned, outside);
                break;
            }

            case CSGNodeType.Addition:
            {
                //  ( A ||  B)
                var relativeLeftTrans  = Vector3.Subtract(processedNode.Translation, leftNode.Translation);
                var relativeRightTrans = Vector3.Subtract(processedNode.Translation, rightNode.Translation);
                if (AABBi.IsOutside(processedNode.Bounds, relativeLeftTrans, leftNode.Bounds))
                {
                    if (AABBi.IsOutside(processedNode.Bounds, relativeRightTrans, rightNode.Bounds))
                    {
                        // When our polygons lie outside the bounds of both the left and the right node, then
                        // all the polygons can be categorized as being 'outside'
                        outside.AddRange(inputPolygons);
                    }
                    else
                    {
                        //Categorize(processedNode, mesh, right,
                        //           inputPolygons,
                        //           inside, aligned, revAligned, outside);
                        categorizationNode = rightNode;
                        goto Restart;
                    }
                }
                else
                if (AABBi.IsOutside(processedNode.Bounds, relativeRightTrans, rightNode.Bounds))
                {
                    //Categorize(processedNode, left, mesh,
                    //           inputPolygons,
                    //           inside, aligned, revAligned, outside);
                    categorizationNode = leftNode;
                    goto Restart;
                }
                else
                {
                    LogicalOr(processedNode, mesh, categorizationNode,
                              inputPolygons,
                              inside, aligned, revAligned, outside,
                              false, false);
                }
                break;
            }

            case CSGNodeType.Common:
            {
                // !(!A || !B)
                var relativeLeftTrans  = Vector3.Subtract(processedNode.Translation, leftNode.Translation);
                var relativeRightTrans = Vector3.Subtract(processedNode.Translation, rightNode.Translation);
                if (AABBi.IsOutside(processedNode.Bounds, relativeLeftTrans, leftNode.Bounds) ||
                    AABBi.IsOutside(processedNode.Bounds, relativeRightTrans, rightNode.Bounds))
                {
                    // When our polygons lie outside the bounds of both the left and the right node, then
                    // all the polygons can be categorized as being 'outside'
                    outside.AddRange(inputPolygons);
                }
                else
                {
                    LogicalOr(processedNode, mesh, categorizationNode,
                              inputPolygons,
                              outside, revAligned, aligned, inside,
                              true, true);
                }
                break;
            }

            case CSGNodeType.Subtraction:
            {
                // !(!A ||  B)
                var relativeLeftTrans  = Vector3.Subtract(processedNode.Translation, leftNode.Translation);
                var relativeRightTrans = Vector3.Subtract(processedNode.Translation, rightNode.Translation);
                if (AABBi.IsOutside(processedNode.Bounds, relativeLeftTrans, leftNode.Bounds))
                {
                    // When our polygons lie outside the bounds of both the left node, then
                    // all the polygons can be categorized as being 'outside'
                    outside.AddRange(inputPolygons);
                }
                else
                if (AABBi.IsOutside(processedNode.Bounds, relativeRightTrans, rightNode.Bounds))
                {
                    categorizationNode = leftNode;
                    goto Restart;
                }
                else
                {
                    LogicalOr(processedNode, mesh, categorizationNode,
                              inputPolygons,
                              outside, revAligned, aligned, inside,
                              true, false);
                }
                break;
            }
            }
        }
        void ComputeNext(Random random, AABBi current, AABBi next, VoxelField volume, ref Vector3i densestVoxel, long delta, double temperature)
        {
            current.Clone(next);

            do
            {
                double probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                    next.MinX = densestVoxel.X + random.Next(0 - densestVoxel.X, 0);
                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                    next.MinY = densestVoxel.Y + random.Next(0 - densestVoxel.Y, 0);
                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                    next.MinZ = densestVoxel.Z + random.Next(0 - densestVoxel.Z, 0);

                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                    next.MaxX = densestVoxel.X + random.Next(0, volume.VoxelSize.X - densestVoxel.X) + 1;
                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                    next.MaxY = densestVoxel.Y + random.Next(0, volume.VoxelSize.Y - densestVoxel.Y) + 1;
                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                    next.MaxZ = densestVoxel.Z + random.Next(0, volume.VoxelSize.Z - densestVoxel.Z) + 1;

            } while (!TestRangeForFreeSpace(volume,
                new Vector3i(next.MinX, next.MinY, next.MinZ),
                new Vector3i(next.MaxX, next.MaxY, next.MaxZ)));
        }
 protected static bool FillRange(VoxelField volume, AABBi box, Byte fillByte)
 {
     return FillRange(volume, new Vector3i(box.MinX, box.MinY, box.MinZ), new Vector3i(box.MaxX - 1, box.MaxY - 1, box.MaxZ - 1), fillByte);
 }
Exemple #19
0
 public static bool IsOutside(AABBi left, Vector3 translation, AABBi right)
 {
     return(((left.MaxX + translation.X) - right.MinX) < 0 || ((left.MinX + translation.X) - right.MaxX) > 0 ||
            ((left.MaxY + translation.Y) - right.MinY) < 0 || ((left.MinY + translation.Y) - right.MaxY) > 0 ||
            ((left.MaxZ + translation.Z) - right.MinZ) < 0 || ((left.MinZ + translation.Z) - right.MaxZ) > 0);
 }
Exemple #20
0
 public bool IsOutside(AABBi other)
 {
     return((this.MaxX - other.MinX) < 0 || (this.MinX - other.MaxX) > 0 ||
            (this.MaxY - other.MinY) < 0 || (this.MinY - other.MaxY) > 0 ||
            (this.MaxZ - other.MinZ) < 0 || (this.MinZ - other.MaxZ) > 0);
 }
Exemple #21
0
 public static bool IsOutside(AABBi left, AABBi right)
 {
     return((left.MaxX - right.MinX) < 0 || (left.MinX - right.MaxX) > 0 ||
            (left.MaxY - right.MinY) < 0 || (left.MinY - right.MaxY) > 0 ||
            (left.MaxZ - right.MinZ) < 0 || (left.MinZ - right.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;
        }
Exemple #23
0
        // Intersects a mesh with a brush (set of planes)
        #region Intersect
        public void Intersect(AABBi cuttingNodeBounds,
                              Plane[] cuttingNodePlanes,
                              Vector3 cuttingNodeTranslation,
                              Vector3 inputPolygonTranslation,

                              List <Polygon> inputPolygons,
                              List <Polygon> inside,
                              List <Polygon> aligned,
                              List <Polygon> revAligned,
                              List <Polygon> outside)
        {
            var categories       = new PolygonSplitResult[cuttingNodePlanes.Length];
            var translatedPlanes = new Plane[cuttingNodePlanes.Length];
            var translation      = cuttingNodeTranslation - inputPolygonTranslation;

            // translate the planes we cut our polygons with so that they're located at the same
            // relative distance from the polygons as the brushes are from each other.
            for (int i = 0; i < cuttingNodePlanes.Length; i++)
            {
                translatedPlanes[i] = Plane.Translated(cuttingNodePlanes[i], translation);
            }

            var vertices = this.Vertices;
            var edges    = this.Edges;
            var planes   = this.Planes;

            for (int i = inputPolygons.Count - 1; i >= 0; i--)
            {
                var inputPolygon = inputPolygons[i];
                if (inputPolygon.FirstIndex == -1)
                {
                    continue;
                }

                var bounds      = inputPolygon.Bounds;
                var finalResult = PolygonSplitResult.CompletelyInside;

                // A quick check if the polygon lies outside the planes we're cutting our polygons with.
                if (!AABBi.IsOutside(cuttingNodeBounds, translation, bounds))
                {
                    PolygonSplitResult intermediateResult;
                    Polygon            outsidePolygon = null;
                    for (int otherIndex = 0; otherIndex < translatedPlanes.Length; otherIndex++)
                    {
                        var translatedCuttingPlane = translatedPlanes[otherIndex];

                        var side = cuttingNodePlanes[otherIndex].OnSide(bounds, -translation);
                        if (side == PlaneSideResult.Outside)
                        {
                            finalResult = PolygonSplitResult.CompletelyOutside;
                            break;      // nothing left to process, so we exit
                        }
                        else
                        if (side == PlaneSideResult.Inside)
                        {
                            continue;
                        }

                        var polygon = inputPolygon;
                        intermediateResult = PolygonSplit(translatedCuttingPlane, inputPolygonTranslation, ref polygon, out outsidePolygon);
                        inputPolygon       = polygon;

                        if (intermediateResult == PolygonSplitResult.CompletelyOutside)
                        {
                            finalResult = PolygonSplitResult.CompletelyOutside;
                            break;      // nothing left to process, so we exit
                        }
                        else
                        if (intermediateResult == PolygonSplitResult.Split)
                        {
                            if (outside != null)
                            {
                                outside.Add(outsidePolygon);
                            }
                            // Note: left over is still completely inside,
                            //		 or plane (opposite) aligned
                        }
                        else
                        if (intermediateResult != PolygonSplitResult.CompletelyInside)
                        {
                            finalResult = intermediateResult;
                        }
                    }
                }
                else
                {
                    finalResult = PolygonSplitResult.CompletelyOutside;
                }

                switch (finalResult)
                {
                case PolygonSplitResult.CompletelyInside: inside.Add(inputPolygon); break;

                case PolygonSplitResult.CompletelyOutside: outside.Add(inputPolygon); break;

                // The polygon can only be visible if it's part of the last brush that shares it's surface area,
                // otherwise we'd get overlapping polygons if two brushes overlap.
                // When the (final) polygon is aligned with one of the cutting planes, we know it lies on the surface of
                // the CSG node we're cutting the polygons with. We also know that this node is not the node this polygon belongs to
                // because we've done that check earlier on. So we flag this polygon as being invisible.
                case PolygonSplitResult.PlaneAligned: inputPolygon.Visible = false; aligned.Add(inputPolygon); break;

                case PolygonSplitResult.PlaneOppositeAligned: inputPolygon.Visible = false; revAligned.Add(inputPolygon); break;
                }
            }
        }
Exemple #24
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);
        }
Exemple #25
0
 public AABBi(AABBi other)
 {
     Clear();
     Set(other);
 }
Exemple #26
0
 public CSGMesh(Plane[] planes, List <Polygon> polygons, List <HalfEdge> edges, List <Vector3> vertices, AABBi bounds)
 {
     this.Planes   = planes;
     this.Polygons = polygons;
     this.Edges    = edges;
     this.Vertices = vertices;
     this.Bounds.Set(bounds);
 }
Exemple #27
0
        public void Add(AABBi bounds)
        {
            MinX = Math.Min(MinX, bounds.MinX);
            MinY = Math.Min(MinY, bounds.MinY);
            MinZ = Math.Min(MinZ, bounds.MinZ);

            MaxX = Math.Max(MaxX, bounds.MaxX);
            MaxY = Math.Max(MaxY, bounds.MaxY);
            MaxZ = Math.Max(MaxZ, bounds.MaxZ);
        }
        public CSGMesh Clone()
        {
            var newPlanes = new Plane[Planes.Length];
            for (int i = 0; i < Planes.Length; i++)
            {
                var plane = Planes[i];
                newPlanes[i] = new Plane(plane.A, plane.B, plane.C, plane.D);
            }
            var newPolygons = new List<Polygon>(Polygons.Count);
            foreach (var polygon in Polygons)
            {
                var newPolygon = new Polygon();
                newPolygon.FirstIndex = polygon.FirstIndex;
                newPolygon.Visible = polygon.Visible;
                newPolygon.Category = polygon.Category;
                newPolygon.PlaneIndex = polygon.PlaneIndex;
                newPolygon.Bounds.Set(polygon.Bounds);

                newPolygons.Add(newPolygon);
            }

            var newEdges = new List<HalfEdge>(Edges.Count);
            foreach (var edge in Edges)
            {
                var newEdge = new HalfEdge();
                newEdge.NextIndex = edge.NextIndex;
                newEdge.PolygonIndex = edge.PolygonIndex;
                newEdge.TwinIndex = edge.TwinIndex;
                newEdge.VertexIndex = edge.VertexIndex;
                newEdges.Add(newEdge);
            }

            var newVertices = new List<Vector3>(Vertices.Count);
            foreach (var vertex in Vertices)
            {
                newVertices.Add(vertex);
            }

            var newBounds = new AABBi(Bounds);
            var newMesh = new CSGMesh(
                newPlanes,
                newPolygons,
                newEdges,
                newVertices,
                newBounds);

            return newMesh;
        }
Exemple #29
0
        // Combines multiple meshes into one
        #region Combine
        public static CSGMesh Combine(Vector3 offset, IDictionary <CSGNode, CSGMesh> brushMeshes)
        {
            var planeLookup  = new Dictionary <Plane, short>();
            var vertexLookup = new Dictionary <Vector3, short>();

            var planes   = new List <Plane>();
            var polygons = new List <Polygon>();
            var edges    = new List <HalfEdge>();
            var vertices = new List <Vector3>();

            var bounds = new AABBi();

            bounds.Clear();
            int edgeIndex    = 0;
            int polygonIndex = 0;

            foreach (var item in brushMeshes)
            {
                var node        = item.Key;
                var translation = node.Translation - offset;
                var mesh        = item.Value;
                foreach (var edge in mesh.Edges)
                {
                    short vertexIndex;
                    var   vertex = mesh.Vertices[edge.VertexIndex] + translation;
                    if (!vertexLookup.TryGetValue(vertex, out vertexIndex))
                    {
                        vertexIndex = (short)vertices.Count;
                        vertices.Add(vertex);
                        vertexLookup.Add(vertex, vertexIndex);
                    }

                    var newEdge = new HalfEdge();
                    newEdge.VertexIndex  = vertexIndex;
                    newEdge.NextIndex    = (short)(edge.NextIndex + edgeIndex);
                    newEdge.TwinIndex    = (short)(edge.TwinIndex + edgeIndex);
                    newEdge.PolygonIndex = (short)(edge.PolygonIndex + polygonIndex);

                    edges.Add(newEdge);
                }

                foreach (var polygon in mesh.Polygons)
                {
                    if (polygon.FirstIndex == -1)
                    {
                        continue;
                    }
                    short planeIndex;
                    var   plane = mesh.Planes[polygon.PlaneIndex];
                    if (!planeLookup.TryGetValue(plane, out planeIndex))
                    {
                        planeIndex = (short)planes.Count;
                        planes.Add(plane);
                        planeLookup.Add(plane, planeIndex);
                    }

                    var newPolygon = new Polygon();
                    newPolygon.PlaneIndex = planeIndex;
                    newPolygon.FirstIndex = (short)(polygon.FirstIndex + edgeIndex);
                    newPolygon.Category   = polygon.Category;
                    newPolygon.Visible    = polygon.Visible;
                    newPolygon.Bounds.Set(polygon.Bounds, translation);

                    polygons.Add(newPolygon);

                    if (newPolygon.Visible)
                    {
                        var first    = edges[newPolygon.FirstIndex];
                        var iterator = first;
                        do
                        {
                            bounds.Add(vertices[iterator.VertexIndex]);
                            iterator = edges[iterator.NextIndex];
                        } while (iterator != first);
                    }
                }
                edgeIndex    = edges.Count;
                polygonIndex = polygons.Count;
            }
            return(new CSGMesh(planes.ToArray(), polygons, edges, vertices, bounds));
        }
Exemple #30
0
        public AABBi Clone()
        {
            AABBi clone = new AABBi();
            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;
        }
Exemple #31
0
        AABBi BruteForceFill(VoxelizationInput input, SilhouetteOcclusionValidator sov, VoxelField voxelField, Vector3i densestVoxel, byte fillByte, List <Occluder> currentOccluders)
        {
            Object syncroot        = new Object();
            Int64  largestVolume   = 1;
            AABBi  largestOccluder = new AABBi(densestVoxel.X, densestVoxel.Y, densestVoxel.Z, densestVoxel.X + 1, densestVoxel.Y + 1, densestVoxel.Z + 1);

            int          MaxTopOccluders = 2000;
            List <AABBi> bestOccluders   = new List <AABBi>(MaxTopOccluders);

            Parallel.For(densestVoxel.Z + 1, voxelField.VoxelSize.Z, max_z =>
            {
                for (Int32 min_z = densestVoxel.Z; min_z >= 0; --min_z)
                {
                    for (Int32 max_y = densestVoxel.Y + 1; max_y < voxelField.VoxelSize.Y; ++max_y)
                    {
                        for (Int32 min_y = densestVoxel.Y; min_y >= 0; --min_y)
                        {
                            for (Int32 max_x = densestVoxel.X + 1; max_x < voxelField.VoxelSize.X; ++max_x)
                            {
                                for (Int32 min_x = densestVoxel.X; min_x >= 0; --min_x)
                                {
                                    Int32 dx     = max_x - min_x;
                                    Int32 dy     = max_y - min_y;
                                    Int32 dz     = max_z - min_z;
                                    Int64 volume = dx * dy * dz;

                                    if (TestRangeForFreeSpace(voxelField, new AABBi(min_x, min_y, min_z, max_x, max_y, max_z)))
                                    {
                                        lock (syncroot)
                                        {
                                            if (volume > largestVolume)
                                            {
                                                largestVolume   = volume;
                                                largestOccluder = new AABBi(min_x, min_y, min_z, max_x, max_y, max_z);
                                                if (bestOccluders.Count >= MaxTopOccluders)
                                                {
                                                    bestOccluders.RemoveAt(MaxTopOccluders - 1);
                                                }
                                                bestOccluders.Insert(0, largestOccluder);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        // if we can't expand outward any further there's no point in checking more.
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }

                Debug.WriteLine("Checked " + max_z);
            });

            List <AABBi> relevantOccluders = GetRelevantOccluders(input, currentOccluders);

            long  bestCoverage       = 0;
            AABBi bestCoverageVolume = largestOccluder;

            foreach (AABBi occluder in bestOccluders)
            {
                List <AABBi> tempOccluders = relevantOccluders.ToList();
                tempOccluders.Add(occluder);
                long coverage = MeasureOccluderOcclusion(sov, input, tempOccluders);
                if (coverage > bestCoverage)
                {
                    bestCoverage       = coverage;
                    bestCoverageVolume = occluder;
                }
            }

            FillRange(voxelField, bestCoverageVolume, fillByte);

            return(bestCoverageVolume);
        }
Exemple #32
0
 public void Clone(AABBi clone)
 {
     clone.MaxX = this.MaxX;
     clone.MaxY = this.MaxY;
     clone.MaxZ = this.MaxZ;
     clone.MinX = this.MinX;
     clone.MinY = this.MinY;
     clone.MinZ = this.MinZ;
 }
Exemple #33
0
        AABBi SimulatedAnnealingFill(VoxelizationInput input, SilhouetteOcclusionValidator sov, VoxelField volume, ref Vector3i densestVoxel, byte fillByte, List <Occluder> currentOccluders)
        {
            AABBi current = new AABBi(densestVoxel.X, densestVoxel.Y, densestVoxel.Z, densestVoxel.X + 1, densestVoxel.Y + 1, densestVoxel.Z + 1);
            AABBi next    = new AABBi(0, 0, 0, 0, 0, 0);

            int iteration = -1;

            List <AABBi> relevantOccluders = GetRelevantOccluders(input, currentOccluders);

            List <AABBi> occluders = relevantOccluders.ToList();

            occluders.Add(current);
            long coverage = MeasureOccluderOcclusion(sov, input, occluders);

            double coolignAlpha = 0.999;
            double temperature  = 400.0;
            double epsilon      = 0.001;

            Random random = new Random(1337);

            int maxItterations = 1000;

            long delta = 0;

            while (temperature > epsilon && iteration < maxItterations)
            {
                iteration++;

                ComputeNext(random, current, next, volume, ref densestVoxel, delta, temperature);

                occluders = relevantOccluders.ToList();
                occluders.Add(next);
                delta = MeasureOccluderOcclusion(sov, input, occluders) - coverage;

                if (delta < 0)
                {
                    next.Clone(current);
                    coverage = delta + coverage;
                }
                else
                {
                    double probability = random.NextDouble();

                    if (probability < Math.Exp(-delta / temperature))
                    {
                        next.Clone(current);
                        coverage = delta + coverage;
                    }
                }

                temperature *= coolignAlpha;

                if (iteration % 400 == 0)
                {
                    Console.WriteLine(coverage);
                }
            }

            FillRange(volume,
                      new Vector3i(current.MinX, current.MinY, current.MinZ),
                      new Vector3i(current.MaxX, current.MaxY, current.MaxZ),
                      fillByte);

            return(current);
        }
Exemple #34
0
 public bool Intersects(AABBi 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 static CSGMesh CreateFromPlanes(Plane[] brushPlanes)
        {
            var planes = new Plane[brushPlanes.Length];
            for (int i = 0; i < brushPlanes.Length; i++)
            {
                var plane = brushPlanes[i];
                planes[i] = new Plane(plane.A, plane.B, plane.C, plane.D);
            }

            var pointIntersections = new List<PointIntersection>(planes.Length * planes.Length);
            var intersectingPlanes = new List<short>();
            var vertices = new List<Vector3>();
            var edges = new List<HalfEdge>();

            // Find all point intersections where 3 (or more planes) intersect
            for (short planeIndex1 = 0; planeIndex1 < planes.Length - 2; planeIndex1++)
            {
                var plane1 = planes[planeIndex1];
                for (short planeIndex2 = (short)(planeIndex1 + 1); planeIndex2 < planes.Length - 1; planeIndex2++)
                {
                    var plane2 = planes[planeIndex2];
                    for (short planeIndex3 = (short)(planeIndex2 + 1); planeIndex3 < planes.Length; planeIndex3++)
                    {
                        var plane3 = planes[planeIndex3];

                        // Calculate the intersection
                        var vertex = Plane.Intersection(plane1, plane2, plane3);

                        // Check if the intersection is valid
                        if (float.IsNaN(vertex.X) || float.IsNaN(vertex.Y) || float.IsNaN(vertex.Z) ||
                            float.IsInfinity(vertex.X) || float.IsInfinity(vertex.Y) || float.IsInfinity(vertex.Z))
                            continue;

                        intersectingPlanes.Clear();
                        intersectingPlanes.Add(planeIndex1);
                        intersectingPlanes.Add(planeIndex2);
                        intersectingPlanes.Add(planeIndex3);

                        for (short planeIndex4 = 0; planeIndex4 < planes.Length; planeIndex4++)
                        {
                            if (planeIndex4 == planeIndex1 ||
                                planeIndex4 == planeIndex2 ||
                                planeIndex4 == planeIndex3)
                                continue;

                            var plane4 = planes[planeIndex4];
                            var side = plane4.OnSide(vertex);
                            if (side == PlaneSideResult.Intersects)
                            {
                                if (planeIndex4 < planeIndex3)
                                    // Already found this vertex
                                    goto SkipIntersection;

                                // We've found another plane which goes trough our found intersection point
                                intersectingPlanes.Add(planeIndex4);
                            }
                            else
                                if (side == PlaneSideResult.Outside)
                                    // Intersection is outside of brush
                                    goto SkipIntersection;
                        }

                        var vertexIndex = (short)vertices.Count;
                        vertices.Add(vertex);

                        // Add intersection point to our list
                        pointIntersections.Add(new PointIntersection(vertexIndex, intersectingPlanes));

                    SkipIntersection:
                        ;
                    }
                }
            }

            var foundPlanes = new short[2];
            // Find all our intersection edges which are formed by a pair of planes
            // (this could probably be done inside the previous loop)
            for (int i = 0; i < pointIntersections.Count; i++)
            {
                var pointIntersectionA = pointIntersections[i];
                for (int j = i + 1; j < pointIntersections.Count; j++)
                {
                    var pointIntersectionB = pointIntersections[j];
                    var planesIndicesA = pointIntersectionA.PlaneIndices;
                    var planesIndicesB = pointIntersectionB.PlaneIndices;

                    short foundPlaneIndex = 0;
                    foreach (var currentPlaneIndex in planesIndicesA)
                    {
                        if (!planesIndicesB.Contains(currentPlaneIndex))
                            continue;

                        foundPlanes[foundPlaneIndex] = currentPlaneIndex;
                        foundPlaneIndex++;

                        if (foundPlaneIndex == 2)
                            break;
                    }

                    // If foundPlaneIndex is 0 or 1 then either this combination does not exist,
                    // or only goes trough one point
                    if (foundPlaneIndex < 2)
                        continue;

                    // Create our found intersection edge
                    var halfEdgeA = new HalfEdge();
                    var halfEdgeAIndex = (short)edges.Count;
                    edges.Add(halfEdgeA);

                    var halfEdgeB = new HalfEdge();
                    var halfEdgeBIndex = (short)edges.Count;
                    edges.Add(halfEdgeB);

                    halfEdgeA.TwinIndex = halfEdgeBIndex;
                    halfEdgeB.TwinIndex = halfEdgeAIndex;

                    halfEdgeA.VertexIndex = pointIntersectionA.VertexIndex;
                    halfEdgeB.VertexIndex = pointIntersectionB.VertexIndex;

                    // Add it to our points
                    pointIntersectionA.Edges.Add(new EdgeIntersection(
                                                        halfEdgeA,
                                                        foundPlanes[0],
                                                        foundPlanes[1]));
                    pointIntersectionB.Edges.Add(new EdgeIntersection(
                                                        halfEdgeB,
                                                        foundPlanes[0],
                                                        foundPlanes[1]));
                }
            }

            var polygons = new List<Polygon>();
            for (short i = 0; i < (short)planes.Length; i++)
            {
                var polygon = new Polygon();
                polygon.PlaneIndex = i;
                polygons.Add(polygon);
            }

            var bounds = new AABBi();
            var direction = new Vector3();
            for (int i = pointIntersections.Count - 1; i >= 0; i--)
            {
                var pointIntersection = pointIntersections[i];
                var pointEdges = pointIntersection.Edges;

                // Make sure that we have at least 2 edges ...
                // This may happen when a plane only intersects at a single edge.
                if (pointEdges.Count <= 2)
                {
                    pointIntersections.RemoveAt(i);
                    continue;
                }

                var vertexIndex = pointIntersection.VertexIndex;
                var vertex = vertices[vertexIndex];

                for (int j = 0; j < pointEdges.Count - 1; j++)
                {
                    var edge1 = pointEdges[j];
                    for (int k = j + 1; k < pointEdges.Count; k++)
                    {
                        var edge2 = pointEdges[k];

                        int planeIndex1 = -1;
                        int planeIndex2 = -1;

                        // Determine if and which of our 2 planes are identical
                        if (edge1.PlaneIndices[0] == edge2.PlaneIndices[0]) { planeIndex1 = 0; planeIndex2 = 0; }
                        else
                            if (edge1.PlaneIndices[0] == edge2.PlaneIndices[1]) { planeIndex1 = 0; planeIndex2 = 1; }
                            else
                                if (edge1.PlaneIndices[1] == edge2.PlaneIndices[0]) { planeIndex1 = 1; planeIndex2 = 0; }
                                else
                                    if (edge1.PlaneIndices[1] == edge2.PlaneIndices[1]) { planeIndex1 = 1; planeIndex2 = 1; }
                                    else
                                        continue;

                        HalfEdge ingoing;
                        HalfEdge outgoing;
                        short outgoingIndex;

                        var shared_plane = planes[edge1.PlaneIndices[planeIndex1]];
                        var edge1_plane = planes[edge1.PlaneIndices[1 - planeIndex1]];
                        var edge2_plane = planes[edge2.PlaneIndices[1 - planeIndex2]];

                        direction = Vector3.Cross(shared_plane.Normal, edge1_plane.Normal);

                        // Determine the orientation of our two edges to determine
                        // which edge is in-going, and which one is out-going
                        if (Vector3.Dot(direction, edge2_plane.Normal) < 0)
                        {
                            ingoing = edge2.Edge;
                            outgoingIndex = edge1.Edge.TwinIndex;
                            outgoing = edges[outgoingIndex];
                        }
                        else
                        {
                            ingoing = edge1.Edge;
                            outgoingIndex = edge2.Edge.TwinIndex;
                            outgoing = edges[outgoingIndex];
                        }

                        // Link the out-going half-edge to the in-going half-edge
                        ingoing.NextIndex = outgoingIndex;

                        // Add reference to polygon to half-edge, and make sure our
                        // polygon has a reference to a half-edge
                        // Since a half-edge, in this case, serves as a circular
                        // linked list this just works.
                        var polygonIndex = edge1.PlaneIndices[planeIndex1];

                        ingoing.PolygonIndex = polygonIndex;
                        outgoing.PolygonIndex = polygonIndex;

                        var polygon = polygons[polygonIndex];
                        polygon.FirstIndex = outgoingIndex;
                        polygon.Bounds.Add(vertex);
                    }
                }

                // Add the intersection point to the area of our bounding box
                bounds.Add(vertex);
            }

            return new CSGMesh(planes, polygons, edges, vertices, bounds);
        }
Exemple #36
0
 public bool IsOutside(AABBi other)
 {
     return	(this.MaxX - other.MinX) < 0 || (this.MinX - other.MaxX) > 0 ||
             (this.MaxY - other.MinY) < 0 || (this.MinY - other.MaxY) > 0 ||
             (this.MaxZ - other.MinZ) < 0 || (this.MinZ - other.MaxZ) > 0;
 }
        public static CSGMesh Combine(Vector3 offset, IDictionary<CSGNode, CSGMesh> brushMeshes)
        {
            var planeLookup = new Dictionary<Plane, short>();
            var vertexLookup = new Dictionary<Vector3, short>();

            var planes = new List<Plane>();
            var polygons = new List<Polygon>();
            var edges = new List<HalfEdge>();
            var vertices = new List<Vector3>();

            var bounds = new AABBi();

            bounds.Clear();
            int edgeIndex = 0;
            int polygonIndex = 0;
            foreach (var item in brushMeshes)
            {
                var node = item.Key;
                var translation = node.Translation - offset;
                var mesh = item.Value;
                foreach (var edge in mesh.Edges)
                {
                    short vertexIndex;
                    var vertex = mesh.Vertices[edge.VertexIndex] + translation;
                    if (!vertexLookup.TryGetValue(vertex, out vertexIndex))
                    {
                        vertexIndex = (short)vertices.Count;
                        vertices.Add(vertex);
                        vertexLookup.Add(vertex, vertexIndex);
                    }

                    var newEdge = new HalfEdge();
                    newEdge.VertexIndex = vertexIndex;
                    newEdge.NextIndex = (short)(edge.NextIndex + edgeIndex);
                    newEdge.TwinIndex = (short)(edge.TwinIndex + edgeIndex);
                    newEdge.PolygonIndex = (short)(edge.PolygonIndex + polygonIndex);

                    edges.Add(newEdge);
                }

                foreach (var polygon in mesh.Polygons)
                {
                    if (polygon.FirstIndex == -1)
                        continue;
                    short planeIndex;
                    var plane = mesh.Planes[polygon.PlaneIndex];
                    if (!planeLookup.TryGetValue(plane, out planeIndex))
                    {
                        planeIndex = (short)planes.Count;
                        planes.Add(plane);
                        planeLookup.Add(plane, planeIndex);
                    }

                    var newPolygon = new Polygon();
                    newPolygon.PlaneIndex = planeIndex;
                    newPolygon.FirstIndex = (short)(polygon.FirstIndex + edgeIndex);
                    newPolygon.Category = polygon.Category;
                    newPolygon.Visible = polygon.Visible;
                    newPolygon.Bounds.Set(polygon.Bounds, translation);

                    polygons.Add(newPolygon);

                    if (newPolygon.Visible)
                    {
                        var first = edges[newPolygon.FirstIndex];
                        var iterator = first;
                        do
                        {
                            bounds.Add(vertices[iterator.VertexIndex]);
                            iterator = edges[iterator.NextIndex];
                        } while (iterator != first);
                    }
                }
                edgeIndex = edges.Count;
                polygonIndex = polygons.Count;
            }
            return new CSGMesh(planes.ToArray(), polygons, edges, vertices, bounds);
        }
Exemple #38
0
        public void Set(AABBi bounds)
        {
            this.MinX = bounds.MinX;
            this.MinY = bounds.MinY;
            this.MinZ = bounds.MinZ;

            this.MaxX = bounds.MaxX;
            this.MaxY = bounds.MaxY;
            this.MaxZ = bounds.MaxZ;
        }
        public void Intersect(AABBi cuttingNodeBounds,
                              Plane[] cuttingNodePlanes,
                              Vector3 cuttingNodeTranslation,
                              Vector3 inputPolygonTranslation,

                              List<Polygon> inputPolygons,
                              List<Polygon> inside,
                              List<Polygon> aligned,
                              List<Polygon> revAligned,
                              List<Polygon> outside)
        {
            var categories = new PolygonSplitResult[cuttingNodePlanes.Length];
            var translatedPlanes = new Plane[cuttingNodePlanes.Length];
            var translation = cuttingNodeTranslation - inputPolygonTranslation;

            // translate the planes we cut our polygons with so that they're located at the same
            // relative distance from the polygons as the brushes are from each other.
            for (int i = 0; i < cuttingNodePlanes.Length; i++)
                translatedPlanes[i] = Plane.Translated(cuttingNodePlanes[i], translation);

            var vertices = this.Vertices;
            var edges = this.Edges;
            var planes = this.Planes;
            for (int i = inputPolygons.Count - 1; i >= 0; i--)
            {
                var inputPolygon = inputPolygons[i];
                if (inputPolygon.FirstIndex == -1)
                    continue;

                var bounds = inputPolygon.Bounds;
                var finalResult = PolygonSplitResult.CompletelyInside;

                // A quick check if the polygon lies outside the planes we're cutting our polygons with.
                if (!AABBi.IsOutside(cuttingNodeBounds, translation, bounds))
                {
                    PolygonSplitResult intermediateResult;
                    Polygon outsidePolygon = null;
                    for (int otherIndex = 0; otherIndex < translatedPlanes.Length; otherIndex++)
                    {
                        var translatedCuttingPlane = translatedPlanes[otherIndex];

                        var side = cuttingNodePlanes[otherIndex].OnSide(bounds, -translation);
                        if (side == PlaneSideResult.Outside)
                        {
                            finalResult = PolygonSplitResult.CompletelyOutside;
                            break;	// nothing left to process, so we exit
                        }
                        else
                            if (side == PlaneSideResult.Inside)
                                continue;

                        var polygon = inputPolygon;
                        intermediateResult = PolygonSplit(translatedCuttingPlane, inputPolygonTranslation, ref polygon, out outsidePolygon);
                        inputPolygon = polygon;

                        if (intermediateResult == PolygonSplitResult.CompletelyOutside)
                        {
                            finalResult = PolygonSplitResult.CompletelyOutside;
                            break;	// nothing left to process, so we exit
                        }
                        else
                            if (intermediateResult == PolygonSplitResult.Split)
                            {
                                if (outside != null)
                                    outside.Add(outsidePolygon);
                                // Note: left over is still completely inside,
                                //		 or plane (opposite) aligned
                            }
                            else
                                if (intermediateResult != PolygonSplitResult.CompletelyInside)
                                    finalResult = intermediateResult;
                    }
                }
                else
                    finalResult = PolygonSplitResult.CompletelyOutside;

                switch (finalResult)
                {
                    case PolygonSplitResult.CompletelyInside: inside.Add(inputPolygon); break;
                    case PolygonSplitResult.CompletelyOutside: outside.Add(inputPolygon); break;

                    // The polygon can only be visible if it's part of the last brush that shares it's surface area,
                    // otherwise we'd get overlapping polygons if two brushes overlap.
                    // When the (final) polygon is aligned with one of the cutting planes, we know it lies on the surface of
                    // the CSG node we're cutting the polygons with. We also know that this node is not the node this polygon belongs to
                    // because we've done that check earlier on. So we flag this polygon as being invisible.
                    case PolygonSplitResult.PlaneAligned: inputPolygon.Visible = false; aligned.Add(inputPolygon); break;
                    case PolygonSplitResult.PlaneOppositeAligned: inputPolygon.Visible = false; revAligned.Add(inputPolygon); break;
                }
            }
        }
Exemple #40
0
        public void Set(AABBi from, Vector3 translation)
        {
            MinX = (int)Math.Floor(from.MinX + translation.X);
            MinY = (int)Math.Floor(from.MinY + translation.Y);
            MinZ = (int)Math.Floor(from.MinZ + translation.Z);

            MaxX = (int)Math.Ceiling(from.MaxX + translation.X);
            MaxY = (int)Math.Ceiling(from.MaxY + translation.Y);
            MaxZ = (int)Math.Ceiling(from.MaxZ + translation.Z);
        }
        AABBi BruteForceFill(VoxelizationInput input, SilhouetteOcclusionValidator sov, VoxelField voxelField, Vector3i densestVoxel, byte fillByte, List<Occluder> currentOccluders)
        {
            Object syncroot = new Object();
            Int64 largestVolume = 1;
            AABBi largestOccluder = new AABBi(densestVoxel.X, densestVoxel.Y, densestVoxel.Z, densestVoxel.X + 1, densestVoxel.Y + 1, densestVoxel.Z + 1);

            int MaxTopOccluders = 2000;
            List<AABBi> bestOccluders = new List<AABBi>(MaxTopOccluders);

            Parallel.For(densestVoxel.Z + 1, voxelField.VoxelSize.Z, max_z =>
            {
                for (Int32 min_z = densestVoxel.Z; min_z >= 0; --min_z)
                {
                    for (Int32 max_y = densestVoxel.Y + 1; max_y < voxelField.VoxelSize.Y; ++max_y)
                    {
                        for (Int32 min_y = densestVoxel.Y; min_y >= 0; --min_y)
                        {
                            for (Int32 max_x = densestVoxel.X + 1; max_x < voxelField.VoxelSize.X; ++max_x)
                            {
                                for (Int32 min_x = densestVoxel.X; min_x >= 0; --min_x)
                                {
                                    Int32 dx = max_x - min_x;
                                    Int32 dy = max_y - min_y;
                                    Int32 dz = max_z - min_z;
                                    Int64 volume = dx * dy * dz;

                                    if (TestRangeForFreeSpace(voxelField, new AABBi(min_x, min_y, min_z, max_x, max_y, max_z)))
                                    {
                                        lock (syncroot)
                                        {
                                            if (volume > largestVolume)
                                            {
                                                largestVolume = volume;
                                                largestOccluder = new AABBi(min_x, min_y, min_z, max_x, max_y, max_z);
                                                if (bestOccluders.Count >= MaxTopOccluders)
                                                    bestOccluders.RemoveAt(MaxTopOccluders - 1);
                                                bestOccluders.Insert(0, largestOccluder);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        // if we can't expand outward any further there's no point in checking more.
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }

                Debug.WriteLine("Checked " + max_z);
            });

            List<AABBi> relevantOccluders = GetRelevantOccluders(input, currentOccluders);

            long bestCoverage = 0;
            AABBi bestCoverageVolume = largestOccluder;
            foreach (AABBi occluder in bestOccluders)
            {
                List<AABBi> tempOccluders = relevantOccluders.ToList();
                tempOccluders.Add(occluder);
                long coverage = MeasureOccluderOcclusion(sov, input, tempOccluders);
                if (coverage > bestCoverage)
                {
                    bestCoverage = coverage;
                    bestCoverageVolume = occluder;
                }
            }

            FillRange(voxelField, bestCoverageVolume, fillByte);

            return bestCoverageVolume;
        }
Exemple #42
0
 public AABBi(AABBi other)
 {
     Clear();
     Set(other);
 }
        AABBi SimulatedAnnealingFill(VoxelizationInput input, SilhouetteOcclusionValidator sov, VoxelField volume, ref Vector3i densestVoxel, byte fillByte, List<Occluder> currentOccluders)
        {
            AABBi current = new AABBi(densestVoxel.X, densestVoxel.Y, densestVoxel.Z, densestVoxel.X + 1, densestVoxel.Y + 1, densestVoxel.Z + 1);
            AABBi next = new AABBi(0, 0, 0, 0, 0, 0);

            int iteration = -1;

            List<AABBi> relevantOccluders = GetRelevantOccluders(input, currentOccluders);

            List<AABBi> occluders = relevantOccluders.ToList();
            occluders.Add(current);
            long coverage = MeasureOccluderOcclusion(sov, input, occluders);

            double coolignAlpha = 0.999;
            double temperature = 400.0;
            double epsilon = 0.001;

            Random random = new Random(1337);

            int maxItterations = 1000;

            long delta = 0;
            while (temperature > epsilon && iteration < maxItterations)
            {
                iteration++;

                ComputeNext(random, current, next, volume, ref densestVoxel, delta, temperature);

                occluders = relevantOccluders.ToList();
                occluders.Add(next);
                delta = MeasureOccluderOcclusion(sov, input, occluders) - coverage;

                if (delta < 0)
                {
                    next.Clone(current);
                    coverage = delta + coverage;
                }
                else
                {
                    double probability = random.NextDouble();

                    if (probability < Math.Exp(-delta / temperature))
                    {
                        next.Clone(current);
                        coverage = delta + coverage;
                    }
                }

                temperature *= coolignAlpha;

                if (iteration % 400 == 0)
                    Console.WriteLine(coverage);
            }

            FillRange(volume,
                new Vector3i(current.MinX, current.MinY, current.MinZ),
                new Vector3i(current.MaxX, current.MaxY, current.MaxZ),
                fillByte);

            return current;
        }
Exemple #44
0
 public static bool IsOutside(AABBi left, AABBi right)
 {
     return	(left.MaxX - right.MinX) < 0 || (left.MinX - right.MaxX) > 0 ||
             (left.MaxY - right.MinY) < 0 || (left.MinY - right.MaxY) > 0 ||
             (left.MaxZ - right.MinZ) < 0 || (left.MinZ - right.MaxZ) > 0;
 }
Exemple #45
0
 public static bool IsOutside(AABBi left, Vector3 translation, AABBi right)
 {
     return  ((left.MaxX + translation.X) - right.MinX) < 0 || ((left.MinX + translation.X) - right.MaxX) > 0 ||
             ((left.MaxY + translation.Y) - right.MinY) < 0 || ((left.MinY + translation.Y) - right.MaxY) > 0 ||
             ((left.MaxZ + translation.Z) - right.MinZ) < 0 || ((left.MinZ + translation.Z) - right.MaxZ) > 0;
 }
        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));
        }
Exemple #47
0
 protected static bool FillRange(VoxelField volume, AABBi box, Byte fillByte)
 {
     return(FillRange(volume, new Vector3i(box.MinX, box.MinY, box.MinZ), new Vector3i(box.MaxX - 1, box.MaxY - 1, box.MaxZ - 1), fillByte));
 }
Exemple #48
0
        public static CSGMesh CreateFromPlanes(Plane[] brushPlanes)
        {
            var planes = new Plane[brushPlanes.Length];

            for (int i = 0; i < brushPlanes.Length; i++)
            {
                var plane = brushPlanes[i];
                planes[i] = new Plane(plane.A, plane.B, plane.C, plane.D);
            }

            var pointIntersections = new List <PointIntersection>(planes.Length * planes.Length);
            var intersectingPlanes = new List <short>();
            var vertices           = new List <Vector3>();
            var edges = new List <HalfEdge>();

            // Find all point intersections where 3 (or more planes) intersect
            for (short planeIndex1 = 0; planeIndex1 < planes.Length - 2; planeIndex1++)
            {
                var plane1 = planes[planeIndex1];
                for (short planeIndex2 = (short)(planeIndex1 + 1); planeIndex2 < planes.Length - 1; planeIndex2++)
                {
                    var plane2 = planes[planeIndex2];
                    for (short planeIndex3 = (short)(planeIndex2 + 1); planeIndex3 < planes.Length; planeIndex3++)
                    {
                        var plane3 = planes[planeIndex3];

                        // Calculate the intersection
                        var vertex = Plane.Intersection(plane1, plane2, plane3);

                        // Check if the intersection is valid
                        if (float.IsNaN(vertex.X) || float.IsNaN(vertex.Y) || float.IsNaN(vertex.Z) ||
                            float.IsInfinity(vertex.X) || float.IsInfinity(vertex.Y) || float.IsInfinity(vertex.Z))
                        {
                            continue;
                        }

                        intersectingPlanes.Clear();
                        intersectingPlanes.Add(planeIndex1);
                        intersectingPlanes.Add(planeIndex2);
                        intersectingPlanes.Add(planeIndex3);

                        for (short planeIndex4 = 0; planeIndex4 < planes.Length; planeIndex4++)
                        {
                            if (planeIndex4 == planeIndex1 ||
                                planeIndex4 == planeIndex2 ||
                                planeIndex4 == planeIndex3)
                            {
                                continue;
                            }

                            var plane4 = planes[planeIndex4];
                            var side   = plane4.OnSide(vertex);
                            if (side == PlaneSideResult.Intersects)
                            {
                                if (planeIndex4 < planeIndex3)
                                {
                                    // Already found this vertex
                                    goto SkipIntersection;
                                }

                                // We've found another plane which goes trough our found intersection point
                                intersectingPlanes.Add(planeIndex4);
                            }
                            else
                            if (side == PlaneSideResult.Outside)
                            {
                                // Intersection is outside of brush
                                goto SkipIntersection;
                            }
                        }

                        var vertexIndex = (short)vertices.Count;
                        vertices.Add(vertex);

                        // Add intersection point to our list
                        pointIntersections.Add(new PointIntersection(vertexIndex, intersectingPlanes));

SkipIntersection:
                        ;
                    }
                }
            }

            var foundPlanes = new short[2];

            // Find all our intersection edges which are formed by a pair of planes
            // (this could probably be done inside the previous loop)
            for (int i = 0; i < pointIntersections.Count; i++)
            {
                var pointIntersectionA = pointIntersections[i];
                for (int j = i + 1; j < pointIntersections.Count; j++)
                {
                    var pointIntersectionB = pointIntersections[j];
                    var planesIndicesA     = pointIntersectionA.PlaneIndices;
                    var planesIndicesB     = pointIntersectionB.PlaneIndices;

                    short foundPlaneIndex = 0;
                    foreach (var currentPlaneIndex in planesIndicesA)
                    {
                        if (!planesIndicesB.Contains(currentPlaneIndex))
                        {
                            continue;
                        }

                        foundPlanes[foundPlaneIndex] = currentPlaneIndex;
                        foundPlaneIndex++;

                        if (foundPlaneIndex == 2)
                        {
                            break;
                        }
                    }

                    // If foundPlaneIndex is 0 or 1 then either this combination does not exist,
                    // or only goes trough one point
                    if (foundPlaneIndex < 2)
                    {
                        continue;
                    }

                    // Create our found intersection edge
                    var halfEdgeA      = new HalfEdge();
                    var halfEdgeAIndex = (short)edges.Count;
                    edges.Add(halfEdgeA);

                    var halfEdgeB      = new HalfEdge();
                    var halfEdgeBIndex = (short)edges.Count;
                    edges.Add(halfEdgeB);

                    halfEdgeA.TwinIndex = halfEdgeBIndex;
                    halfEdgeB.TwinIndex = halfEdgeAIndex;

                    halfEdgeA.VertexIndex = pointIntersectionA.VertexIndex;
                    halfEdgeB.VertexIndex = pointIntersectionB.VertexIndex;

                    // Add it to our points
                    pointIntersectionA.Edges.Add(new EdgeIntersection(
                                                     halfEdgeA,
                                                     foundPlanes[0],
                                                     foundPlanes[1]));
                    pointIntersectionB.Edges.Add(new EdgeIntersection(
                                                     halfEdgeB,
                                                     foundPlanes[0],
                                                     foundPlanes[1]));
                }
            }


            var polygons = new List <Polygon>();

            for (short i = 0; i < (short)planes.Length; i++)
            {
                var polygon = new Polygon();
                polygon.PlaneIndex = i;
                polygons.Add(polygon);
            }

            var bounds    = new AABBi();
            var direction = new Vector3();

            for (int i = pointIntersections.Count - 1; i >= 0; i--)
            {
                var pointIntersection = pointIntersections[i];
                var pointEdges        = pointIntersection.Edges;

                // Make sure that we have at least 2 edges ...
                // This may happen when a plane only intersects at a single edge.
                if (pointEdges.Count <= 2)
                {
                    pointIntersections.RemoveAt(i);
                    continue;
                }

                var vertexIndex = pointIntersection.VertexIndex;
                var vertex      = vertices[vertexIndex];

                for (int j = 0; j < pointEdges.Count - 1; j++)
                {
                    var edge1 = pointEdges[j];
                    for (int k = j + 1; k < pointEdges.Count; k++)
                    {
                        var edge2 = pointEdges[k];

                        int planeIndex1 = -1;
                        int planeIndex2 = -1;

                        // Determine if and which of our 2 planes are identical
                        if (edge1.PlaneIndices[0] == edge2.PlaneIndices[0])
                        {
                            planeIndex1 = 0; planeIndex2 = 0;
                        }
                        else
                        if (edge1.PlaneIndices[0] == edge2.PlaneIndices[1])
                        {
                            planeIndex1 = 0; planeIndex2 = 1;
                        }
                        else
                        if (edge1.PlaneIndices[1] == edge2.PlaneIndices[0])
                        {
                            planeIndex1 = 1; planeIndex2 = 0;
                        }
                        else
                        if (edge1.PlaneIndices[1] == edge2.PlaneIndices[1])
                        {
                            planeIndex1 = 1; planeIndex2 = 1;
                        }
                        else
                        {
                            continue;
                        }

                        HalfEdge ingoing;
                        HalfEdge outgoing;
                        short    outgoingIndex;

                        var shared_plane = planes[edge1.PlaneIndices[planeIndex1]];
                        var edge1_plane  = planes[edge1.PlaneIndices[1 - planeIndex1]];
                        var edge2_plane  = planes[edge2.PlaneIndices[1 - planeIndex2]];

                        direction = Vector3.Cross(shared_plane.Normal, edge1_plane.Normal);

                        // Determine the orientation of our two edges to determine
                        // which edge is in-going, and which one is out-going
                        if (Vector3.Dot(direction, edge2_plane.Normal) < 0)
                        {
                            ingoing       = edge2.Edge;
                            outgoingIndex = edge1.Edge.TwinIndex;
                            outgoing      = edges[outgoingIndex];
                        }
                        else
                        {
                            ingoing       = edge1.Edge;
                            outgoingIndex = edge2.Edge.TwinIndex;
                            outgoing      = edges[outgoingIndex];
                        }


                        // Link the out-going half-edge to the in-going half-edge
                        ingoing.NextIndex = outgoingIndex;


                        // Add reference to polygon to half-edge, and make sure our
                        // polygon has a reference to a half-edge
                        // Since a half-edge, in this case, serves as a circular
                        // linked list this just works.
                        var polygonIndex = edge1.PlaneIndices[planeIndex1];

                        ingoing.PolygonIndex  = polygonIndex;
                        outgoing.PolygonIndex = polygonIndex;

                        var polygon = polygons[polygonIndex];
                        polygon.FirstIndex = outgoingIndex;
                        polygon.Bounds.Add(vertex);
                    }
                }

                // Add the intersection point to the area of our bounding box
                bounds.Add(vertex);
            }

            return(new CSGMesh(planes, polygons, edges, vertices, bounds));
        }
Exemple #49
0
 protected static bool TestRangeForFreeSpace(VoxelField volume, AABBi box)
 {
     return(TestRangeForFreeSpace(volume, new Vector3i(box.MinX, box.MinY, box.MinZ), new Vector3i(box.MaxX - 1, box.MaxY - 1, box.MaxZ - 1)));
 }
        protected static AABBi ExpandAndFillBox(VoxelField volume, ref Vector3i originVoxel, Byte fillByte)
        {
            int pX, nX, pY, nY, pZ, nZ;
            pX = nX = pY = nY = pZ = nZ = 0;

            volume.SetVoxel(originVoxel.X, originVoxel.Y, originVoxel.Z, fillByte);

            bool boxGrew = false;
            do
            {
                // +Z Axis
                bool pZGrew = FillRange(volume,
                    new Vector3i(originVoxel.X - nX, originVoxel.Y - nY, originVoxel.Z + (pZ + 1)),
                    new Vector3i(originVoxel.X + pX, originVoxel.Y + pY, originVoxel.Z + (pZ + 1)), fillByte);
                if (pZGrew)
                    pZ++;

                // -Z Axis
                bool nZGrew = FillRange(volume,
                    new Vector3i(originVoxel.X - nX, originVoxel.Y - nY, originVoxel.Z - (nZ + 1)),
                    new Vector3i(originVoxel.X + pX, originVoxel.Y + pY, originVoxel.Z - (nZ + 1)), fillByte);
                if (nZGrew)
                    nZ++;

                // +Y Axis
                bool pYGrew = FillRange(volume,
                    new Vector3i(originVoxel.X - nX, originVoxel.Y + (pY + 1), originVoxel.Z - nZ),
                    new Vector3i(originVoxel.X + pX, originVoxel.Y + (pY + 1), originVoxel.Z + pZ), fillByte);
                if (pYGrew)
                    pY++;

                // -Y Axis
                bool nYGrew = FillRange(volume,
                    new Vector3i(originVoxel.X - nX, originVoxel.Y - (nY + 1), originVoxel.Z - nZ),
                    new Vector3i(originVoxel.X + pX, originVoxel.Y - (nY + 1), originVoxel.Z + pZ), fillByte);
                if (nYGrew)
                    nY++;

                // +X Axis
                bool pXGrew = FillRange(volume,
                    new Vector3i(originVoxel.X + (pX + 1), originVoxel.Y - nY, originVoxel.Z - nZ),
                    new Vector3i(originVoxel.X + (pX + 1), originVoxel.Y + pY, originVoxel.Z + pZ), fillByte);
                if (pXGrew)
                    pX++;

                // -X Axis
                bool nXGrew = FillRange(volume,
                    new Vector3i(originVoxel.X - (nX + 1), originVoxel.Y - nY, originVoxel.Z - nZ),
                    new Vector3i(originVoxel.X - (nX + 1), originVoxel.Y + pY, originVoxel.Z + pZ), fillByte);
                if (nXGrew)
                    nX++;

                boxGrew = (pZGrew || nZGrew || pYGrew || nYGrew || pXGrew || nXGrew);
            } while (boxGrew);

            AABBi box = new AABBi();
            box.MinX = originVoxel.X - nX;
            box.MinY = originVoxel.Y - nY;
            box.MinZ = originVoxel.Z - nZ;

            box.MaxX = originVoxel.X + pX + 1;
            box.MaxY = originVoxel.Y + pY + 1;
            box.MaxZ = originVoxel.Z + pZ + 1;

            return box;
        }