// -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);
        }
    }