Beispiel #1
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;
            }
            }
        }
        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;
                    }
            }
        }