Exemplo n.º 1
0
 /// <summary>
 /// Looks at the information in the passed <paramref name="face"/> and tries to find the triangle defined
 /// by <paramref name="face"/> with the greatest area. If <paramref name="face"/> does not reference any
 /// vertices then we generate a triangle through the referenced <see cref="Plane"/> instead.
 /// </summary>
 /// <param name="face">The <see cref="Face"/> to find a triangle for.</param>
 /// <returns>Three points defining a triangle which define the plane which <paramref name="face"/> lies on.</returns>
 private Vector3d[] GetPointsForFace(Face face, BrushSide brushSide)
 {
     Vector3d[] ret;
     if (face.numVertices > 2)
     {
         ret = new Vector3d[3];
         double bestArea = 0;
         for (int i = 0; i < face.numIndices / 3; ++i)
         {
             Vector3d[] temp = new Vector3d[] {
                 _bsp.vertices[(int)(face.firstVertex + _bsp.indices[face.firstIndex + (i * 3)])].position,
                 _bsp.vertices[(int)(face.firstVertex + _bsp.indices[face.firstIndex + 1 + (i * 3)])].position,
                 _bsp.vertices[(int)(face.firstVertex + _bsp.indices[face.firstIndex + 2 + (i * 3)])].position
             };
             double area = Vector3d.SqrTriangleArea(temp[0], temp[1], temp[2]);
             if (area > bestArea)
             {
                 bestArea = area;
                 ret      = temp;
             }
         }
         if (bestArea > 0.001)
         {
             return(ret);
         }
     }
     if (face.numEdges > 0)
     {
         // TODO: Edges = triangles
     }
     if (face.plane >= 0 && face.plane < _bsp.planes.Count)
     {
         ret = _bsp.planes[face.plane].GenerateThreePoints();
     }
     else if (brushSide.plane >= 0 && brushSide.plane < _bsp.planes.Count)
     {
         ret = _bsp.planes[brushSide.plane].GenerateThreePoints();
     }
     else
     {
         _master.Print("WARNING: Brush side with no points!");
         return(new Vector3d[3]);
     }
     return(ret);
 }
Exemplo n.º 2
0
        /// <summary>
        /// Processes a <see cref="BrushSide"/> into a state where it can be output into a file that map editors can read.
        /// </summary>
        /// <param name="brushSide">The <see cref="BrushSide"/> to process.</param>
        /// <param name="worldPosition">The position of the parent <see cref="Entity"/> in the world. This is important for calculating UVs on solids.</param>
        /// <param name="sideIndex">The index of this side reference in the parent <see cref="Brush"/>. Important for Call of Duty series maps, since
        /// the first six <see cref="BrushSide"/>s in a <see cref="Brush"/> don't contain <see cref="Plane"/> references.</param>
        /// <returns>The processed <see cref="MAPBrushSode"/> object, to be added to a <see cref="Brush"/> object.</returns>
        private MAPBrushSide ProcessBrushSide(BrushSide brushSide, Vector3d worldPosition, int sideIndex)
        {
            if (brushSide.bevel)
            {
                return(null);
            }
            MAPBrushSide mapBrushSide;
            // The things we'll need to define a .MAP brush side
            string      texture;
            string      material = "wld_lightmap";
            TextureInfo texInfo;

            Vector3d[] threePoints;
            Plane      plane;
            int        flags = 0;

            // If we have a face reference here, let's use it!
            if (brushSide.face >= 0)
            {
                Face face = _bsp.faces[brushSide.face];
                // In Nightfire, faces with "256" flag set should be ignored
                if ((face.flags & (1 << 8)) != 0)
                {
                    return(null);
                }
                texture     = (_master.settings.replace512WithNull && (face.flags & (1 << 9)) != 0) ? "**nulltexture**" : _bsp.textures[face.texture].name;
                threePoints = GetPointsForFace(face, brushSide);
                if (face.plane >= 0 && face.plane < _bsp.planes.Count)
                {
                    plane = _bsp.planes[face.plane];
                }
                else if (brushSide.plane >= 0 && brushSide.plane < _bsp.planes.Count)
                {
                    plane = _bsp.planes[brushSide.plane];
                }
                else
                {
                    plane = new Plane(0, 0, 0, 0);
                }
                if (_bsp.texInfo != null)
                {
                    texInfo = _bsp.texInfo[face.textureInfo];
                }
                else
                {
                    Vector3d[] newAxes = TextureInfo.TextureAxisFromPlane(plane);
                    texInfo = new TextureInfo(newAxes[0], newAxes[1], Vector2d.zero, Vector2d.one, flags, -1, 0);
                }
                flags = _master.settings.noFaceFlags ? 0 : face.flags;
                if (face.material >= 0)
                {
                    material = _bsp.materials[face.material].name;
                }
            }
            else
            {
                // TODO: This is awful. Let's rework the enum to have internal ways to check engine forks.
                if (_bsp.version == MapType.CoD || _bsp.version == MapType.CoD2 || _bsp.version == MapType.CoD4)
                {
                    switch (sideIndex)
                    {
                    case 0: {                             // XMin
                        plane = new Plane(-1, 0, 0, -brushSide.dist);
                        break;
                    }

                    case 1: {                             // XMax
                        plane = new Plane(1, 0, 0, brushSide.dist);
                        break;
                    }

                    case 2: {                             // YMin
                        plane = new Plane(0, -1, 0, -brushSide.dist);
                        break;
                    }

                    case 3: {                             // YMax
                        plane = new Plane(0, 1, 0, brushSide.dist);
                        break;
                    }

                    case 4: {                             // ZMin
                        plane = new Plane(0, 0, -1, -brushSide.dist);
                        break;
                    }

                    case 5: {                             // ZMax
                        plane = new Plane(0, 0, 1, brushSide.dist);
                        break;
                    }

                    default: {
                        plane = _bsp.planes[brushSide.plane];
                        break;
                    }
                    }
                }
                else
                {
                    plane = _bsp.planes[brushSide.plane];
                }
                threePoints = plane.GenerateThreePoints();
                if (brushSide.texture >= 0)
                {
                    // TODO: This is awful. Let's rework the enum to have internal ways to check engine forks.
                    if (_bsp.version == MapType.Source17 ||
                        _bsp.version == MapType.Source18 ||
                        _bsp.version == MapType.Source19 ||
                        _bsp.version == MapType.Source20 ||
                        _bsp.version == MapType.Source21 ||
                        _bsp.version == MapType.Source22 ||
                        _bsp.version == MapType.Source23 ||
                        _bsp.version == MapType.Source27 ||
                        _bsp.version == MapType.Vindictus ||
                        _bsp.version == MapType.DMoMaM ||
                        _bsp.version == MapType.L4D2 ||
                        _bsp.version == MapType.TacticalInterventionEncrypted ||
                        _bsp.version == MapType.Titanfall)
                    {
                        texInfo = _bsp.texInfo[brushSide.texture];
                        TextureData currentTexData;
                        // I've only found one case where this is bad: c2a3a in HL Source. Don't know why.
                        if (texInfo.texture >= 0)
                        {
                            currentTexData = _bsp.texDatas[texInfo.texture];
                            texture        = _bsp.textures.GetTextureAtOffset((uint)_bsp.texTable[currentTexData.stringTableIndex]);
                        }
                        else
                        {
                            texture = "**skiptexture**";
                        }
                    }
                    else
                    {
                        Texture textureDef = _bsp.textures[brushSide.texture];
                        texture = textureDef.name;
                        texInfo = textureDef.texAxes;
                    }
                }
                else
                {
                    Vector3d[] newAxes = TextureInfo.TextureAxisFromPlane(plane);
                    texInfo = new TextureInfo(newAxes[0], newAxes[1], Vector2d.zero, Vector2d.one, flags, -1, 0);
                    texture = "**cliptexture**";
                }
            }

            TextureInfo outputTexInfo;

            if (texInfo != null)
            {
                outputTexInfo = texInfo.BSP2MAPTexInfo(worldPosition);
            }
            else
            {
                Vector3d[] newAxes = TextureInfo.TextureAxisFromPlane(plane);
                outputTexInfo = new TextureInfo(newAxes[0], newAxes[1], Vector2d.zero, Vector2d.one, 0, -1, 0);
            }

            mapBrushSide = new MAPBrushSide()
            {
                vertices    = threePoints,
                plane       = plane,
                texture     = texture,
                textureInfo = outputTexInfo,
                material    = material,
                lgtScale    = 16,
                lgtRot      = 0
            };

            return(mapBrushSide);
        }
Exemplo n.º 3
0
    // -decompileBrush(Brush, int, boolean)
    // Decompiles the Brush and adds it to entitiy #currentEntity as .MAP data.
    private void  decompileBrush(Brush brush, int currentEntity)
    {
        Vector3D origin    = mapFile[currentEntity].Origin;
        int      firstSide = brush.FirstSide;
        int      numSides  = brush.NumSides;

        MAPBrushSide[] brushSides = new MAPBrushSide[0];
        bool           isDetail   = false;

        if (!Settings.noDetail && (brush.Contents[1] & ((sbyte)1 << 1)) != 0)
        {
            isDetail = true;
        }
        MAPBrush mapBrush     = new MAPBrush(numBrshs, currentEntity, isDetail);
        int      numRealFaces = 0;

        Plane[] brushPlanes = new Plane[0];
        //DecompilerThread.OnMessage(this, ": " + numSides + " sides");
        if (mapFile[currentEntity]["classname"] == "func_water")
        {
            mapBrush.Water = true;
        }
        for (int l = 0; l < numSides; l++)
        {
            // For each side of the brush
            BrushSide currentSide = BSPObject.BrushSides[firstSide + l];
            Face      currentFace = BSPObject.Faces[currentSide.Face];        // To find those three points, I can use vertices referenced by faces.
            string    texture     = BSPObject.Textures[currentFace.Texture].Name;
            if ((currentFace.Flags & 0x00000100) == 0)
            {
                // Surfaceflags 512 + 256 + 32 are set only by the compiler, on faces that need to be thrown out.
                if (!texture.ToUpper().Equals("special/clip".ToUpper()) && !texture.ToUpper().Equals("special/playerclip".ToUpper()) && !texture.ToUpper().Equals("special/enemyclip".ToUpper()))
                {
                    if (Settings.replaceWithNull && ((currentFace.Flags & 0x00000200) != 0) && !texture.ToUpper().Equals("special/trigger".ToUpper()))
                    {
                        texture           = "special/null";
                        currentFace.Flags = 0;
                    }
                }
                int   firstVertex = currentFace.FirstVertex;
                int   numVertices = currentFace.NumVertices;
                Plane currentPlane;
                try
                {
                    // I've only ever come across this error once or twice, but something causes it very rarely
                    currentPlane = BSPObject.Planes[currentSide.Plane];
                }
                catch (System.IndexOutOfRangeException)
                {
                    try
                    {
                        // So try to get the plane index from somewhere else
                        currentPlane = BSPObject.Planes[currentFace.Plane];
                    }
                    catch (System.IndexOutOfRangeException f)
                    {
                        // If that fails, BS something
                        DecompilerThread.OnMessage(this, "WARNING: BSP has error, references nonexistant plane " + currentSide.Plane + ", bad side " + (l) + " of brush " + numBrshs + " Entity " + currentEntity);
                        currentPlane = new Plane((double)1, (double)0, (double)0, (double)0);
                    }
                }
                Vector3D[] triangle     = new Vector3D[0];             // Three points define a plane. All I have to do is find three points on that plane.
                bool       pointsWorked = false;
                if (numVertices != 0 && !Settings.planarDecomp)
                {
                    // If the face actually references a set of vertices
                    triangle = new Vector3D[3];
                    double currentHighest = 0.0;
                    // Find the combination of three vertices which gives the greatest area
                    for (int p1 = 0; p1 < numVertices - 2; p1++)
                    {
                        for (int p2 = p1 + 1; p2 < numVertices - 1; p2++)
                        {
                            for (int p3 = p2 + 1; p3 < numVertices; p3++)
                            {
                                double currentArea = Vector3D.SqrTriangleArea(BSPObject.Vertices[firstVertex + p1].Vector, BSPObject.Vertices[firstVertex + p2].Vector, BSPObject.Vertices[firstVertex + p3].Vector);
                                if (currentArea > Settings.precision * Settings.precision * 4.0)                                  // Three collinear points will generate an area of 0 or almost 0
                                {
                                    pointsWorked = true;
                                    if (currentArea > currentHighest)
                                    {
                                        currentHighest = currentArea;
                                        triangle[0]    = BSPObject.Vertices[firstVertex + p1].Vector;
                                        triangle[1]    = BSPObject.Vertices[firstVertex + p2].Vector;
                                        triangle[2]    = BSPObject.Vertices[firstVertex + p3].Vector;
                                    }
                                }
                            }
                        }
                    }
                }
                double[] textureU       = new double[3];
                double[] textureV       = new double[3];
                TexInfo  currentTexInfo = BSPObject.TexInfo[currentFace.TextureScale];
                // Get the lengths of the axis vectors
                double SAxisLength = System.Math.Sqrt(System.Math.Pow((double)currentTexInfo.SAxis.X, 2) + System.Math.Pow((double)currentTexInfo.SAxis.Y, 2) + System.Math.Pow((double)currentTexInfo.SAxis.Z, 2));
                double TAxisLength = System.Math.Sqrt(System.Math.Pow((double)currentTexInfo.TAxis.X, 2) + System.Math.Pow((double)currentTexInfo.TAxis.Y, 2) + System.Math.Pow((double)currentTexInfo.TAxis.Z, 2));
                // In compiled maps, shorter vectors=longer textures and vice versa. This will convert their lengths back to 1. We'll use the actual scale values for length.
                double texScaleU = (1 / SAxisLength);                 // Let's use these values using the lengths of the U and V axes we found above.
                double texScaleV = (1 / TAxisLength);
                textureU[0] = ((double)currentTexInfo.SAxis.X / SAxisLength);
                textureU[1] = ((double)currentTexInfo.SAxis.Y / SAxisLength);
                textureU[2] = ((double)currentTexInfo.SAxis.Z / SAxisLength);
                double originShiftU  = (textureU[0] * origin[X] + textureU[1] * origin[Y] + textureU[2] * origin[Z]) / texScaleU;
                double textureUhiftU = (double)currentTexInfo.SShift - originShiftU;
                textureV[0] = ((double)currentTexInfo.TAxis.X / TAxisLength);
                textureV[1] = ((double)currentTexInfo.TAxis.Y / TAxisLength);
                textureV[2] = ((double)currentTexInfo.TAxis.Z / TAxisLength);
                double originShiftV  = (textureV[0] * origin[X] + textureV[1] * origin[Y] + textureV[2] * origin[Z]) / texScaleV;
                double textureUhiftV = (double)currentTexInfo.TShift - originShiftV;
                float  texRot        = 0;         // In compiled maps this is calculated into the U and V axes, so set it to 0 until I can figure out a good way to determine a better value.
                string material;
                try {
                    material = BSPObject.Materials[currentFace.Material].Name;
                } catch (System.IndexOutOfRangeException) {
                    // In case the BSP has some strange error making it reference nonexistant materials
                    DecompilerThread.OnMessage(this, "WARNING: Map referenced nonexistant material #" + currentFace.Material + ", using wld_lightmap instead!");
                    material = "wld_lightmap";
                }
                double         lgtScale = 16;      // These values are impossible to get from a compiled map since they
                double         lgtRot   = 0;       // are used by RAD for generating lightmaps, then are discarded, I believe.
                MAPBrushSide[] newList  = new MAPBrushSide[brushSides.Length + 1];
                for (int i = 0; i < brushSides.Length; i++)
                {
                    newList[i] = brushSides[i];
                }
                if (Settings.noFaceFlags)
                {
                    currentFace.Flags = 0;
                }
                if (pointsWorked)
                {
                    newList[brushSides.Length] = new MAPBrushSide(currentPlane, triangle, texture, textureU, textureUhiftU, textureV, textureUhiftV, texRot, texScaleU, texScaleV, currentFace.Flags, material, lgtScale, lgtRot);
                }
                else
                {
                    newList[brushSides.Length] = new MAPBrushSide(currentPlane, texture, textureU, textureUhiftU, textureV, textureUhiftV, texRot, texScaleU, texScaleV, currentFace.Flags, material, lgtScale, lgtRot);
                }
                brushSides = newList;
                numRealFaces++;
            }
        }

        for (int i = 0; i < brushSides.Length; i++)
        {
            mapBrush.add(brushSides[i]);
        }

        brushPlanes = new Plane[mapBrush.NumSides];
        for (int i = 0; i < brushPlanes.Length; i++)
        {
            brushPlanes[i] = mapBrush[i].Plane;
        }

        if (!Settings.skipPlaneFlip)
        {
            if (mapBrush.hasBadSide())
            {
                // If there's a side that might be backward
                if (mapBrush.hasGoodSide())
                {
                    // If there's a side that is forward
                    mapBrush = MAPBrush.SimpleCorrectPlanes(mapBrush);
                    numSimpleCorrects++;
                    if (Settings.calcVerts)
                    {
                        // This is performed in advancedcorrect, so don't use it if that's happening
                        try
                        {
                            mapBrush = MAPBrush.CalcBrushVertices(mapBrush);
                        }
                        catch (System.NullReferenceException)
                        {
                            DecompilerThread.OnMessage(this, "WARNING: Brush vertex calculation failed on entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                        }
                    }
                }
                else
                {
                    // If no forward side exists
                    try
                    {
                        mapBrush = MAPBrush.AdvancedCorrectPlanes(mapBrush);
                        numAdvancedCorrects++;
                    }
                    catch (System.ArithmeticException)
                    {
                        DecompilerThread.OnMessage(this, "WARNING: Plane correct returned 0 triangles for entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                    }
                }
            }
            else
            {
                numGoodBrushes++;
            }
        }
        else
        {
            if (Settings.calcVerts)
            {
                // This is performed in advancedcorrect, so don't use it if that's happening
                try
                {
                    mapBrush = MAPBrush.CalcBrushVertices(mapBrush);
                }
                catch (System.NullReferenceException)
                {
                    DecompilerThread.OnMessage(this, "WARNING: Brush vertex calculation failed on entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                }
            }
        }

        // This adds the brush we've been finding and creating to
        // the current entity as an attribute. The way I've coded
        // this whole program and the entities parser, this shouldn't
        // cause any issues at all.
        if (Settings.brushesToWorld)
        {
            mapBrush.Water = false;
            mapFile[0].Brushes.Add(mapBrush);
        }
        else
        {
            mapFile[currentEntity].Brushes.Add(mapBrush);
        }
    }
    // -decompileBrush(Brush, int)
    // Decompiles the Brush and adds it to entitiy #currentEntity as MAPBrush classes.
    private void decompileBrush(Brush brush, int currentEntity)
    {
        Vector3D origin    = mapFile[currentEntity].Origin;
        int      firstSide = brush.FirstSide;
        int      numSides  = brush.NumSides;

        if (firstSide < 0)
        {
            isCoD             = true;
            firstSide         = currentSideIndex;
            currentSideIndex += numSides;
        }
        MAPBrushSide[] brushSides        = new MAPBrushSide[0];
        bool           isDetail          = false;
        int            brushTextureIndex = brush.Texture;

        byte[] contents = new byte[4];
        if (brushTextureIndex >= 0)
        {
            contents = BSPObject.Textures[brushTextureIndex].Contents;
        }
        if (!Settings.noDetail && (contents[3] & ((byte)1 << 3)) != 0)
        {
            // This is the flag according to q3 source
            isDetail = true;             // it's the same as Q2 (and Source), but I haven't found any Q3 maps that use it, so far
        }
        MAPBrush mapBrush     = new MAPBrush(numBrshs, currentEntity, isDetail);
        int      numRealFaces = 0;

        Plane[] brushPlanes = new Plane[0];
        //DecompilerThread.OnMessage(this, ": " + numSides + " sides");
        if (!Settings.noWater && (contents[0] & ((byte)1 << 5)) != 0)
        {
            mapBrush.Water = true;
        }
        bool isVisBrush = false;

        for (int i = 0; i < numSides; i++)
        {
            // For each side of the brush
            BrushSide currentSide      = BSPObject.BrushSides[firstSide + i];
            int       currentFaceIndex = currentSide.Face;
            Plane     currentPlane;
            if (isCoD)
            {
                switch (i)
                {
                case 0:                         // XMin
                    currentPlane = new Plane((double)(-1), (double)0, (double)0, (double)(-currentSide.Dist));
                    break;

                case 1:                         // XMax
                    currentPlane = new Plane((double)1, (double)0, (double)0, (double)currentSide.Dist);
                    break;

                case 2:                         // YMin
                    currentPlane = new Plane((double)0, (double)(-1), (double)0, (double)(-currentSide.Dist));
                    break;

                case 3:                         // YMax
                    currentPlane = new Plane((double)0, (double)1, (double)0, (double)currentSide.Dist);
                    break;

                case 4:                         // ZMin
                    currentPlane = new Plane((double)0, (double)0, (double)(-1), (double)(-currentSide.Dist));
                    break;

                case 5:                         // ZMax
                    currentPlane = new Plane((double)0, (double)0, (double)1, (double)currentSide.Dist);
                    break;

                default:
                    currentPlane = BSPObject.Planes[currentSide.Plane];
                    break;
                }
            }
            else
            {
                currentPlane = BSPObject.Planes[currentSide.Plane];
            }
            Vector3D[] triangle     = new Vector3D[0];         // Three points define a plane. All I have to do is find three points on that plane.
            bool       pointsWorked = false;
            int        firstVertex  = -1;
            int        numVertices  = 0;
            string     texture      = "noshader";
            bool       masked       = false;
            if (currentFaceIndex > -1)
            {
                Face currentFace         = BSPObject.Faces[currentFaceIndex];
                int  currentTextureIndex = currentFace.Texture;
                firstVertex = currentFace.FirstVertex;
                numVertices = currentFace.NumVertices;
                string mask = BSPObject.Textures[currentTextureIndex].Mask;
                if (mask.ToUpper().Equals("ignore".ToUpper()) || mask.Length == 0)
                {
                    texture = BSPObject.Textures[currentTextureIndex].Name;
                }
                else
                {
                    texture = mask.Substring(0, (mask.Length - 4) - (0));                     // Because mask includes file extensions
                    masked  = true;
                }
                if (numVertices != 0 && !Settings.planarDecomp)
                {
                    // If the face actually references a set of vertices
                    triangle = new Vector3D[3];
                    double currentHighest = 0.0;
                    // Find the combination of three vertices which gives the greatest area
                    for (int p1 = 0; p1 < numVertices - 2; p1++)
                    {
                        for (int p2 = p1 + 1; p2 < numVertices - 1; p2++)
                        {
                            for (int p3 = p2 + 1; p3 < numVertices; p3++)
                            {
                                double currentArea = Vector3D.SqrTriangleArea(BSPObject.Vertices[firstVertex + p1].Vector, BSPObject.Vertices[firstVertex + p2].Vector, BSPObject.Vertices[firstVertex + p3].Vector);
                                if (currentArea > Settings.precision * Settings.precision * 4.0)                                  // Three collinear points will generate an area of 0 or almost 0
                                {
                                    pointsWorked = true;
                                    if (currentArea > currentHighest)
                                    {
                                        currentHighest = currentArea;
                                        triangle[0]    = BSPObject.Vertices[firstVertex + p1].Vector;
                                        triangle[1]    = BSPObject.Vertices[firstVertex + p2].Vector;
                                        triangle[2]    = BSPObject.Vertices[firstVertex + p3].Vector;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                // If face information is not available, use the brush side's info instead
                int currentTextureIndex = currentSide.Texture;
                if (currentTextureIndex >= 0)
                {
                    string mask = BSPObject.Textures[currentTextureIndex].Mask;
                    if (mask.ToUpper().Equals("ignore".ToUpper()) || mask.Length == 0)
                    {
                        texture = BSPObject.Textures[currentTextureIndex].Name;
                    }
                    else
                    {
                        texture = mask.Substring(0, (mask.Length - 4) - (0));                         // Because mask includes file extensions
                        masked  = true;
                    }
                }
                else
                {
                    // If neither face or brush side has texture info, fall all the way back to brush. I don't know if this ever happens.
                    if (brushTextureIndex >= 0)
                    {
                        // If none of them have any info, noshader
                        string mask = BSPObject.Textures[brushTextureIndex].Mask;
                        if (mask.ToUpper().Equals("ignore".ToUpper()) || mask.Length == 0)
                        {
                            texture = BSPObject.Textures[brushTextureIndex].Name;
                        }
                        else
                        {
                            texture = mask.Substring(0, (mask.Length - 4) - (0));                             // Because mask includes file extensions
                            masked  = true;
                        }
                    }
                }
            }
            if (texture.ToUpper().Equals("textures/common/vis".ToUpper()))
            {
                isVisBrush = true;
                return;                 // TODO: Try to recreate the vis entity? It's impossible to recreate the links...
            }
            // Get the lengths of the axis vectors.
            // TODO: This information seems to be contained in Q3's vertex structure. But there doesn't seem
            // to be a way to directly link faces to brush sides.
            double     UAxisLength  = 1;
            double     VAxisLength  = 1;
            double     texScaleS    = 1;
            double     texScaleT    = 1;
            Vector3D[] textureAxes  = TexInfo.textureAxisFromPlane(currentPlane);
            double     originShiftS = (textureAxes[0].X * origin[X]) + (textureAxes[0].Y * origin[Y]) + (textureAxes[0].Z * origin[Z]);
            double     originShiftT = (textureAxes[1].X * origin[X]) + (textureAxes[1].Y * origin[Y]) + (textureAxes[1].Z * origin[Z]);
            double     textureShiftS;
            double     textureShiftT;
            if (firstVertex >= 0)
            {
                textureShiftS = (double)BSPObject.Vertices[firstVertex].TexCoordX - originShiftS;
                textureShiftT = (double)BSPObject.Vertices[firstVertex].TexCoordY - originShiftT;
            }
            else
            {
                textureShiftS = 0 - originShiftS;
                textureShiftT = 0 - originShiftT;
            }
            float  texRot = 0;
            string material;
            if (masked)
            {
                material = "wld_masked";
            }
            else
            {
                material = "wld_lightmap";
            }
            double         lgtScale = 16;
            double         lgtRot   = 0;
            MAPBrushSide[] newList  = new MAPBrushSide[brushSides.Length + 1];
            for (int j = 0; j < brushSides.Length; j++)
            {
                newList[j] = brushSides[j];
            }
            int flags;
            //if(Settings.noFaceFlags) {
            flags = 0;
            //}
            if (pointsWorked)
            {
                newList[brushSides.Length] = new MAPBrushSide(currentPlane, triangle, texture, textureAxes[0].Point, textureShiftS, textureAxes[1].Point, textureShiftT, texRot, texScaleS, texScaleT, flags, material, lgtScale, lgtRot);
            }
            else
            {
                newList[brushSides.Length] = new MAPBrushSide(currentPlane, texture, textureAxes[0].Point, textureShiftS, textureAxes[1].Point, textureShiftT, texRot, texScaleS, texScaleT, flags, material, lgtScale, lgtRot);
            }
            brushSides = newList;
            numRealFaces++;
        }

        for (int i = 0; i < brushSides.Length; i++)
        {
            mapBrush.add(brushSides[i]);
        }

        brushPlanes = new Plane[mapBrush.NumSides];
        for (int i = 0; i < brushPlanes.Length; i++)
        {
            brushPlanes[i] = mapBrush[i].Plane;
        }

        if (isCoD && mapBrush.NumSides > 6)
        {
            // Now we need to get rid of all the sides that aren't used. Get a list of
            // the useless sides from one brush, and delete those sides from all of them,
            // since they all have the same sides.
            if (!Settings.dontCull && numSides > 6)
            {
                int[] badSides = MAPBrush.findUnusedPlanes(mapBrush);
                // Need to iterate backward, since these lists go from low indices to high, and
                // the index of all subsequent items changes when something before it is removed.
                if (mapBrush.NumSides - badSides.Length < 4)
                {
                    DecompilerThread.OnMessage(this, "WARNING: Plane cull returned less than 4 sides for entity " + currentEntity + " brush " + numBrshs);
                }
                else
                {
                    for (int i = badSides.Length - 1; i > -1; i--)
                    {
                        mapBrush.delete(badSides[i]);
                    }
                }
            }
        }

        if (!Settings.skipPlaneFlip)
        {
            if (mapBrush.hasBadSide())
            {
                // If there's a side that might be backward
                if (mapBrush.hasGoodSide())
                {
                    // If there's a side that is forward
                    mapBrush = MAPBrush.SimpleCorrectPlanes(mapBrush);
                    numSimpleCorrects++;
                    if (Settings.calcVerts)
                    {
                        // This is performed in advancedcorrect, so don't use it if that's happening
                        try {
                            mapBrush = MAPBrush.CalcBrushVertices(mapBrush);
                        } catch (System.NullReferenceException) {
                            DecompilerThread.OnMessage(this, "WARNING: Brush vertex calculation failed on entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                        }
                    }
                }
                else
                {
                    // If no forward side exists
                    try {
                        mapBrush = MAPBrush.AdvancedCorrectPlanes(mapBrush);
                        numAdvancedCorrects++;
                    } catch (System.ArithmeticException) {
                        DecompilerThread.OnMessage(this, "WARNING: Plane correct returned 0 triangles for entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                    }
                }
            }
            else
            {
                numGoodBrushes++;
            }
        }
        else
        {
            if (Settings.calcVerts)
            {
                // This is performed in advancedcorrect, so don't use it if that's happening
                try {
                    mapBrush = MAPBrush.CalcBrushVertices(mapBrush);
                } catch (System.NullReferenceException) {
                    DecompilerThread.OnMessage(this, "WARNING: Brush vertex calculation failed on entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                }
            }
        }

        // This adds the brush we've been finding and creating to
        // the current entity as an attribute. The way I've coded
        // this whole program and the entities parser, this shouldn't
        // cause any issues at all.
        if (Settings.brushesToWorld)
        {
            mapBrush.Water = false;
            worldspawn.Brushes.Add(mapBrush);
        }
        else
        {
            mapFile[currentEntity].Brushes.Add(mapBrush);
        }
    }
Exemplo n.º 5
0
    // METHODS

    public void readBSP()
    {
        try {
            byte[] theLump = new byte[0];
            byte[] vis     = new byte[0];
            BSPObject = new BSP(BSPFile.FullName);
            Console.WriteLine("Opening " + BSPFile.FullName);
            for (int i = 0; i < 18; i++)
            {
                try {
                    theLump = readLumpNum(i);
                    switch (i)
                    {
                    case 0:
                        BSPObject.Entities = Entity.createLump(theLump);
                        break;

                    case 1:
                        BSPObject.Planes = Plane.createLump(theLump);
                        break;

                    case 2:
                        BSPObject.Textures = Texture.createLump(theLump);
                        break;

                    case 3:
                        BSPObject.Materials = Texture.createLump(theLump);
                        break;

                    case 4:
                        BSPObject.Vertices = Vertex.createLump(theLump);
                        break;

                    case 5:
                        BSPObject.Normals = new Lump <LumpObject>(theLump.Length, 0);
                        break;

                    case 6:
                        BSPObject.Indices = new NumList(theLump, NumList.dataType.UINT);
                        break;

                    case 7:
                        vis = theLump;
                        break;

                    case 8:
                        BSPObject.Nodes = Node.createLump(theLump);
                        break;

                    case 9:
                        BSPObject.Faces = Face.createLump(theLump);
                        break;

                    case 10:
                        BSPObject.Lightmaps = new NumList(theLump, NumList.dataType.UBYTE);
                        break;

                    case 11:
                        BSPObject.Leaves = Leaf.createLump(theLump);
                        break;

                    case 12:
                        BSPObject.MarkSurfaces = new NumList(theLump, NumList.dataType.UINT);
                        break;

                    case 13:
                        BSPObject.MarkBrushes = new NumList(theLump, NumList.dataType.UINT);
                        break;

                    case 14:
                        BSPObject.Models = Model.createLump(theLump);
                        break;

                    case 15:
                        BSPObject.Brushes = Brush.createLump(theLump);
                        break;

                    case 16:
                        BSPObject.BrushSides = BrushSide.createLump(theLump);
                        break;

                    case 17:
                        BSPObject.TexInfo = TexInfo.createLump(theLump);
                        break;
                    }
                } catch {
                    dumpLump(theLump);
                }
            }
            try {
                int visLength = BSPObject.Leaves[2].PVS;
                if (visLength > 0 && vis.Length > 0)
                {
                    BSPObject.Vis = new Lump <LumpObject>(vis, visLength);
                }
            } catch (ArgumentOutOfRangeException) {; }
            if (BSPObject.Vis == null)
            {
                BSPObject.Vis = new Lump <LumpObject>(0, 0);
            }
            BSPObject.printBSPReport();
        }
        catch (System.IO.IOException) {
            Console.WriteLine("Unable to access BSP file! Is it open in another program?");
        }
        br.Close();
    }
Exemplo n.º 6
0
    // -decompileBrush38(Brush, int, boolean)
    // Decompiles the Brush and adds it to entitiy #currentEntity as .MAP data.
    private void decompileBrush(Brush brush, int currentEntity)
    {
        Vector3D origin    = mapFile[currentEntity].Origin;
        int      firstSide = brush.FirstSide;
        int      numSides  = brush.NumSides;
        bool     isDetail  = false;

        MAPBrushSide[] brushSides = new MAPBrushSide[numSides];
        if (!Settings.noDetail && (brush.Contents[3] & ((sbyte)1 << 3)) != 0)
        {
            // According to Q2's source, this is the detail flag
            isDetail = true;
        }
        MAPBrush mapBrush = new MAPBrush(numBrshs, currentEntity, isDetail);

        //DecompilerThread.OnMessage(this, ": " + numSides + " sides");
        if (!Settings.noWater && (brush.Contents[0] & ((sbyte)1 << 5)) != 0)
        {
            mapBrush.Water = true;
        }
        for (int i = 0; i < numSides; i++)
        {
            // For each side of the brush
            Vector3D[] plane        = new Vector3D[3];                     // Three points define a plane. All I have to do is find three points on that plane.
            BrushSide  currentSide  = BSPObject.BrushSides[firstSide + i];
            Plane      currentPlane = BSPObject.Planes[currentSide.Plane]; // To find those three points, I must extrapolate from planes until I find a way to associate faces with brushes
            Texture    currentTexture;
            bool       isDuplicate = false;
            for (int j = i + 1; j < numSides; j++)
            {
                // For each subsequent side of the brush
                // For some reason, QUAKE 2 MAKES COPLANAR SIDES OF BRUSHES. I don't know why but it's stupid.
                if (currentPlane.Equals(BSPObject.Planes[BSPObject.BrushSides[firstSide + j].Plane]))
                {
                    DecompilerThread.OnMessage(this, "WARNING: Duplicate planes in entity " + currentEntity + " brush " + numBrshs + ", sides " + i + " and " + j + " (BSP planes " + currentSide.Plane + " and " + BSPObject.BrushSides[firstSide + j].Plane);
                    isDuplicate = true;
                }
            }
            if (!isDuplicate)
            {
                /*
                 * if(!Settings.planarDecomp) {
                 * // Find a face whose plane and texture information corresponds to the current side
                 * // It doesn't really matter if it's the actual brush's face, just as long as it provides vertices.
                 * SiNFace currentFace=null;
                 * boolean faceFound=false;
                 * for(int j=0;j<BSP.getSFaces().size();j++) {
                 * currentFace=BSP.getSFaces().getFace(j);
                 * if(currentFace.getPlane()==currentSide.getPlane() && currentFace.getTexInfo()==currentSide.getTexInfo() && currentFace.getNumEdges()>1) {
                 * faceFound=true;
                 * break;
                 * }
                 * }
                 * if(faceFound) {
                 * int markEdge=BSP.getMarkEdges().getInt(currentFace.getFirstEdge());
                 * int currentMarkEdge=0;
                 * int firstVertex;
                 * int secondVertex;
                 * if(markEdge>0) {
                 * firstVertex=BSP.getEdges().getEdge(markEdge).getFirstVertex();
                 * secondVertex=BSP.getEdges().getEdge(markEdge).getSecondVertex();
                 * } else {
                 * firstVertex=BSP.getEdges().getEdge(-markEdge).getSecondVertex();
                 * secondVertex=BSP.getEdges().getEdge(-markEdge).getFirstVertex();
                 * }
                 * int numVertices=currentFace.getNumEdges()+1;
                 * boolean pointsWorked=false;
                 * plane[0]=new Vector3D(BSP.getVertices().getVertex(firstVertex)); // Grab and store the first one
                 * plane[1]=new Vector3D(BSP.getVertices().getVertex(secondVertex)); // The second should be unique from the first
                 * boolean second=false;
                 * if(plane[0].equals(plane[1])) { // If for some messed up reason they are the same
                 * for(currentMarkEdge=1;currentMarkEdge<currentFace.getNumEdges();currentMarkEdge++) { // For each edge after the first one
                 * markEdge=BSP.getMarkEdges().getInt(currentFace.getFirstEdge()+currentMarkEdge);
                 * if(markEdge>0) {
                 * plane[1]=new Vector3D(BSP.getVertices().getVertex(BSP.getEdges().getEdge(markEdge).getFirstVertex()));
                 * } else {
                 * plane[1]=new Vector3D(BSP.getVertices().getVertex(BSP.getEdges().getEdge(-markEdge).getSecondVertex()));
                 * }
                 * if(!plane[0].equals(plane[1])) { // Make sure the point isn't the same as the first one
                 * second=false;
                 * break; // If it isn't the same, this point is good
                 * } else {
                 * if(markEdge>0) {
                 * plane[1]=new Vector3D(BSP.getVertices().getVertex(BSP.getEdges().getEdge(markEdge).getSecondVertex()));
                 * } else {
                 * plane[1]=new Vector3D(BSP.getVertices().getVertex(BSP.getEdges().getEdge(-markEdge).getFirstVertex()));
                 * }
                 * if(!plane[0].equals(plane[1])) {
                 * second=true;
                 * break;
                 * }
                 * }
                 * }
                 * }
                 * if(second) {
                 * currentMarkEdge++;
                 * }
                 * for(;currentMarkEdge<currentFace.getNumEdges();currentMarkEdge++) {
                 * markEdge=BSP.getMarkEdges().getInt(currentFace.getFirstEdge()+currentMarkEdge);
                 * if(second) {
                 * if(markEdge>0) {
                 * plane[2]=new Vector3D(BSP.getVertices().getVertex(BSP.getEdges().getEdge(markEdge).getFirstVertex()));
                 * } else {
                 * plane[2]=new Vector3D(BSP.getVertices().getVertex(BSP.getEdges().getEdge(-markEdge).getSecondVertex()));
                 * }
                 * if(!plane[2].equals(plane[0]) && !plane[2].equals(plane[1])) { // Make sure no point is equal to the third one
                 * if((Vector3D.crossProduct(plane[0].subtract(plane[1]), plane[0].subtract(plane[2])).X!=0) || // Make sure all
                 * (Vector3D.crossProduct(plane[0].subtract(plane[1]), plane[0].subtract(plane[2])).Y!=0) || // three points
                 * (Vector3D.crossProduct(plane[0].subtract(plane[1]), plane[0].subtract(plane[2])).Z!=0)) { // are not collinear
                 * pointsWorked=true;
                 * break;
                 * }
                 * }
                 * }
                 * // if we get to here, the first vertex of the edge failed, or was already used
                 * if(markEdge>0) { // use the second vertex
                 * plane[2]=new Vector3D(BSP.getVertices().getVertex(BSP.getEdges().getEdge(markEdge).getSecondVertex()));
                 * } else {
                 * plane[2]=new Vector3D(BSP.getVertices().getVertex(BSP.getEdges().getEdge(-markEdge).getFirstVertex()));
                 * }
                 * if(!plane[2].equals(plane[0]) && !plane[2].equals(plane[1])) { // Make sure no point is equal to the third one
                 * if((Vector3D.crossProduct(plane[0].subtract(plane[1]), plane[0].subtract(plane[2])).X!=0) || // Make sure all
                 * (Vector3D.crossProduct(plane[0].subtract(plane[1]), plane[0].subtract(plane[2])).Y!=0) || // three points
                 * (Vector3D.crossProduct(plane[0].subtract(plane[1]), plane[0].subtract(plane[2])).Z!=0)) { // are not collinear
                 * pointsWorked=true;
                 * break;
                 * }
                 * }
                 * // If we get here, neither point worked and we need to try the next edge.
                 * second=true;
                 * }
                 * if(!pointsWorked) {
                 * plane=Plane.generatePlanePoints(currentPlane);
                 * }
                 * } else { // Face not found
                 * plane=Plane.generatePlanePoints(currentPlane);
                 * }
                 * } else { // Planar decomp only */
                plane = Plane.generatePlanePoints(currentPlane);
                // }
                string   texture   = "special/clip";
                double[] textureU  = new double[3];
                double[] textureV  = new double[3];
                double   UShift    = 0;
                double   VShift    = 0;
                double   texScaleU = 1;
                double   texScaleV = 1;
                if (currentSide.Texture > -1)
                {
                    currentTexture = BSPObject.Textures[currentSide.Texture];
                    if ((currentTexture.Flags[0] & ((sbyte)1 << 2)) != 0)
                    {
                        texture = "special/sky";
                    }
                    else
                    {
                        if ((currentTexture.Flags[1] & ((sbyte)1 << 1)) != 0)
                        {
                            texture = "special/skip";
                        }
                        else
                        {
                            if ((currentTexture.Flags[1] & ((sbyte)1 << 0)) != 0)
                            {
                                if (currentEntity == 0)
                                {
                                    texture = "special/hint";                                     // Hint was not used the same way in Quake 2 as other games.
                                }
                                else
                                {
                                    // For example, a Hint brush CAN be used for a trigger in Q2 and is used as such a lot.
                                    texture = "special/trigger";
                                }
                            }
                            else
                            {
                                texture = currentTexture.Name;
                            }
                        }
                    }
                    // Get the lengths of the axis vectors
                    double SAxisLength = System.Math.Sqrt(System.Math.Pow((double)currentTexture.TexAxes.SAxis.X, 2) + System.Math.Pow((double)currentTexture.TexAxes.SAxis.Y, 2) + System.Math.Pow((double)currentTexture.TexAxes.SAxis.Z, 2));
                    double TAxisLength = System.Math.Sqrt(System.Math.Pow((double)currentTexture.TexAxes.TAxis.X, 2) + System.Math.Pow((double)currentTexture.TexAxes.TAxis.Y, 2) + System.Math.Pow((double)currentTexture.TexAxes.TAxis.Z, 2));
                    // In compiled maps, shorter vectors=longer textures and vice versa. This will convert their lengths back to 1. We'll use the actual scale values for length.
                    texScaleU   = (1 / SAxisLength);                   // Let's use these values using the lengths of the U and V axes we found above.
                    texScaleV   = (1 / TAxisLength);
                    textureU[0] = ((double)currentTexture.TexAxes.SAxis.X / SAxisLength);
                    textureU[1] = ((double)currentTexture.TexAxes.SAxis.Y / SAxisLength);
                    textureU[2] = ((double)currentTexture.TexAxes.SAxis.Z / SAxisLength);
                    textureV[0] = ((double)currentTexture.TexAxes.TAxis.X / TAxisLength);
                    textureV[1] = ((double)currentTexture.TexAxes.TAxis.Y / TAxisLength);
                    textureV[2] = ((double)currentTexture.TexAxes.TAxis.Z / TAxisLength);
                    UShift      = (double)currentTexture.TexAxes.SShift;
                    VShift      = (double)currentTexture.TexAxes.TShift;
                }
                else
                {
                    Vector3D[] axes = TexInfo.textureAxisFromPlane(currentPlane);
                    textureU = axes[0].Point;
                    textureV = axes[1].Point;
                }
                double originShiftU  = (textureU[0] * origin[X] + textureU[1] * origin[Y] + textureU[2] * origin[Z]) / texScaleU;
                double textureShiftU = UShift - originShiftU;
                double originShiftV  = (textureV[0] * origin[X] + textureV[1] * origin[Y] + textureV[2] * origin[Z]) / texScaleV;
                double textureShiftV = VShift - originShiftV;
                float  texRot        = 0;              // In compiled maps this is calculated into the U and V axes, so set it to 0 until I can figure out a good way to determine a better value.
                int    flags         = 0;              // Set this to 0 until we can somehow associate faces with brushes
                string material      = "wld_lightmap"; // Since materials are a NightFire only thing, set this to a good default
                double lgtScale      = 16;             // These values are impossible to get from a compiled map since they
                double lgtRot        = 0;              // are used by RAD for generating lightmaps, then are discarded, I believe.
                brushSides[i] = new MAPBrushSide(plane, texture, textureU, textureShiftU, textureV, textureShiftV, texRot, texScaleU, texScaleV, flags, material, lgtScale, lgtRot);
                mapBrush.add(brushSides[i]);
            }
        }

        if (!Settings.skipPlaneFlip)
        {
            if (mapBrush.hasBadSide())
            {
                // If there's a side that might be backward
                if (mapBrush.hasGoodSide())
                {
                    // If there's a side that is forward
                    mapBrush = MAPBrush.SimpleCorrectPlanes(mapBrush);
                    numSimpleCorrects++;
                    if (Settings.calcVerts)
                    {
                        // This is performed in advancedcorrect, so don't use it if that's happening
                        try
                        {
                            mapBrush = MAPBrush.CalcBrushVertices(mapBrush);
                        }
                        catch (System.NullReferenceException)
                        {
                            DecompilerThread.OnMessage(this, "WARNING: Brush vertex calculation failed on entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                        }
                    }
                }
                else
                {
                    // If no forward side exists
                    try
                    {
                        mapBrush = MAPBrush.AdvancedCorrectPlanes(mapBrush);
                        numAdvancedCorrects++;
                    }
                    catch (System.ArithmeticException)
                    {
                        DecompilerThread.OnMessage(this, "WARNING: Plane correct returned 0 triangles for entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                    }
                }
            }
            else
            {
                numGoodBrushes++;
            }
        }
        else
        {
            if (Settings.calcVerts)
            {
                // This is performed in advancedcorrect, so don't use it if that's happening
                try
                {
                    mapBrush = MAPBrush.CalcBrushVertices(mapBrush);
                }
                catch (System.NullReferenceException)
                {
                    DecompilerThread.OnMessage(this, "WARNING: Brush vertex calculation failed on entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                }
            }
        }

        // This adds the brush we've been finding and creating to
        // the current entity as an attribute. The way I've coded
        // this whole program and the entities parser, this shouldn't
        // cause any issues at all.
        if (Settings.brushesToWorld)
        {
            mapBrush.Water = false;
            mapFile[0].Brushes.Add(mapBrush);
        }
        else
        {
            mapFile[currentEntity].Brushes.Add(mapBrush);
        }
    }
    // -decompileBrush38(Brush, int, boolean)
    // Decompiles the Brush and adds it to entitiy #currentEntity as .MAP data.
    private void decompileBrush(Brush brush, int currentEntity)
    {
        Vector3D origin    = mapFile[currentEntity].Origin;
        int      firstSide = brush.FirstSide;
        int      numSides  = brush.NumSides;

        MAPBrushSide[] brushSides = new MAPBrushSide[numSides];
        bool           isDetail   = false;

        if (currentEntity == 0 && !Settings.noDetail && (brush.Contents[3] & ((sbyte)1 << 3)) != 0)
        {
            isDetail = true;
        }
        MAPBrush mapBrush = new MAPBrush(numBrshs, currentEntity, isDetail);

        if (currentEntity == 0 && !Settings.noWater && (brush.Contents[0] & ((sbyte)1 << 5)) != 0)
        {
            mapBrush.Water = true;
        }
        //DecompilerThread.OnMessage(this, ": " + numSides + " sides, detail: " + isDetail);
        for (int i = 0; i < numSides; i++)
        {
            // For each side of the brush
            BrushSide currentSide = BSPObject.BrushSides[firstSide + i];
            if (currentSide.isBevel() == 0)
            {
                // Bevel sides are evil
                Vector3D[] plane        = new Vector3D[3];                     // Three points define a plane. All I have to do is find three points on that plane.
                Plane      currentPlane = BSPObject.Planes[currentSide.Plane]; // To find those three points, I must extrapolate from planes until I find a way to associate faces with brushes
                bool       isDuplicate  = false;                               /* TODO: We sure don't want duplicate planes (though this is already handled by the MAPBrush class). Make sure neither checked side is bevel.
                                                                                * for(int j=i+1;j<numSides;j++) { // For each subsequent side of the brush
                                                                                * if(currentPlane.equals(BSPObject.Planes.getPlane(BSPObject.getBrushSides()[firstSide+j).getPlane()))) {
                                                                                * DecompilerThread.OnMessage(this, "WARNING: Duplicate planes in a brush, sides "+i+" and "+j,Settings.VERBOSITY_WARNINGS);
                                                                                * isDuplicate=true;
                                                                                * }
                                                                                * }*/
                if (!isDuplicate)
                {
                    TexInfo currentTexInfo = null;
                    string  texture        = "tools/toolsclip";
                    if (currentSide.Texture > -1)
                    {
                        currentTexInfo = BSPObject.TexInfo[currentSide.Texture];
                    }
                    else
                    {
                        int dataIndex = BSPObject.findTexDataWithTexture("tools/toolsclip");
                        if (dataIndex >= 0)
                        {
                            currentTexInfo = new TexInfo(new Vector3D(0, 0, 0), 0, new Vector3D(0, 0, 0), 0, 0, dataIndex);
                        }
                    }
                    if (currentTexInfo != null)
                    {
                        SourceTexData currentTexData;
                        if (currentTexInfo.Texture >= 0)
                        {
                            // I've only found one case where this is a problem: c2a3a in HL Source. Don't know why.
                            currentTexData = BSPObject.TexDatas[currentTexInfo.Texture];
                            texture        = BSPObject.Textures.getTextureAtOffset((uint)BSPObject.TexTable[currentTexData.StringTableIndex]);
                        }
                        else
                        {
                            texture = "tools/toolsskip";
                        }
                    }
                    double[] textureU      = new double[3];
                    double[] textureV      = new double[3];
                    double   textureShiftU = 0;
                    double   textureShiftV = 0;
                    double   texScaleU     = 1;
                    double   texScaleV     = 1;
                    // Get the lengths of the axis vectors
                    if ((texture.Length > 6 && texture.Substring(0, (6) - (0)).ToUpper().Equals("tools/".ToUpper())) || currentTexInfo == null)
                    {
                        // Tools textured faces do not maintain their own texture axes. Therefore, an arbitrary axis is
                        // used in the compiled map. When decompiled, these axes might smear the texture on the face. Fix that.
                        Vector3D[] axes = TexInfo.textureAxisFromPlane(currentPlane);
                        textureU = axes[0].Point;
                        textureV = axes[1].Point;
                    }
                    else
                    {
                        double SAxisLength = System.Math.Sqrt(System.Math.Pow((double)currentTexInfo.SAxis.X, 2) + System.Math.Pow((double)currentTexInfo.SAxis.Y, 2) + System.Math.Pow((double)currentTexInfo.SAxis.Z, 2));
                        double TAxisLength = System.Math.Sqrt(System.Math.Pow((double)currentTexInfo.TAxis.X, 2) + System.Math.Pow((double)currentTexInfo.TAxis.Y, 2) + System.Math.Pow((double)currentTexInfo.TAxis.Z, 2));
                        // In compiled maps, shorter vectors=longer textures and vice versa. This will convert their lengths back to 1. We'll use the actual scale values for length.
                        texScaleU   = (1 / SAxisLength);                       // Let's use these values using the lengths of the U and V axes we found above.
                        texScaleV   = (1 / TAxisLength);
                        textureU[0] = ((double)currentTexInfo.SAxis.X / SAxisLength);
                        textureU[1] = ((double)currentTexInfo.SAxis.Y / SAxisLength);
                        textureU[2] = ((double)currentTexInfo.SAxis.Z / SAxisLength);
                        double originShiftU = (((double)currentTexInfo.SAxis.X / SAxisLength) * origin[X] + ((double)currentTexInfo.SAxis.Y / SAxisLength) * origin[Y] + ((double)currentTexInfo.SAxis.Z / SAxisLength) * origin[Z]) / texScaleU;
                        //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextSettingsIndex'&keyword='jlca1042'"
                        textureShiftU = (double)currentTexInfo.SShift - originShiftU;
                        textureV[0]   = ((double)currentTexInfo.TAxis.X / TAxisLength);
                        textureV[1]   = ((double)currentTexInfo.TAxis.Y / TAxisLength);
                        textureV[2]   = ((double)currentTexInfo.TAxis.Z / TAxisLength);
                        double originShiftV = (((double)currentTexInfo.TAxis.X / TAxisLength) * origin[X] + ((double)currentTexInfo.TAxis.Y / TAxisLength) * origin[Y] + ((double)currentTexInfo.TAxis.Z / TAxisLength) * origin[Z]) / texScaleV;
                        //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextSettingsIndex'&keyword='jlca1042'"
                        textureShiftV = (double)currentTexInfo.TShift - originShiftV;
                    }
                    float  texRot   = 0;               // In compiled maps this is calculated into the U and V axes, so set it to 0 until I can figure out a good way to determine a better value.
                    int    flags    = 0;               // Set this to 0 until we can somehow associate faces with brushes
                    string material = "wld_lightmap";  // Since materials are a NightFire only thing, set this to a good default
                    double lgtScale = 16;              // These values are impossible to get from a compiled map since they
                    double lgtRot   = 0;               // are used by RAD for generating lightmaps, then are discarded, I believe.
                    brushSides[i] = new MAPBrushSide(currentPlane, texture, textureU, textureShiftU, textureV, textureShiftV, texRot, texScaleU, texScaleV, flags, material, lgtScale, lgtRot);
                    mapBrush.add(brushSides[i]);
                }
            }
        }

        if (!Settings.skipPlaneFlip)
        {
            if (mapBrush.hasBadSide())
            {
                // If there's a side that might be backward
                if (mapBrush.hasGoodSide())
                {
                    // If there's a side that is forward
                    mapBrush = MAPBrush.SimpleCorrectPlanes(mapBrush);
                    numSimpleCorrects++;
                    if (Settings.calcVerts)
                    {
                        // This is performed in advancedcorrect, so don't use it if that's happening
                        try
                        {
                            mapBrush = MAPBrush.CalcBrushVertices(mapBrush);
                        }
                        catch (System.NullReferenceException)
                        {
                            DecompilerThread.OnMessage(this, "WARNING: Brush vertex calculation failed on entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                        }
                    }
                }
                else
                {
                    // If no forward side exists
                    try
                    {
                        mapBrush = MAPBrush.AdvancedCorrectPlanes(mapBrush);
                        numAdvancedCorrects++;
                    }
                    catch (System.ArithmeticException)
                    {
                        DecompilerThread.OnMessage(this, "WARNING: Plane correct returned 0 triangles for entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                    }
                }
            }
            else
            {
                numGoodBrushes++;
            }
        }
        else
        {
            if (Settings.calcVerts)
            {
                // This is performed in advancedcorrect, so don't use it if that's happening
                try
                {
                    mapBrush = MAPBrush.CalcBrushVertices(mapBrush);
                }
                catch (System.NullReferenceException)
                {
                    DecompilerThread.OnMessage(this, "WARNING: Brush vertex calculation failed on entity " + mapBrush.Entnum + " brush " + mapBrush.Brushnum + "");
                }
            }
        }

        // This adds the brush we've been finding and creating to
        // the current entity as an attribute. The way I've coded
        // this whole program and the entities parser, this shouldn't
        // cause any issues at all.
        if (Settings.brushesToWorld)
        {
            mapBrush.Water = false;
            mapFile[0].Brushes.Add(mapBrush);
        }
        else
        {
            mapFile[currentEntity].Brushes.Add(mapBrush);
        }
    }