예제 #1
0
            public void RecursivePlaneSplit(int treeDepth, int maxTreeDepth, int maxGeometryPerNode, int parentSplitAxis)
            {
                Contract.Requires(treeDepth > 0);
                Contract.Requires(maxTreeDepth > 0);
                Contract.Requires(maxGeometryPerNode > 0);
                Contract.Requires(0 <= parentSplitAxis && parentSplitAxis <= 2);
                Contract.Requires(splittingPlane == null, "splitting-plane is null");
                Assert.IsTrue(normalSide == null, "normal-side is null");
                Assert.IsTrue(backSide == null, "back-side is null");
                Assert.IsTrue(geometry != null, "geometry collection is null");
                Assert.IsTrue(geometry.Count > 0, "geometry collection is empty");

                leafColor      = (uint)random.Next();
                totalTreeDepth = Math.Max(totalTreeDepth, treeDepth);


                if (treeDepth >= maxTreeDepth ||            // Have we made the tree deep enough?
                    geometry.Count <= maxGeometryPerNode)   // Stop splitting nodes when current node contains little enough geometry.
                {
                    leafNodes++;
                    ProcessLeafNode();
                    return;
                }

                // Pick an axis-aligned plane to split the current sector.
                // TODO: balance tree better by choosing splitting plane direction based on
                // longest bounding box length, or most even split of geometry?
                int axis;

#if SPLIT_LONGEST_AXIS
                Vector boxExtent = boundingBox.Max - boundingBox.Min;
                boxExtent = new Vector(Math.Abs(boxExtent.x), Math.Abs(boxExtent.y), Math.Abs(boxExtent.z));
                if (boxExtent.x > boxExtent.y)
                {
                    if (boxExtent.x > boxExtent.z)
                    {
                        axis = 0; // X-axis
                    }
                    else
                    {
                        axis = 2; // Z-axis
                    }
                }
                else
                {
                    if (boxExtent.y > boxExtent.z)
                    {
                        axis = 1; // Y-axis
                    }
                    else
                    {
                        axis = 2; // Z-axis
                    }
                }
#else
                // Split on the next axis after the parent's split axis (i.e. X -> Y -> Z -> X ...)
                axis = (parentSplitAxis + 1) % 3;
#endif

                Vector splitPt = boundingBox.Centre;
#if ADAPTIVE_SPLIT
                // Compute centroid of all triangle vertices in this node.
                Vector centroid    = new Vector(0, 0, 0);
                int    vertexCount = 0;
                foreach (Triangle tri in geometry)
                {
                    //if (boundingBox.ContainsPoint(tri.Vertex1))
                    //{
                    //    centroid += tri.Vertex1;
                    //    vertexCount++;
                    //}
                    //if (boundingBox.ContainsPoint(tri.Vertex2))
                    //{
                    //    centroid += tri.Vertex2;
                    //    vertexCount++;
                    //}
                    //if (boundingBox.ContainsPoint(tri.Vertex3))
                    //{
                    //    centroid += tri.Vertex3;
                    //    vertexCount++;
                    //}
                    centroid    += tri.Vertex1;
                    centroid    += tri.Vertex2;
                    centroid    += tri.Vertex3;
                    vertexCount += 3;
                }
                splitPt = centroid / vertexCount;
#endif

                // Create splitting plane.
                switch (axis)
                {
                case 0: splittingPlane = new Plane(splitPt, new Vector(1, 0, 0)); break;     // X-axis

                case 1: splittingPlane = new Plane(splitPt, new Vector(0, 1, 0)); break;     // Y-axis

                case 2: splittingPlane = new Plane(splitPt, new Vector(0, 0, 1)); break;     // Z-axis

                default: Assert.Fail("Unexpected axis index"); break;
                }

                // Copy all geometry from current node to the appropriate child nodes.
                var normalSideGeom = new List <Triangle>();
                var backSideGeom   = new List <Triangle>();
                foreach (Triangle geom in geometry)
                {
                    PlaneHalfSpace planeHalfSpace = geom.IntersectPlane(splittingPlane);
                    Assert.IsTrue((planeHalfSpace & ~(PlaneHalfSpace.NormalSide | PlaneHalfSpace.BackSide)) == 0,
                                  "Unexpected PlaneHalfSpace enumeration bit value");

                    if ((planeHalfSpace & PlaneHalfSpace.NormalSide) != 0)
                    {
                        normalSideGeom.Add(geom);
                    }
                    if ((planeHalfSpace & PlaneHalfSpace.BackSide) != 0)
                    {
                        backSideGeom.Add(geom);
                    }
                }

                // If either child node ends up with no geometry or all geometry, keep the geometry in the current node.
                // TODO: if we cannot split along this axis, attempt to split along the other two axes?
                bool rejectSplit = (normalSideGeom.Count == geometry.Count || backSideGeom.Count == geometry.Count);

#if LOG_SPLITS
                Logger.Log("Tree node: axis {0}, depth: {1}, {2} tri => {3},{4} {5}",
                           axis, treeDepth, geometry.Count, normalSideGeom.Count, backSideGeom.Count, rejectSplit ? "(rejected split)" : "");
#endif

                if (rejectSplit)
                {
                    // Throw away the child nodes, i.e. reverse the splitting of the current node.
                    splittingPlane = null;
                    leafNodes++;
                    ProcessLeafNode();
                    return;
                }

                // Delete all geometry from current node, so that geometry only exists in child nodes.
                geometry.Clear();

                // Create bounding boxes for child nodes.
                Vector backSideBoxMax   = boundingBox.Max;
                Vector normalSideBoxMin = boundingBox.Min;
                switch (axis)
                {
                case 0: backSideBoxMax.x = normalSideBoxMin.x = splitPt.x; break;     // X-axis

                case 1: backSideBoxMax.y = normalSideBoxMin.y = splitPt.y; break;     // Y-axis

                case 2: backSideBoxMax.z = normalSideBoxMin.z = splitPt.z; break;     // Z-axis

                default: Assert.Fail("Unexpected axis index"); break;
                }
                AxisAlignedBox backSideBox   = new AxisAlignedBox(boundingBox.Min, backSideBoxMax);
                AxisAlignedBox normalSideBox = new AxisAlignedBox(normalSideBoxMin, boundingBox.Max);

                // Create child nodes and recursively split them.
                //axis = (axis + 1) % 3;
                treeDepth++;
                if (normalSideGeom.Count > 0)
                {
                    normalSide = new Node(normalSideGeom, normalSideBox);
                    normalSide.RecursivePlaneSplit(treeDepth, maxTreeDepth, maxGeometryPerNode, axis);
                }
                if (backSideGeom.Count > 0)
                {
                    backSide = new Node(backSideGeom, backSideBox);
                    backSide.RecursivePlaneSplit(treeDepth, maxTreeDepth, maxGeometryPerNode, axis);
                }

                // Is current node a leaf node?
                if (normalSide == null && backSide == null)
                {
                    // Yes, so increment the leaf node count.
                    // TODO: we never seem to reach this code!
                    leafNodes++;
                    splittingPlane = null;
                    // TODO: Contracts analyser complains that this assert is always false, due to "initialisation of this.backSide" !?!
                    Contract.Assert(geometry.Count > 0, "Leaf node must contain some geometry");
                    Contract.Assert(splittingPlane == null, "Leaf node must not have a splitting plane");
                }
                else
                {
                    // No, current node is an internal node. Check that no internal node has any geometry.
                    Contract.Assert(geometry.Count == 0, "Internal node must not contain any geometry");
                    Contract.Assert(splittingPlane != null, "Internal node must have a splitting plane");
                }
            }
예제 #2
0
        /// <summary>
        /// Convert a list of triangles into a grid of voxels.
        /// </summary>
        /// <param name="triangles">Triangles bounded by the unit cube centred at the origin</param>
        /// <param name="voxelGridSize">The resolution of the voxel grid</param>
        /// <returns>Number of voxels cells that are 'filled in', i.e. non-empty</returns>
        static public int Convert(List <Raytrace.Triangle> triangles, int voxelGridSize, VoxelGrid voxelGrid)
        {
            Contract.Requires(Contract.ForAll(triangles, (t =>
                                                          t.Vertex1.x >= -0.5 && t.Vertex1.x <= 0.5 &&
                                                          t.Vertex2.y >= -0.5 && t.Vertex2.y <= 0.5 &&
                                                          t.Vertex3.z >= -0.5 && t.Vertex3.z <= 0.5)));

            var voxelColors         = new uint[voxelGridSize, voxelGridSize, voxelGridSize];
            var voxelNormals        = new Vector[voxelGridSize, voxelGridSize, voxelGridSize];
            int totalTriInCellCount = 0;
            int numFilledVoxels     = 0;

            for (var x = 0; x < voxelGridSize; x++)
            {
                var   x0          = ((double)x / voxelGridSize) - 0.5;
                var   x1          = ((double)(x + 1) / voxelGridSize) - 0.5;
                Plane leftPlane   = new Plane(new Vector(x0, 0, 0), new Vector(1, 0, 0));
                Plane rightPlane  = new Plane(new Vector(x1, 0, 0), new Vector(-1, 0, 0));
                var   trisInSlabX = FindTrianglesInsidePlanes(triangles, new List <Plane> {
                    leftPlane, rightPlane
                });

                for (var y = 0; y < voxelGridSize; y++)
                {
                    var   y0          = ((double)y / voxelGridSize) - 0.5;
                    var   y1          = ((double)(y + 1) / voxelGridSize) - 0.5;
                    Plane topPlane    = new Plane(new Vector(0, y0, 0), new Vector(0, 1, 0));
                    Plane bottomPlane = new Plane(new Vector(0, y1, 0), new Vector(0, -1, 0));
                    var   trisInRowXY = FindTrianglesInsidePlanes(trisInSlabX, new List <Plane> {
                        topPlane, bottomPlane                                                                        /*, leftPlane, rightPlane*/
                    });

                    for (var z = 0; z < voxelGridSize; z++)
                    {
                        var   z0         = ((double)z / voxelGridSize) - 0.5;
                        var   z1         = ((double)(z + 1) / voxelGridSize) - 0.5;
                        Plane nearPlane  = new Plane(new Vector(0, 0, z0), new Vector(0, 0, 1));
                        Plane farPlane   = new Plane(new Vector(0, 0, z1), new Vector(0, 0, -1));
                        var   trisInCell = FindTrianglesInsidePlanes(trisInRowXY, new List <Plane> {
                            nearPlane, farPlane                                                                       /*, topPlane, bottomPlane, leftPlane, rightPlane*/
                        });

                        // Color based on number of triangles in cell
//                        uint color = (trisInCell.Count == 0 ? 0 : (uint)(trisInCell.Count * 123456789) | 0xff000000);

                        // Fixed color
//                        uint color = (trisInCell.Count == 0 ? 0 : 0xff00ff00);

                        // Pick color of first triangle in cell
//                        uint color = (trisInCell.Count == 0 ? 0 : trisInCell[0].Color);

                        // Average colors of all triangles in cell
                        uint cellColor = 0;
                        if (trisInCell.Count > 0)
                        {
                            var voxelBox = new AxisAlignedBox(new Vector(x0, y0, z0), new Vector(x1, y1, z1));

                            Color color = Color.Black;
                            int   count = 0;
                            foreach (var tri in trisInCell)
                            {
                                //if (voxelBox.IntersectsTriangle(tri))
                                {
                                    color += new Color(tri.Color);
                                    count++;
                                }
                            }
                            color    /= count; // trisInCell.Count;
                            cellColor = color.ToARGB();

                            numFilledVoxels++;
                            totalTriInCellCount += count; // trisInCell.Count;
                        }

                        voxelColors[x, y, z] = cellColor;

                        // Pick normal of first triangle in cell
                        Vector normal = (trisInCell.Count == 0 ? Vector.Zero : trisInCell[0].Plane.Normal);

                        voxelNormals[x, y, z] = normal;
                    }
                }
            }

            Contract.Assert(totalTriInCellCount >= triangles.Count);

/*
 *          // Random voxel grid
 *          var random = new Random(1234567890);
 *          for (var x = 0; x < voxelGridSize; x++)
 *          {
 *              for (var y = 0; y < voxelGridSize; y++)
 *              {
 *                  for (var z = 0; z < voxelGridSize; z++)
 *                  {
 *                      // pick a random opaque color
 *                      var color = (random.Next(2) == 0 ? 0 : (uint)random.Next() | 0xff000000);
 *                      voxels[x, y, z] = color;
 *                  }
 *              }
 *          }
 */

            voxelGrid.SetData(voxelColors, voxelNormals);

            return(numFilledVoxels);
        }