Пример #1
0
        public void ClassifyFace(BSPFace face, Vector3 planePoint, Vector3 planeNorm)
        {
            face.Classification = BSPClassification.OnPlane;

            foreach (BSPVertex point in face.Points)
            {
                ClassifyPoint(point, planePoint, planeNorm);

                if (point.Classification != face.Classification)
                {
                    if (face.Classification == BSPClassification.OnPlane)
                    {
                        face.Classification = point.Classification;
                    }
                    else if (point.Classification != BSPClassification.OnPlane)
                    {
                        face.Classification = BSPClassification.Spanning;
                        return;
                    }
                }
            }
            if (face.Classification == BSPClassification.OnPlane) //Place coplanar faces on the front side of the plane if it is facing the same direction
            {
                if (Vector3.Dot(face.Normal, planeNorm) >= 0)
                {
                    face.Classification = BSPClassification.Front;
                }
                else
                {
                    face.Classification = BSPClassification.Back;
                }
            }
        }
Пример #2
0
        public BSPFace FindSplitter(List <BSPFace> faces, ref Vector3 planePoint, ref Vector3 planeNorm)
        {
            int     bestScore = int.MaxValue;
            BSPFace bestFace  = null;
            int     score;

            foreach (BSPFace potential in faces)
            {
#if DEBUG_SPLITTER_DIAGONISTICS
                if (recursionLevel <= 2)
                {
                    Console.WriteLine("Evalulating splitter {0}.", faces.IndexOf(potential));
                }
#endif
                score = EvalulateSplitter(faces, potential);
                if (score < bestScore)
                {
                    bestScore = score;
                    bestFace  = potential;
                }
            }
            if (bestFace != null)
            {
                planePoint = bestFace.Point;
                planeNorm  = bestFace.Normal;
            }
#if DEBUG_SPLITTER_DIAGONISTICS
            if (recursionLevel <= 2)
            {
                if (bestFace != null)
                {
                    Console.WriteLine("Best face is {0}.", faces.IndexOf(bestFace));
                }
                else
                {
                    Console.WriteLine("This object is convex.");
                }
            }
#endif
            return(bestFace);
        }
Пример #3
0
        public void SplitPolygon(BSPFace face, Vector3 planePoint, Vector3 planeNorm, ref BSPFace front, ref BSPFace back)
        {
            front.TextureID = face.TextureID;
            back.TextureID  = face.TextureID;

            BSPVertex firstPoint = face.Points[0];

            if (firstPoint.Classification == BSPClassification.OnPlane)
            {
                front.Points.Add(firstPoint);
                back.Points.Add(firstPoint);
            }
            else if (firstPoint.Classification == BSPClassification.Front)
            {
                front.Points.Add(firstPoint);
            }
            else
            {
                back.Points.Add(firstPoint);
            }

            int       current = 0;
            BSPVertex vert1, vert2;

            for (int i = 1; i < face.Points.Count + 1; i++)
            {
                if (i == face.Points.Count)
                {
                    current = 0;
                }
                else
                {
                    current = i;
                }

                vert1 = face.Points[i - 1];
                vert2 = face.Points[current];

                ClassifyPoint(vert2, planePoint, planeNorm);
                if (vert2.Classification == BSPClassification.OnPlane)
                {
                    front.Points.Add(vert2);
                    back.Points.Add(vert2);
                }
                else
                {
                    Vector3 intersect  = new Vector3();
                    float   percentage = 0.0f;

                    bool split = SplitEdge(vert1.Point, vert2.Point, planePoint, planeNorm, ref percentage, ref intersect);

                    if (split)
                    {
                        Vector3   texDelta = vert2.UVs - vert1.UVs;
                        BSPVertex newVert  = new BSPVertex
                        {
                            Classification = BSPClassification.OnPlane,
                            Point          = intersect,
                            UVs            = texDelta * percentage + vert1.UVs
                        };

                        if (vert2.Classification == BSPClassification.Front)
                        {
                            back.Points.Add(newVert);
                            front.Points.Add(newVert);
                            front.Points.Add(vert2);
                        }
                        else if (vert2.Classification == BSPClassification.Back)
                        {
                            front.Points.Add(newVert);
                            back.Points.Add(newVert);
                            back.Points.Add(vert2);
                        }
                    }
                    else
                    {
                        if (vert2.Classification == BSPClassification.Front)
                        {
                            front.Points.Add(vert2);
                        }
                        else if (vert2.Classification == BSPClassification.Back)
                        {
                            back.Points.Add(vert2);
                        }
                    }
                }
            }
            //TODO: This isn't always accurate at extreme splits.
            front.Normal = face.Normal;
            back.Normal  = face.Normal;

            //front.CalculateNormal();
            front.CalculateCenter();

            //back.CalculateNormal();
            back.CalculateCenter();
        }
Пример #4
0
        public int EvalulateSplitter(List <BSPFace> faces, BSPFace splitter)
        {
            int numFront = 0, numBack = 0, numSplits = 0;

            //int numVertsFront = 0, numVertsBack = 0;
            foreach (BSPFace face in faces)
            {
                if (face == splitter) //Bugfix? Splitter is always on front side.
                {
                    numFront++;
                    continue;
                }
                ClassifyFace(face, splitter.Point, splitter.Normal);
                switch (face.Classification)
                {
                case BSPClassification.Front:
                    numFront++;
                    break;

                case BSPClassification.Back:
                    numBack++;
                    break;

                case BSPClassification.Spanning:
                    numFront++;
                    numBack++;
                    numSplits++;
                    break;
                }

                /*foreach (BSPVertex vert in face.Points)
                 * {
                 *  if (vert.Classification == BSPClassification.Back)
                 *      numVertsBack++;
                 *  else if (vert.Classification == BSPClassification.Front)
                 *      numVertsFront++;
                 *  else if (vert.Classification == BSPClassification.OnPlane)
                 *  {
                 *      numVertsFront++;
                 *  }
                 * }*/
            }

            int newFaces = (numFront + numBack) - faces.Count;

            if (numSplits == 0 && (numFront == 0 || numBack == 0)) //If everything is on one side of this splitter, it has no value.
            {
#if DEBUG_SPLITTER_DIAGONISTICS
                if (recursionLevel <= 2)
                {
                    Console.WriteLine("\tSplitter is not needed.");
                }
#endif
                return(int.MaxValue);
            }
#if DEBUG_SPLITTER_DIAGONISTICS
            if (recursionLevel <= 2)
            {
                Console.WriteLine("\tSplitter score is {0} (Base:{1}, f:{2} b:{3} s:{4})", Math.Abs(numFront - numBack) + (numSplits * 8), faces.Count, numFront, numBack, numSplits);
            }
#endif
            return(Math.Abs(numFront - numBack) + (numSplits * 8));
            //return Math.Abs(numVertsFront - numVertsBack) + (numSplits * 6);
            //return Math.Max(numFront, numBack) + (newFaces * 24);
        }
Пример #5
0
        public void BuildTree(BSPNode node, List <BSPFace> faces)
        {
#if DEBUG_SPLITTER_DIAGONISTICS
            if (recursionLevel == 0)
            {
                Console.WriteLine("----------------------------------------------");
                Console.WriteLine("STARTING");
                Console.WriteLine("----------------------------------------------");
            }
            recursionLevel++;
#endif
            List <BSPFace> frontList = new List <BSPFace>();
            List <BSPFace> backList  = new List <BSPFace>();

            node.Splitter = FindSplitter(faces, ref node.Point, ref node.Normal);

            if (node.Splitter == null) //If a splitter wasn't found, this set of faces is convex
            {
                node.faces = faces;
                node.type  = BSPNodeType.Leaf;
            }
            else //A splitter is known, so do any needed splits and recurse
            {
                foreach (BSPFace face in faces)
                {
                    if (face != node.Splitter) //splitter is classified and added later.
                    {
                        ClassifyFace(face, node.Splitter.Point, node.Splitter.Normal);
                        //else
                        //    //[ISB] Fix bug with splitters ending up on both sides. Doom puts them in front implicity
                        //    face.Classification = BSPClassification.Front;

                        switch (face.Classification)
                        {
                        case BSPClassification.Front:
                            frontList.Add(face);
                            break;

                        case BSPClassification.Back:
                            backList.Add(face);
                            break;

                        case BSPClassification.Spanning:
                            BSPFace frontFace = new BSPFace();
                            BSPFace backFace  = new BSPFace();
                            if (face.TextureID == -1)     // colored face
                            {
                                frontFace.Color = backFace.Color = face.Color;
                            }

                            SplitPolygon(face, node.Splitter.Point, node.Splitter.Normal, ref frontFace, ref backFace);


                            frontList.Add(frontFace);
                            backList.Add(backFace);
                            break;

                        default:
                            throw new Exception("BSPTree::BuildTree: Face has invalid classification.");
                        }
                    }
                }

                //Where does the splitter go?
                //Try it in both the front and back. Whichever one generates the highest score fails.
                int            frontScore = 0;
                int            backScore  = 0;
                int            tempScore;
                List <BSPFace> faceTemp;

                if (frontList.Count > 0)
                {
                    faceTemp = new List <BSPFace>(frontList);
                    faceTemp.Add(node.Splitter);
                    foreach (BSPFace face in faceTemp)
                    {
                        tempScore = EvalulateSplitter(faceTemp, face);
                        if (tempScore != int.MaxValue && tempScore > frontScore)
                        {
                            frontScore = tempScore;
                        }
                    }
                }
                else
                {
                    frontScore = int.MaxValue;
                }

                if (backList.Count > 0)
                {
                    faceTemp = new List <BSPFace>(backList);
                    faceTemp.Add(node.Splitter);
                    foreach (BSPFace face in faceTemp)
                    {
                        tempScore = EvalulateSplitter(faceTemp, face);
                        if (tempScore != int.MaxValue && tempScore > backScore)
                        {
                            backScore = tempScore;
                        }
                    }
                }
                else
                {
                    backScore = int.MaxValue;
                }

                if (frontScore > backScore)
                {
                    frontList.Add(node.Splitter);
                }
                else
                {
                    backList.Add(node.Splitter);
                }

#if DEBUG_SPLITTER_DIAGONISTICS
                if (recursionLevel <= 2)
                {
                    Console.WriteLine("Frontlist:{0} Backlist:{1}, fs<bs:{2}", frontList.Count, backList.Count, frontScore < backScore);
                }
#endif

                if (frontList.Count > 0)
                {
                    BSPNode newNode = new BSPNode();
                    newNode.type = BSPNodeType.Node;

#if DEBUG_SPLITTER_DIAGONISTICS
                    if (recursionLevel <= 1)
                    {
                        Console.WriteLine("Doing front.");
                    }
#endif
                    BuildTree(newNode, frontList);
                    node.Front = newNode;
                }

                if (backList.Count > 0)
                {
                    BSPNode newNode = new BSPNode();
                    newNode.type = BSPNodeType.Node;
#if DEBUG_SPLITTER_DIAGONISTICS
                    if (recursionLevel <= 1)
                    {
                        Console.WriteLine("Doing back.");
                    }
#endif
                    BuildTree(newNode, backList);
                    node.Back = newNode;
                }
            }
#if DEBUG_SPLITTER_DIAGONISTICS
            recursionLevel--;
#endif
        }
        private void Execute(byte[] data, int offset, Polymodel mainModel, Submodel model, BSPModel currentModel)
        {
            short instruction = GetShort(data, ref offset);

            while (true)
            {
                switch (instruction)
                {
                case ModelOpCode.End:
                    return;

                case ModelOpCode.Points:
                {
                    short pointc = GetShort(data, ref offset);
                    for (int i = 0; i < pointc; i++)
                    {
                        interpPoints[i] = GetFixVector(data, ref offset);
                    }
                }
                break;

                case ModelOpCode.FlatPoly:     //FLATPOLY
                {
                    short     pointc = GetShort(data, ref offset);
                    FixVector point  = GetFixVector(data, ref offset);
                    FixVector normal = GetFixVector(data, ref offset);
                    short     color  = GetShort(data, ref offset);

                    short[] points = new short[pointc];         //TODO: seems wasteful to do all these allocations?
                    for (int i = 0; i < pointc; i++)
                    {
                        points[i] = GetShort(data, ref offset);
                    }
                    if (pointc % 2 == 0)
                    {
                        GetShort(data, ref offset);
                    }

                    if (pointc >= 3)
                    {
                        var triangle = new BSPFace();

                        triangle.Normal    = new Vector3(normal.X, normal.Y, normal.Z);
                        triangle.Point     = new Vector3(point.X, point.Y, point.Z);
                        triangle.Color     = color;
                        triangle.TextureID = -1;

                        currentModel.Polygons.Add(triangle);

                        for (int i = 0; i < pointc; i++)
                        {
                            var vxA = interpPoints[points[i]].X;
                            var vyA = interpPoints[points[i]].Y;
                            var vzA = interpPoints[points[i]].Z;

                            triangle.Points.Add(new BSPVertex {
                                    Point = new Vector3(vxA, vyA, vzA), UVs = new Vector3(0.0f, 0.0f, 0.0f)
                                });
                        }
                    }
                }
                break;

                case ModelOpCode.TexturedPoly:     //TMAPPOLY
                {
                    short     pointc  = GetShort(data, ref offset);
                    FixVector point   = GetFixVector(data, ref offset);
                    FixVector normal  = GetFixVector(data, ref offset);
                    short     texture = GetShort(data, ref offset);

                    short[]     points = new short[pointc];     //TODO: seems wasteful to do all these allocations?
                    FixVector[] uvls   = new FixVector[pointc];
                    for (int i = 0; i < pointc; i++)
                    {
                        points[i] = GetShort(data, ref offset);
                    }
                    if (pointc % 2 == 0)
                    {
                        GetShort(data, ref offset);
                    }

                    for (int i = 0; i < pointc; i++)
                    {
                        uvls[i] = GetFixVector(data, ref offset);
                    }

                    if (pointc >= 3)
                    {
                        var triangle = new BSPFace();

                        triangle.Normal    = new Vector3(normal.X, normal.Y, normal.Z);
                        triangle.Point     = new Vector3(point.X, point.Y, point.Z);
                        triangle.TextureID = texture;
                        currentModel.Polygons.Add(triangle);

                        for (int i = 0; i < pointc; i++)
                        {
                            var vxA = interpPoints[points[i]].X;
                            var vyA = interpPoints[points[i]].Y;
                            var vzA = interpPoints[points[i]].Z;

                            var uvxA = uvls[i].X;
                            var uvyA = uvls[i].Y;

                            triangle.Points.Add(new BSPVertex {
                                    Point = new Vector3(vxA, vyA, vzA), UVs = new Vector3(uvxA, uvyA, 0.0f)
                                });
                        }
                    }
                }
                break;

                case ModelOpCode.SortNormal:     //SORTNORM
                {
                    IsPartitioned = true;
                    int       baseOffset  = offset - 2;
                    int       n_points    = GetShort(data, ref offset);
                    FixVector norm        = GetFixVector(data, ref offset);
                    FixVector point       = GetFixVector(data, ref offset);
                    short     backOffset  = GetShort(data, ref offset);
                    short     frontOffset = GetShort(data, ref offset);

                    Execute(data, baseOffset + frontOffset, mainModel, model, currentModel);
                    Execute(data, baseOffset + backOffset, mainModel, model, currentModel);
                }
                break;

                case ModelOpCode.Rod:     //RODBM
                {
                    offset += 34;
                }
                break;

                case ModelOpCode.SubCall:     //SUBCALL
                {
                    int       baseOffset     = offset - 2;
                    short     submodelNum    = GetShort(data, ref offset);
                    FixVector submodelOffset = GetFixVector(data, ref offset);
                    short     modelOffset    = GetShort(data, ref offset);
                    offset += 2;

                    Submodel newModel = mainModel.Submodels[submodelNum];

                    currentModel.modelOffset = submodelOffset;

                    Execute(data, baseOffset + modelOffset, mainModel, newModel, modelDatas[submodelNum]);
                }
                break;

                case ModelOpCode.DefinePointStart:     //DEFPSTART
                {
                    short pointc     = GetShort(data, ref offset);
                    short firstPoint = GetShort(data, ref offset);
                    offset += 2;

                    for (int i = 0; i < pointc; i++)
                    {
                        interpPoints[i + firstPoint] = GetFixVector(data, ref offset);
                    }
                }
                break;

                case ModelOpCode.Glow:
                    offset += 2;
                    break;

                default:
                    throw new Exception(string.Format("Unknown interpreter instruction {0} at offset {1}\n", instruction, offset));
                }
                instruction = GetShort(data, ref offset);
            }
        }