Exemplo n.º 1
0
    // Create a rectangular brush from mins to maxs, with specified texture
    public static MAPBrush createBrush(Vector3D mins, Vector3D maxs, string texture)
    {
        MAPBrush newBrush = new MAPBrush(-1, 0, false);

        Vector3D[][] planes = new Vector3D[6][];
        for (int i = 0; i < 6; i++)
        {
            planes[i] = new Vector3D[3];
        }         // Six planes for a cube brush, three vertices for each plane
        double[][] textureS = new double[6][];
        for (int i2 = 0; i2 < 6; i2++)
        {
            textureS[i2] = new double[3];
        }
        double[][] textureT = new double[6][];
        for (int i3 = 0; i3 < 6; i3++)
        {
            textureT[i3] = new double[3];
        }
        // The planes and their texture scales
        // I got these from an origin brush created by Gearcraft. Don't worry where these numbers came from, they work.
        // Top
        planes[0][0]   = new Vector3D(mins.X, maxs.Y, maxs.Z);
        planes[0][1]   = new Vector3D(maxs.X, maxs.Y, maxs.Z);
        planes[0][2]   = new Vector3D(maxs.X, mins.Y, maxs.Z);
        textureS[0][0] = 1;
        textureT[0][1] = -1;
        // Bottom
        planes[1][0]   = new Vector3D(mins.X, mins.Y, mins.Z);
        planes[1][1]   = new Vector3D(maxs.X, mins.Y, mins.Z);
        planes[1][2]   = new Vector3D(maxs.X, maxs.Y, mins.Z);
        textureS[1][0] = 1;
        textureT[1][1] = -1;
        // Left
        planes[2][0]   = new Vector3D(mins.X, maxs.Y, maxs.Z);
        planes[2][1]   = new Vector3D(mins.X, mins.Y, maxs.Z);
        planes[2][2]   = new Vector3D(mins.X, mins.Y, mins.Z);
        textureS[2][1] = 1;
        textureT[2][2] = -1;
        // Right
        planes[3][0]   = new Vector3D(maxs.X, maxs.Y, mins.Z);
        planes[3][1]   = new Vector3D(maxs.X, mins.Y, mins.Z);
        planes[3][2]   = new Vector3D(maxs.X, mins.Y, maxs.Z);
        textureS[3][1] = 1;
        textureT[3][2] = -1;
        // Near
        planes[4][0]   = new Vector3D(maxs.X, maxs.Y, maxs.Z);
        planes[4][1]   = new Vector3D(mins.X, maxs.Y, maxs.Z);
        planes[4][2]   = new Vector3D(mins.X, maxs.Y, mins.Z);
        textureS[4][0] = 1;
        textureT[4][2] = -1;
        // Far
        planes[5][0]   = new Vector3D(maxs.X, mins.Y, mins.Z);
        planes[5][1]   = new Vector3D(mins.X, mins.Y, mins.Z);
        planes[5][2]   = new Vector3D(mins.X, mins.Y, maxs.Z);
        textureS[5][0] = 1;
        textureT[5][2] = -1;

        for (int j = 0; j < 6; j++)
        {
            MAPBrushSide currentEdge = new MAPBrushSide(planes[j], texture, textureS[j], 0, textureT[j], 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
            newBrush.add(currentEdge);
        }
        return(newBrush);
    }
Exemplo n.º 2
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.º 4
0
    // createFaceBrush(String, String, Vector3D, Vector3D)
    // This creates a rectangular brush. The String is assumed to be a texture for a face, and
    // the two vectors are a bounding box to create a plane with (mins-maxs).
    // The second String is the texture to apply to all other sides.
    public static MAPBrush createFaceBrush(string texture, string backTexture, Vector3D mins, Vector3D maxs, double xoff, double yoff, bool lowerUnpegged, int shiftYCieling, int shiftYFloor)
    {
        //DecompilerThread.OnMessage(this, "Creating brush for face with " + texture);
        MAPBrush newBrush = new MAPBrush(0, 0, false);

        Vector3D[][] planes = new Vector3D[6][];
        for (int i = 0; i < 6; i++)
        {
            planes[i] = new Vector3D[3];
        }         // Six planes for a cube brush, three vertices for each plane
        double[][] texS = new double[6][];
        for (int i2 = 0; i2 < 6; i2++)
        {
            texS[i2] = new double[3];
        }
        double[][] texT = new double[6][];
        for (int i3 = 0; i3 < 6; i3++)
        {
            texT[i3] = new double[3];
        }

        double   sideLengthXY = System.Math.Sqrt(System.Math.Pow(mins.X - maxs.X, 2) + System.Math.Pow(mins.Y - maxs.Y, 2));
        Vector3D diffVec1     = new Vector3D(mins.X, mins.Y, maxs.Z) - mins;
        Vector3D diffVec2     = new Vector3D(maxs.X, maxs.Y, mins.Z) - mins;
        Vector3D cross        = diffVec2 ^ diffVec1;

        cross.normalize();

        // Face
        planes[0][0] = new Vector3D(mins.X, mins.Y, maxs.Z);
        planes[0][1] = new Vector3D(maxs.X, maxs.Y, mins.Z);
        planes[0][2] = mins;
        texS[0][0]   = (-(mins.X - maxs.X)) / sideLengthXY;
        texS[0][1]   = (-(mins.Y - maxs.Y)) / sideLengthXY;
        texT[0][2]   = -1;
        // To be fair, to find these properly you ought to take the dot product of these over the length.
        // However, length is always one, and there's only two components (the third sum would turn out to be 0)
        double SShift = xoff - (texS[0][0] * mins.X) - (texS[0][1] * mins.Y);
        double TShift = yoff;

        // One sided linedefs (which this usually makes walls for) are only affected by lower unpegged. Upper is
        // always assumed unless lower is true.
        if (lowerUnpegged)
        {
            TShift += shiftYFloor;
        }
        else
        {
            TShift += shiftYCieling;
        }
        // Far
        planes[1][0] = new Vector3D(mins.X, mins.Y, maxs.Z) - (cross);
        planes[1][1] = mins - (cross);
        planes[1][2] = new Vector3D(maxs.X, maxs.Y, mins.Z) - (cross);
        texS[1][0]   = texS[0][0];
        texS[1][1]   = texS[0][1];
        texT[1][2]   = -1;
        // Top
        planes[2][0] = new Vector3D(mins.X, mins.Y, maxs.Z);
        planes[2][1] = new Vector3D(mins.X, mins.Y, maxs.Z) - (cross);
        planes[2][2] = maxs;
        texS[2][0]   = 1;
        texT[2][1]   = 1;
        // Bottom
        planes[3][0] = mins;
        planes[3][1] = new Vector3D(maxs.X, maxs.Y, mins.Z);
        planes[3][2] = new Vector3D(maxs.X, maxs.Y, mins.Z) - (cross);
        texS[3][0]   = 1;
        texT[3][1]   = 1;
        // Left
        planes[4][0] = mins;
        planes[4][1] = mins - cross;
        planes[4][2] = new Vector3D(mins.X, mins.Y, maxs.Z);
        texS[4][0]   = texS[0][1];
        texS[4][1]   = texS[0][0];
        texT[4][2]   = 1;
        // Right
        planes[5][0] = maxs;
        planes[5][1] = maxs - cross;
        planes[5][2] = new Vector3D(maxs.X, maxs.Y, mins.Z);
        texS[5][0]   = texS[0][1];
        texS[5][1]   = texS[0][0];
        texT[5][2]   = 1;

        MAPBrushSide front = new MAPBrushSide(planes[0], texture, texS[0], SShift, texT[0], TShift, 0, 1, 1, 0, "wld_lightmap", 16, 0);

        newBrush.add(front);
        for (int i = 1; i < 6; i++)
        {
            newBrush.add(new MAPBrushSide(planes[i], backTexture, texS[i], 0, texT[i], 0, 0, 1, 1, 32, "wld_lightmap", 16, 0));
        }

        return(newBrush);
    }
	// -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);
		}
	}
	// -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);
		}
	}
Exemplo n.º 7
0
	// createFaceBrush(String, String, Vector3D, Vector3D)
	// This creates a rectangular brush. The String is assumed to be a texture for a face, and
	// the two vectors are a bounding box to create a plane with (mins-maxs).
	// The second String is the texture to apply to all other sides.
	public static MAPBrush createFaceBrush(string texture, string backTexture, Vector3D mins, Vector3D maxs, double xoff, double yoff, bool lowerUnpegged, int shiftYCieling, int shiftYFloor)
	{
		//DecompilerThread.OnMessage(this, "Creating brush for face with " + texture);
		MAPBrush newBrush = new MAPBrush(0, 0, false);
		Vector3D[][] planes = new Vector3D[6][];
		for (int i = 0; i < 6; i++)
		{
			planes[i] = new Vector3D[3];
		} // Six planes for a cube brush, three vertices for each plane
		double[][] texS = new double[6][];
		for (int i2 = 0; i2 < 6; i2++)
		{
			texS[i2] = new double[3];
		}
		double[][] texT = new double[6][];
		for (int i3 = 0; i3 < 6; i3++)
		{
			texT[i3] = new double[3];
		}
		
		double sideLengthXY = System.Math.Sqrt(System.Math.Pow(mins.X - maxs.X, 2) + System.Math.Pow(mins.Y - maxs.Y, 2));
		Vector3D diffVec1 = new Vector3D(mins.X, mins.Y, maxs.Z)-mins;
		Vector3D diffVec2 = new Vector3D(maxs.X, maxs.Y, mins.Z)-mins;
		Vector3D cross = diffVec2^diffVec1;
		cross.normalize();
		
		// Face
		planes[0][0] = new Vector3D(mins.X, mins.Y, maxs.Z);
		planes[0][1] = new Vector3D(maxs.X, maxs.Y, mins.Z);
		planes[0][2] = mins;
		texS[0][0] = (- (mins.X - maxs.X)) / sideLengthXY;
		texS[0][1] = (- (mins.Y - maxs.Y)) / sideLengthXY;
		texT[0][2] = - 1;
		// To be fair, to find these properly you ought to take the dot product of these over the length.
		// However, length is always one, and there's only two components (the third sum would turn out to be 0)
		double SShift = xoff - (texS[0][0] * mins.X) - (texS[0][1] * mins.Y);
		double TShift = yoff;
		// One sided linedefs (which this usually makes walls for) are only affected by lower unpegged. Upper is
		// always assumed unless lower is true.
		if (lowerUnpegged)
		{
			TShift += shiftYFloor;
		}
		else
		{
			TShift += shiftYCieling;
		}
		// Far
		planes[1][0] = new Vector3D(mins.X, mins.Y, maxs.Z)-(cross);
		planes[1][1] = mins-(cross);
		planes[1][2] = new Vector3D(maxs.X, maxs.Y, mins.Z)-(cross);
		texS[1][0] = texS[0][0];
		texS[1][1] = texS[0][1];
		texT[1][2] = - 1;
		// Top
		planes[2][0] = new Vector3D(mins.X, mins.Y, maxs.Z);
		planes[2][1] = new Vector3D(mins.X, mins.Y, maxs.Z)-(cross);
		planes[2][2] = maxs;
		texS[2][0] = 1;
		texT[2][1] = 1;
		// Bottom
		planes[3][0] = mins;
		planes[3][1] = new Vector3D(maxs.X, maxs.Y, mins.Z);
		planes[3][2] = new Vector3D(maxs.X, maxs.Y, mins.Z)-(cross);
		texS[3][0] = 1;
		texT[3][1] = 1;
		// Left
		planes[4][0] = mins;
		planes[4][1] = mins-cross;
		planes[4][2] = new Vector3D(mins.X, mins.Y, maxs.Z);
		texS[4][0] = texS[0][1];
		texS[4][1] = texS[0][0];
		texT[4][2] = 1;
		// Right
		planes[5][0] = maxs;
		planes[5][1] = maxs-cross;
		planes[5][2] = new Vector3D(maxs.X, maxs.Y, mins.Z);
		texS[5][0] = texS[0][1];
		texS[5][1] = texS[0][0];
		texT[5][2] = 1;
		
		MAPBrushSide front = new MAPBrushSide(planes[0], texture, texS[0], SShift, texT[0], TShift, 0, 1, 1, 0, "wld_lightmap", 16, 0);
		newBrush.add(front);
		for (int i = 1; i < 6; i++)
		{
			newBrush.add(new MAPBrushSide(planes[i], backTexture, texS[i], 0, texT[i], 0, 0, 1, 1, 32, "wld_lightmap", 16, 0));
		}
		
		return newBrush;
	}
Exemplo n.º 8
0
	// Create a rectangular brush from mins to maxs, with specified texture
	public static MAPBrush createBrush(Vector3D mins, Vector3D maxs, string texture)
	{
		MAPBrush newBrush = new MAPBrush(- 1, 0, false);
		Vector3D[][] planes = new Vector3D[6][];
		for (int i = 0; i < 6; i++)
		{
			planes[i] = new Vector3D[3];
		} // Six planes for a cube brush, three vertices for each plane
		double[][] textureS = new double[6][];
		for (int i2 = 0; i2 < 6; i2++)
		{
			textureS[i2] = new double[3];
		}
		double[][] textureT = new double[6][];
		for (int i3 = 0; i3 < 6; i3++)
		{
			textureT[i3] = new double[3];
		}
		// The planes and their texture scales
		// I got these from an origin brush created by Gearcraft. Don't worry where these numbers came from, they work.
		// Top
		planes[0][0] = new Vector3D(mins.X, maxs.Y, maxs.Z);
		planes[0][1] = new Vector3D(maxs.X, maxs.Y, maxs.Z);
		planes[0][2] = new Vector3D(maxs.X, mins.Y, maxs.Z);
		textureS[0][0] = 1;
		textureT[0][1] = - 1;
		// Bottom
		planes[1][0] = new Vector3D(mins.X, mins.Y, mins.Z);
		planes[1][1] = new Vector3D(maxs.X, mins.Y, mins.Z);
		planes[1][2] = new Vector3D(maxs.X, maxs.Y, mins.Z);
		textureS[1][0] = 1;
		textureT[1][1] = - 1;
		// Left
		planes[2][0] = new Vector3D(mins.X, maxs.Y, maxs.Z);
		planes[2][1] = new Vector3D(mins.X, mins.Y, maxs.Z);
		planes[2][2] = new Vector3D(mins.X, mins.Y, mins.Z);
		textureS[2][1] = 1;
		textureT[2][2] = - 1;
		// Right
		planes[3][0] = new Vector3D(maxs.X, maxs.Y, mins.Z);
		planes[3][1] = new Vector3D(maxs.X, mins.Y, mins.Z);
		planes[3][2] = new Vector3D(maxs.X, mins.Y, maxs.Z);
		textureS[3][1] = 1;
		textureT[3][2] = - 1;
		// Near
		planes[4][0] = new Vector3D(maxs.X, maxs.Y, maxs.Z);
		planes[4][1] = new Vector3D(mins.X, maxs.Y, maxs.Z);
		planes[4][2] = new Vector3D(mins.X, maxs.Y, mins.Z);
		textureS[4][0] = 1;
		textureT[4][2] = - 1;
		// Far
		planes[5][0] = new Vector3D(maxs.X, mins.Y, mins.Z);
		planes[5][1] = new Vector3D(mins.X, mins.Y, mins.Z);
		planes[5][2] = new Vector3D(mins.X, mins.Y, maxs.Z);
		textureS[5][0] = 1;
		textureT[5][2] = - 1;
		
		for (int j = 0; j < 6; j++)
		{
			MAPBrushSide currentEdge = new MAPBrushSide(planes[j], texture, textureS[j], 0, textureT[j], 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			newBrush.add(currentEdge);
		}
		return newBrush;
	}
Exemplo n.º 9
0
	// METHODS
	
	// +decompile()
	// Attempts to convert a map in a Doom WAD into a usable .MAP file. This has many
	// challenges, not the least of which is the fact that the Doom engine didn't use
	// brushes (at least, not in any sane way).
	public virtual Entities decompile()
	{
		DecompilerThread.OnMessage(this, "Decompiling...");
		DecompilerThread.OnMessage(this, doomMap.MapName);
		
		mapFile = new Entities();
		Entity world = new Entity("worldspawn");
		mapFile.Add(world);
		
		string[] lowerWallTextures = new string[doomMap.Sidedefs.Count];
		string[] midWallTextures = new string[doomMap.Sidedefs.Count];
		string[] higherWallTextures = new string[doomMap.Sidedefs.Count];
		
		short[] sectorTag = new short[doomMap.Sectors.Count];
		string playerStartOrigin = "";
		
		// Since Doom relied on sectors to define a cieling and floor height, and nothing else,
		// need to find the minimum and maximum used Z values. This is because the Doom engine
		// is only a pseudo-3D engine. For all it cares, the cieling and floor extend to their
		// respective infinities. For a GC/Hammer map, however, this cannot be the case.
		int ZMin = 32767; // Even though the values in the map will never exceed these, use ints here to avoid
		int ZMax = - 32768; // overflows, in case the map DOES go within 32 units of these values.
		for (int i = 0; i < doomMap.Sectors.Count; i++)
		{
			DSector currentSector = doomMap.Sectors[i];
			sectorTag[i] = currentSector.Tag;
			if (currentSector.FloorHeight < ZMin + 32)
			{
				ZMin = currentSector.FloorHeight - 32; // Can't use the actual value, because that IS the floor
			}
			else
			{
				if (currentSector.CielingHeight > ZMax - 32)
				{
					ZMax = currentSector.CielingHeight + 32; // or the cieling. Subtract or add a sane value to it.
				}
			}
		}
		
		// I need to analyze the binary tree and get more information, particularly the
		// parent nodes of each subsector and node, as well as whether it's the right or
		// left child of that node. These are extremely important, as the parent defines
		// boundaries for the children, as well as inheriting further boundaries from its
		// parents. These boundaries are invaluable for forming brushes.
		int[] nodeparents = new int[doomMap.Nodes.Count];
		bool[] nodeIsLeft = new bool[doomMap.Nodes.Count];
		
		for (int i = 0; i < doomMap.Nodes.Count; i++)
		{
			nodeparents[i] = - 1; // There should only be one node left with -1 as a parent. This SHOULD be the root.
			for (int j = 0; j < doomMap.Nodes.Count; j++)
			{
				if (doomMap.Nodes[j].Child1 == i)
				{
					nodeparents[i] = j;
					break;
				}
				else
				{
					if (doomMap.Nodes[j].Child2 == i)
					{
						nodeparents[i] = j;
						nodeIsLeft[i] = true;
						break;
					}
				}
			}
		}
		
		// Keep a list of what subsectors belong to which sector
		int[] subsectorSectors = new int[doomMap.SubSectors.Count];
		// Keep a list of what sidedefs belong to what subsector as well
		int[][] subsectorSidedefs = new int[doomMap.SubSectors.Count][];
		
		short[][] sideDefShifts = new short[2][];
		for (int i2 = 0; i2 < 2; i2++)
		{
			sideDefShifts[i2] = new short[doomMap.Sidedefs.Count];
		}
		
		// Figure out what sector each subsector belongs to, and what node is its parent.
		// Depending on sector "tags" this will help greatly in creation of brushbased entities,
		// and also helps in finding subsector floor and cieling heights.
		int[] ssparents = new int[doomMap.SubSectors.Count];
		bool[] ssIsLeft = new bool[doomMap.SubSectors.Count];
		for (int i = 0; i < doomMap.SubSectors.Count; i++)
		{
			//DecompilerThread.OnMessage(this, "Populating texture lists for subsector " + i);
			// First, find the subsector's parent and whether it is the left or right child.
			ssparents[i] = - 1; // No subsector should have a -1 in here
			for (int j = 0; j < doomMap.Nodes.Count; j++)
			{
				// When a node references a subsector, it is not referenced by negative
				// index, as future BSP versions do. The bits 0-14 ARE the index, and
				// bit 15 (which is the sign bit in two's compliment math) determines
				// whether or not it is a node or subsector. Therefore, we need to add
				// 2^15 to the number to produce the actual index.
				if (doomMap.Nodes[j].Child1 + 32768 == i)
				{
					ssparents[i] = j;
					break;
				}
				else
				{
					if (doomMap.Nodes[j].Child2 + 32768 == i)
					{
						ssparents[i] = j;
						ssIsLeft[i] = true;
						break;
					}
				}
			}
			
			// Second, figure out what sector a subsector belongs to, and the type of sector it is.
			subsectorSectors[i] = - 1;
			Edge currentsubsector = doomMap.SubSectors[i];
			subsectorSidedefs[i] = new int[currentsubsector.NumSegs];
			for (int j = 0; j < currentsubsector.NumSegs; j++)
			{
				// For each segment the subsector references
				DSegment currentsegment = doomMap.Segments[currentsubsector.FirstSeg + j];
				DLinedef currentlinedef = doomMap.Linedefs[currentsegment.Linedef];
				int currentsidedefIndex;
				int othersideIndex;
				if (currentsegment.Direction == 0)
				{
					currentsidedefIndex = currentlinedef.Right;
					othersideIndex = currentlinedef.Left;
				}
				else
				{
					currentsidedefIndex = currentlinedef.Left;
					othersideIndex = currentlinedef.Right;
				}
				subsectorSidedefs[i][j] = currentsidedefIndex;
				DSidedef currentSidedef = doomMap.Sidedefs[currentsidedefIndex];
				if (currentlinedef.OneSided)
				{
					// A one-sided linedef should always be like this
					midWallTextures[currentsidedefIndex] = doomMap.WadName + "/" + currentSidedef.MidTexture;
					higherWallTextures[currentsidedefIndex] = "special/nodraw";
					lowerWallTextures[currentsidedefIndex] = "special/nodraw";
					sideDefShifts[X][currentsidedefIndex] = currentSidedef.OffsetX;
					sideDefShifts[Y][currentsidedefIndex] = currentSidedef.OffsetY;
				}
				else
				{
					// I don't really get why I need to apply these textures to the other side. But if it works I won't argue...
					if (!currentSidedef.MidTexture.Equals("-"))
					{
						midWallTextures[othersideIndex] = doomMap.WadName + "/" + currentSidedef.MidTexture;
					}
					else
					{
						midWallTextures[othersideIndex] = "special/nodraw";
					}
					if (!currentSidedef.HighTexture.Equals("-"))
					{
						higherWallTextures[othersideIndex] = doomMap.WadName + "/" + currentSidedef.HighTexture;
					}
					else
					{
						higherWallTextures[othersideIndex] = "special/nodraw";
					}
					if (!currentSidedef.LowTexture.Equals("-"))
					{
						lowerWallTextures[othersideIndex] = doomMap.WadName + "/" + currentSidedef.LowTexture;
					}
					else
					{
						lowerWallTextures[othersideIndex] = "special/nodraw";
					}
					sideDefShifts[X][othersideIndex] = currentSidedef.OffsetX;
					sideDefShifts[Y][othersideIndex] = currentSidedef.OffsetY;
				}
				// Sometimes a subsector seems to belong to more than one sector. Only the reference in the first seg is true.
				if (j == 0)
				{
					subsectorSectors[i] = currentSidedef.Sector;
				}
			}
		}
		bool[] linedefFlagsDealtWith = new bool[doomMap.Linedefs.Count];
		bool[] linedefSpecialsDealtWith = new bool[doomMap.Linedefs.Count];
		
		MAPBrush[][] sectorFloorBrushes = new MAPBrush[doomMap.Sectors.Count][];
		for (int i3 = 0; i3 < doomMap.Sectors.Count; i3++)
		{
			sectorFloorBrushes[i3] = new MAPBrush[0];
		}
		MAPBrush[][] sectorCielingBrushes = new MAPBrush[doomMap.Sectors.Count][];
		for (int i4 = 0; i4 < doomMap.Sectors.Count; i4++)
		{
			sectorCielingBrushes[i4] = new MAPBrush[0];
		}
		
		// For one-sided linedefs referenced by more than one subsector
		bool[] outsideBrushAlreadyCreated = new bool[doomMap.Linedefs.Count];
		
		int onePercent = (int)((doomMap.SubSectors.Count)/100);
		if(onePercent < 1) {
			onePercent = 1;
		}
		for (int i = 0; i < doomMap.SubSectors.Count; i++)
		{
			//DecompilerThread.OnMessage(this, "Creating brushes for subsector " + i);
			
			Edge currentsubsector = doomMap.SubSectors[i];
			
			// Third, create a few brushes out of the geometry.
			MAPBrush cielingBrush = new MAPBrush(numBrshs++, 0, false);
			MAPBrush floorBrush = new MAPBrush(numBrshs++, 0, false);
			MAPBrush midBrush = new MAPBrush(numBrshs++, 0, false);
			MAPBrush sectorTriggerBrush = new MAPBrush(numBrshs++, 0, false);
			DSector currentSector = doomMap.Sectors[subsectorSectors[i]];
			
			Vector3D[] roofPlane = new Vector3D[3];
			double[] roofTexS = new double[3];
			double[] roofTexT = new double[3];
			roofPlane[0] = new Vector3D(0, Settings.planePointCoef, ZMax);
			roofPlane[1] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, ZMax);
			roofPlane[2] = new Vector3D(Settings.planePointCoef, 0, ZMax);
			roofTexS[0] = 1;
			roofTexT[1] = - 1;
			
			Vector3D[] cileingPlane = new Vector3D[3];
			double[] cileingTexS = new double[3];
			double[] cileingTexT = new double[3];
			cileingPlane[0] = new Vector3D(0, 0, currentSector.CielingHeight);
			cileingPlane[1] = new Vector3D(Settings.planePointCoef, 0, currentSector.CielingHeight);
			cileingPlane[2] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.CielingHeight);
			cileingTexS[0] = 1;
			cileingTexT[1] = - 1;
			
			Vector3D[] floorPlane = new Vector3D[3];
			double[] floorTexS = new double[3];
			double[] floorTexT = new double[3];
			floorPlane[0] = new Vector3D(0, Settings.planePointCoef, currentSector.FloorHeight);
			floorPlane[1] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.FloorHeight);
			floorPlane[2] = new Vector3D(Settings.planePointCoef, 0, currentSector.FloorHeight);
			floorTexS[0] = 1;
			floorTexT[1] = - 1;
			
			Vector3D[] foundationPlane = new Vector3D[3];
			double[] foundationTexS = new double[3];
			double[] foundationTexT = new double[3];
			foundationPlane[0] = new Vector3D(0, 0, ZMin);
			foundationPlane[1] = new Vector3D(Settings.planePointCoef, 0, ZMin);
			foundationPlane[2] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, ZMin);
			foundationTexS[0] = 1;
			foundationTexT[1] = - 1;
			
			Vector3D[] topPlane = new Vector3D[3];
			double[] topTexS = new double[3];
			double[] topTexT = new double[3];
			topPlane[0] = new Vector3D(0, Settings.planePointCoef, currentSector.CielingHeight);
			topPlane[1] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.CielingHeight);
			topPlane[2] = new Vector3D(Settings.planePointCoef, 0, currentSector.CielingHeight);
			topTexS[0] = 1;
			topTexT[1] = - 1;
			
			Vector3D[] bottomPlane = new Vector3D[3];
			double[] bottomTexS = new double[3];
			double[] bottomTexT = new double[3];
			bottomPlane[0] = new Vector3D(0, 0, currentSector.FloorHeight);
			bottomPlane[1] = new Vector3D(Settings.planePointCoef, 0, currentSector.FloorHeight);
			bottomPlane[2] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.FloorHeight);
			bottomTexS[0] = 1;
			bottomTexT[1] = - 1;
			
			int nextNode = ssparents[i];
			bool leftSide = ssIsLeft[i];
			
			for (int j = 0; j < currentsubsector.NumSegs; j++)
			{
				// Iterate through the sidedefs defined by segments of this subsector
				DSegment currentseg = doomMap.Segments[currentsubsector.FirstSeg + j];
				Vector3D start = doomMap.Vertices[currentseg.StartVertex].Vector;
				Vector3D end = doomMap.Vertices[currentseg.EndVertex].Vector;
				DLinedef currentLinedef = doomMap.Linedefs[(int) currentseg.Linedef];
				
				Vector3D[] plane = new Vector3D[3];
				double[] texS = new double[3];
				double[] texT = new double[3];
				plane[0] = new Vector3D(start.X, start.Y, ZMin);
				plane[1] = new Vector3D(end.X, end.Y, ZMin);
				plane[2] = new Vector3D(end.X, end.Y, ZMax);
				
				Vector3D linestart = new Vector3D(doomMap.Vertices[currentLinedef.Start].Vector.X, doomMap.Vertices[currentLinedef.Start].Vector.Y, ZMin);
				Vector3D lineend = new Vector3D(doomMap.Vertices[currentLinedef.End].Vector.X, doomMap.Vertices[currentLinedef.End].Vector.Y, ZMax);
				
				double sideLength = System.Math.Sqrt(System.Math.Pow(start.X - end.X, 2) + System.Math.Pow(start.Y - end.Y, 2));
				
				bool upperUnpegged = !((currentLinedef.Flags[0] & ((sbyte) 1 << 3)) == 0);
				bool lowerUnpegged = !((currentLinedef.Flags[0] & ((sbyte) 1 << 4)) == 0);
				
				texS[0] = (start.X - end.X) / sideLength;
				texS[1] = (start.Y - end.Y) / sideLength;
				texS[2] = 0;
				texT[0] = 0;
				texT[1] = 0;
				texT[2] = - 1;
				
				double SShift = sideDefShifts[X][subsectorSidedefs[i][j]] - (texS[0] * end.X) - (texS[1] * end.Y);
				double lowTShift = 0;
				double highTShift = 0;
				if (!currentLinedef.OneSided)
				{
					DSector otherSideSector;
					if (doomMap.Sectors[doomMap.Sidedefs[currentLinedef.Left].Sector] == currentSector)
					{
						otherSideSector = doomMap.Sectors[doomMap.Sidedefs[currentLinedef.Right].Sector];
					}
					else
					{
						otherSideSector = doomMap.Sectors[doomMap.Sidedefs[currentLinedef.Left].Sector];
					}
					if (lowerUnpegged)
					{
						lowTShift = otherSideSector.CielingHeight;
					}
					else
					{
						lowTShift = currentSector.FloorHeight;
					}
					if (upperUnpegged)
					{
						highTShift = otherSideSector.CielingHeight;
					}
					else
					{
						highTShift = currentSector.CielingHeight;
					}
					lowTShift += sideDefShifts[Y][subsectorSidedefs[i][j]];
					highTShift += sideDefShifts[Y][subsectorSidedefs[i][j]];
				}
				MAPBrushSide low = new MAPBrushSide(plane, lowerWallTextures[subsectorSidedefs[i][j]], texS, SShift, texT, lowTShift, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide high = new MAPBrushSide(plane, higherWallTextures[subsectorSidedefs[i][j]], texS, SShift, texT, highTShift, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide mid;
				MAPBrushSide damage = new MAPBrushSide(plane, "special/trigger", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				
				if (currentLinedef.OneSided)
				{
					if (!outsideBrushAlreadyCreated[currentseg.Linedef])
					{
						outsideBrushAlreadyCreated[currentseg.Linedef] = true;
						double highestCieling = currentSector.CielingHeight;
						double lowestFloor = currentSector.FloorHeight;
						if (currentSector.Tag != 0)
						{
							double temp = getHighestNeighborCielingHeight(subsectorSectors[i]);
							if (temp > highestCieling)
							{
								highestCieling = temp;
							}
							temp = getLowestNeighborFloorHeight(subsectorSectors[i]);
							if (temp < lowestFloor)
							{
								lowestFloor = temp;
							}
						}
						MAPBrush outsideBrush = null;
						if (lowestFloor <= highestCieling)
						{
							outsideBrush = MAPBrush.createFaceBrush(midWallTextures[subsectorSidedefs[i][j]], "special/nodraw", new Vector3D(linestart.X, linestart.Y, ZMin), new Vector3D(lineend.X, lineend.Y, ZMax), sideDefShifts[X][subsectorSidedefs[i][j]], sideDefShifts[Y][subsectorSidedefs[i][j]], lowerUnpegged, currentSector.CielingHeight, currentSector.FloorHeight);
						}
						else
						{
							outsideBrush = MAPBrush.createFaceBrush(midWallTextures[subsectorSidedefs[i][j]], "special/nodraw", new Vector3D(linestart.X, linestart.Y, lowestFloor), new Vector3D(lineend.X, lineend.Y, highestCieling), sideDefShifts[X][subsectorSidedefs[i][j]], sideDefShifts[Y][subsectorSidedefs[i][j]], lowerUnpegged, currentSector.CielingHeight, currentSector.FloorHeight);
						}
						world.Brushes.Add(outsideBrush);
					}
					mid = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				}
				else
				{
					double midTShift = sideDefShifts[Y][subsectorSidedefs[i][j]];
					if (lowerUnpegged)
					{
						midTShift += currentSector.FloorHeight;
					}
					else
					{
						midTShift += currentSector.CielingHeight;
					}
					mid = new MAPBrushSide(plane, midWallTextures[subsectorSidedefs[i][j]], texS, SShift, texT, midTShift, 0, 1, 1, 0, "wld_masked", 16, 0);
					if (!linedefFlagsDealtWith[currentseg.Linedef])
					{
						linedefFlagsDealtWith[currentseg.Linedef] = true;
						if (!((currentLinedef.Flags[0] & ((sbyte) 1 << 0)) == 0))
						{
							// Flag 0x0001 indicates "solid" but doesn't block bullets. It is assumed for all one-sided.
							MAPBrush solidBrush = MAPBrush.createFaceBrush("special/clip", "special/clip", linestart, lineend, 0, 0, false, 0, 0);
							world.Brushes.Add(solidBrush);
						}
						else
						{
							if (!((currentLinedef.Flags[0] & ((sbyte) 1 << 1)) == 0))
							{
								// Flag 0x0002 indicates "monster clip".
								MAPBrush solidBrush = MAPBrush.createFaceBrush("special/enemyclip", "special/enemyclip", linestart, lineend, 0, 0, false, 0, 0);
								world.Brushes.Add(solidBrush);
							}
						}
					}
					DSidedef otherside = doomMap.Sidedefs[currentLinedef.Left];
					if (currentLinedef.Action != 0 && !linedefSpecialsDealtWith[currentseg.Linedef])
					{
						linedefSpecialsDealtWith[currentseg.Linedef] = true;
						Entity trigger = null;
						MAPBrush triggerBrush = MAPBrush.createFaceBrush("special/trigger", "special/trigger", linestart, lineend, 0, 0, false, 0, 0);
						if (doomMap.Version == mapType.TYPE_HEXEN)
						{
							bool[] bitset = new bool[16];
							for (int k = 0; k < 8; k++)
							{
								bitset[k] = !((currentLinedef.Flags[0] & ((sbyte) k << 1)) == 0);
							}
							for (int k = 0; k < 8; k++)
							{
								bitset[k + 8] = !((currentLinedef.Flags[1] & ((sbyte) k << 1)) == 0);
							}
							if (bitset[10] && bitset[11] && !bitset[12])
							{
								// Triggered when "Used" by player
								trigger = new Entity("func_button");
								trigger["spawnflags"] = "1";
								if (bitset[9])
								{
									trigger["wait"] = "1";
								}
								else
								{
									trigger["wait"] = "-1";
								}
							}
							else
							{
								if (bitset[9])
								{
									// Can be activated more than once
									trigger = new Entity("trigger_multiple");
									trigger["wait"] = "1";
								}
								else
								{
									trigger = new Entity("trigger_once");
								}
							}
							switch (currentLinedef.Action)
							{
								case 21: 
								// Floor lower to lowest surrounding floor
								case 22:  // Floor lower to next lowest surrounding floor
									if (currentLinedef.Arguments[0] != 0)
									{
										trigger["target"] = "sector" + currentLinedef.Arguments[0] + "lowerfloor";
									}
									else
									{
										trigger["target"] = "sectornum" + otherside.Sector + "lowerfloor";
									}
									break;
								case 24: 
								// Floor raise to highest surrounding floor
								case 25:  // Floor raise to next highest surrounding floor
									if (currentLinedef.Arguments[0] != 0)
									{
										trigger["target"] = "sector" + currentLinedef.Arguments[0] + "raisefloor";
									}
									else
									{
										trigger["target"] = "sectornum" + otherside.Sector + "raisefloor";
									}
									break;
								case 70:  // Teleport
									trigger = new Entity("trigger_teleport");
									if (currentLinedef.Arguments[0] != 0)
									{
										trigger["target"] = "teledest" + currentLinedef.Arguments[0];
									}
									else
									{
										trigger["target"] = "sector" + currentLinedef.Tag + "teledest";
									}
									break;
								case 80:  // Exec script
									// This is a toughie. I can't write a script-to-entity converter.
									trigger["target"] = "script" + currentLinedef.Arguments[0];
									trigger["arg0"] = "" + currentLinedef.Arguments[2];
									trigger["arg1"] = "" + currentLinedef.Arguments[3];
									trigger["arg2"] = "" + currentLinedef.Arguments[4];
									break;
								case 181:  // PLANE_ALIGN
									trigger = null;
									if (!leftSide)
									{
										DSidedef getsector = doomMap.Sidedefs[currentLinedef.Left];
										DSector copyheight = doomMap.Sectors[getsector.Sector];
										short newHeight = copyheight.FloorHeight;
										//floorPlane[0]=new Vector3D(0, Settings.getPlanePointCoef(), 2000);
										//floorPlane[1]=new Vector3D(Settings.getPlanePointCoef(), Settings.getPlanePointCoef(), currentSector.getFloorHeight());
										//floorPlane[2]=new Vector3D(Settings.getPlanePointCoef(), 0, currentSector.getFloorHeight());
									}
									else
									{
										linedefSpecialsDealtWith[currentseg.Linedef] = false;
									}
									break;
								default: 
									trigger = null;
									break;
							}
						}
						else
						{
							switch (currentLinedef.Action)
							{
								
								case 1: 
								// Use Door. open, wait, close
								case 31:  // Use Door. Open, stay.
									trigger = new Entity("func_button");
									trigger["wait"] = "1";
									if (currentLinedef.Action == 31)
									{
										trigger["wait"] = "-1";
									}
									trigger["spawnflags"] = "1";
									if (doomMap.Sectors[otherside.Sector].Tag == 0)
									{
										trigger["target"] = "sectornum" + otherside.Sector + "door";
										if (currentLinedef.Action == 1)
										{
											sectorTag[otherside.Sector] = - 1;
										}
										if (currentLinedef.Action == 31)
										{
											sectorTag[otherside.Sector] = - 2;
										}
									}
									else
									{
										trigger["target"] = "sector" + doomMap.Sectors[otherside.Sector].Tag + "door";
									}
									break;
								
								case 36: 
								// Floor lower to 8 above next lowest neighboring sector
								case 38:  // Floor lower to next lowest neighboring sector
									trigger = new Entity("trigger_once");
									trigger["target"] = "sector" + currentLinedef.Tag + "lowerfloor";
									break;
								
								case 62:  // Floor lower to next lowest neighboring sector, wait 4s, goes back up
									trigger = new Entity("func_button");
									trigger["target"] = "sector" + currentLinedef.Tag + "vator";
									trigger["wait"] = "1";
									trigger["spawnflags"] = "1";
									break;
								
								case 63: 
								// Door with button, retriggerable
								case 103:  // Push button, one-time door open stay open
									trigger = new Entity("func_button");
									trigger["target"] = "sector" + currentLinedef.Tag + "door";
									trigger["wait"] = "1";
									if (currentLinedef.Action == 103)
									{
										trigger["wait"] = "-1";
									}
									trigger["spawnflags"] = "1";
									break;
								
								case 88:  // Walkover retriggerable elevator trigger
									trigger = new Entity("trigger_multiple");
									trigger["target"] = "sector" + currentLinedef.Tag + "vator";
									break;
								
								case 97:  // Walkover retriggerable Teleport
									trigger = new Entity("trigger_teleport");
									trigger["target"] = "sector" + currentLinedef.Tag + "teledest";
									break;
								
								case 109:  // Walkover one-time door open stay open
									trigger = new Entity("trigger_once");
									trigger["target"] = "sector" + currentLinedef.Tag + "door";
									break;
							}
						}
						if (trigger != null)
						{
							trigger.Brushes.Add(triggerBrush);
							mapFile.Add(trigger);
						}
					}
				}
				
				cielingBrush.add(high);
				midBrush.add(mid);
				floorBrush.add(low);
				sectorTriggerBrush.add(damage);
			}
			
			MAPBrushSide roof = new MAPBrushSide(roofPlane, "special/nodraw", roofTexS, 0, roofTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide cieling = new MAPBrushSide(cileingPlane, doomMap.WadName + "/" + currentSector.CielingTexture, cileingTexS, 0, cileingTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide floor = new MAPBrushSide(floorPlane, doomMap.WadName + "/" + currentSector.FloorTexture, floorTexS, 0, floorTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide foundation = new MAPBrushSide(foundationPlane, "special/nodraw", foundationTexS, 0, foundationTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide top = new MAPBrushSide(topPlane, "special/nodraw", topTexS, 0, topTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide bottom = new MAPBrushSide(bottomPlane, "special/nodraw", bottomTexS, 0, bottomTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide invertedFloor = new MAPBrushSide(Plane.flip(floorPlane), "special/trigger", floorTexS, 0, floorTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide damageTop = new MAPBrushSide(new Vector3D[]{floorPlane[0]+new Vector3D(0, 0, 1), floorPlane[1]+new Vector3D(0, 0, 1), floorPlane[2]+new Vector3D(0, 0, 1)}, "special/trigger", floorTexS, 0, floorTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			sectorTriggerBrush.add(damageTop);
			sectorTriggerBrush.add(invertedFloor);
			
			midBrush.add(top);
			midBrush.add(bottom);
			
			cielingBrush.add(cieling);
			cielingBrush.add(roof);
			
			floorBrush.add(floor);
			floorBrush.add(foundation);
			
			// Now need to add the data from node subdivisions. Neither segments nor nodes
			// will completely define a usable brush, but both of them together will.
			do 
			{
				DNode currentNode = doomMap.Nodes[nextNode];
				Vector3D start;
				Vector3D end;
				if (leftSide)
				{
					start = currentNode.VecHead+currentNode.VecTail;
					end = currentNode.VecHead;
				}
				else
				{
					start = currentNode.VecHead;
					end = currentNode.VecHead+currentNode.VecTail;
				}
				
				Vector3D[] plane = new Vector3D[3];
				double[] texS = new double[3];
				double[] texT = new double[3];
				// This is somehow always correct. And I'm okay with that.
				plane[0] = new Vector3D(start.X, start.Y, ZMin);
				plane[1] = new Vector3D(end.X, end.Y, ZMin);
				plane[2] = new Vector3D(start.X, start.Y, ZMax);
				
				double sideLength = System.Math.Sqrt(System.Math.Pow(start.X - end.X, 2) + System.Math.Pow(start.Y - end.Y, 2));
				
				texS[0] = (start.X - end.X) / sideLength;
				texS[1] = (start.Y - end.Y) / sideLength;
				texS[2] = 0;
				texT[0] = 0;
				texT[1] = 0;
				texT[2] = 1;
				MAPBrushSide low = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide high = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide mid = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide damage = new MAPBrushSide(plane, "special/trigger", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				
				cielingBrush.add(high);
				midBrush.add(mid);
				floorBrush.add(low);
				sectorTriggerBrush.add(damage);
				
				leftSide = nodeIsLeft[nextNode];
				nextNode = nodeparents[nextNode];
			}
			while (nextNode != - 1);
			// 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.
			int[] badSides = new int[0];
			if (!Settings.dontCull)
			{
				badSides = MAPBrush.findUnusedPlanes(cielingBrush);
				// 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 (cielingBrush.NumSides - badSides.Length < 4)
				{
					DecompilerThread.OnMessage(this, "WARNING: Plane cull returned less than 4 sides for subsector " + i);
					badSides = new int[0];
				}
				else
				{
					for (int j = badSides.Length - 1; j > - 1; j--)
					{
						cielingBrush.delete(badSides[j]);
						floorBrush.delete(badSides[j]);
					}
				}
			}
			
			MAPBrush[] newFloorList = new MAPBrush[sectorFloorBrushes[subsectorSectors[i]].Length + 1];
			MAPBrush[] newCielingList = new MAPBrush[sectorCielingBrushes[subsectorSectors[i]].Length + 1];
			for (int j = 0; j < sectorFloorBrushes[subsectorSectors[i]].Length; j++)
			{
				newFloorList[j] = sectorFloorBrushes[subsectorSectors[i]][j];
				newCielingList[j] = sectorCielingBrushes[subsectorSectors[i]][j];
			}
			newFloorList[newFloorList.Length - 1] = floorBrush;
			newCielingList[newCielingList.Length - 1] = cielingBrush;
			sectorFloorBrushes[subsectorSectors[i]] = newFloorList;
			sectorCielingBrushes[subsectorSectors[i]] = newCielingList;
			
			bool containsMiddle = false;
			for (int j = 0; j < midBrush.NumSides; j++)
			{
				if (!midBrush[j].Texture.ToUpper().Equals("special/nodraw".ToUpper()))
				{
					containsMiddle = true;
					break;
				}
			}
			if (containsMiddle && currentSector.CielingHeight > currentSector.FloorHeight)
			{
				Entity middleEnt = new Entity("func_illusionary");
				if (midBrush.NumSides - badSides.Length >= 4)
				{
					for (int j = badSides.Length - 1; j > - 1; j--)
					{
						midBrush.delete(badSides[j]);
					}
				}
				
				middleEnt.Brushes.Add(midBrush);
				mapFile.Add(middleEnt);
			}
			Entity hurtMe = new Entity("trigger_hurt");
			Entity triggerSecret = new Entity("trigger_bondsecret");
			switch (currentSector.Type)
			{
				case 4: 
				// 20% damage/s with lighting properties
				case 11: 
				// 20% damage/s
				case 16:  // 20% damage/s plus end level when player is almost dead
					hurtMe["dmg"] = "40";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					hurtMe.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(hurtMe);
					break;
				case 5:  // 10% damage/s
					hurtMe["dmg"] = "20";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					hurtMe.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(hurtMe);
					break;
				case 7:  // 5% damage/s
					hurtMe["dmg"] = "10";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					hurtMe.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(hurtMe);
					break;
				case 9:  // "secret"
					triggerSecret["Sound"] = "common/mission_success.wav";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					triggerSecret.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(triggerSecret);
					break;
				case 10:  // 30 seconds after level start, "close" like a door
					if (currentSector.Tag == 0)
					{
						sectorTag[subsectorSectors[i]] = - 3;
					}
					break;
				case 14:  // 300 seconds after level start, "open" like a door
					if (currentSector.Tag == 0)
					{
						sectorTag[subsectorSectors[i]] = - 4;
					}
					break;
				default: 
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					if ((currentSector.Type & 96) != 0)
					{
						// "Generalized" pain sectors
						if ((currentSector.Type & 96) == 32)
						{
							hurtMe["dmg"] = "10";
						}
						else
						{
							if ((currentSector.Type & 96) == 64)
							{
								hurtMe["dmg"] = "20";
							}
							else
							{
								if ((currentSector.Type & 96) == 96)
								{
									hurtMe["dmg"] = "40";
								}
							}
						}
						hurtMe.Brushes.Add(sectorTriggerBrush);
						mapFile.Add(hurtMe);
					}
					if ((currentSector.Type & 128) == 128)
					{
						// "Generalized" secret trigger
						triggerSecret["Sound"] = "common/mission_success.wav";
						triggerSecret.Brushes.Add(sectorTriggerBrush);
						mapFile.Add(triggerSecret);
					}
					break;
			}
			if((i+1)%onePercent == 0) {
				parent.OnProgress(this, (i+1)/(double)(doomMap.SubSectors.Count));
			}
			//Settings.setProgress(jobnum, i + 1, doomMap.SubSectors.Count, "Decompiling...");
		}
		
		// Add the brushes to the map, as world by default, or entities if they are supported.
		for (int i = 0; i < doomMap.Sectors.Count; i++)
		{
			bool[] floorsUsed = new bool[sectorFloorBrushes[i].Length];
			bool[] cielingsUsed = new bool[sectorCielingBrushes[i].Length];
			if (sectorTag[i] == 0)
			{
				for (int j = 0; j < sectorFloorBrushes[i].Length; j++)
				{
					world.Brushes.Add(sectorFloorBrushes[i][j]);
					floorsUsed[j] = true;
					world.Brushes.Add(sectorCielingBrushes[i][j]);
					cielingsUsed[j] = true;
				}
			}
			else
			{
				if (sectorTag[i] == - 1 || sectorTag[i] == - 2 || sectorTag[i] == - 3 || sectorTag[i] == - 4)
				{
					// I'm using this to mean a door with no tag number
					Entity newDoor = new Entity("func_door");
					newDoor["speed"] = "60";
					newDoor["angles"] = "270 0 0";
					newDoor["spawnflags"] = "256";
					newDoor["targetname"] = "sectornum" + i + "door";
					if (sectorTag[i] == - 1)
					{
						newDoor["wait"] = "4";
					}
					else
					{
						if (sectorTag[i] == - 2)
						{
							newDoor["wait"] = "-1";
						}
					}
					if (sectorTag[i] == - 3)
					{
						Entity triggerAuto = new Entity("trigger_auto");
						triggerAuto["target"] = "sectornum" + i + "door_mm";
						Entity multiManager = new Entity("multi_manager");
						multiManager["sectornum" + i + "door"] = "30";
						mapFile.Add(triggerAuto);
						mapFile.Add(multiManager);
					}
					if (sectorTag[i] == - 4)
					{
						newDoor["Spawnflags"] = "1";
						Entity triggerAuto = new Entity("trigger_auto");
						triggerAuto["target"] = "sectornum" + i + "door_mm";
						Entity multiManager = new Entity("multi_manager");
						multiManager["sectornum" + i + "door"] = "300";
						mapFile.Add(triggerAuto);
						mapFile.Add(multiManager);
					}
					int lowestNeighborCielingHeight = getLowestNeighborCielingHeight(i);
					int lip = ZMax - lowestNeighborCielingHeight + 4;
					newDoor["lip"] = "" + lip;
					for (int j = 0; j < sectorFloorBrushes[i].Length; j++)
					{
						cielingsUsed[j] = true;
						newDoor.Brushes.Add(sectorCielingBrushes[i][j]);
					}
					mapFile.Add(newDoor);
				}
				else
				{
					for (int j = 0; j < doomMap.Linedefs.Count; j++)
					{
						DLinedef currentLinedef = doomMap.Linedefs[j];
						int linedefTriggerType = currentLinedef.Action;
						if (doomMap.Version == mapType.TYPE_HEXEN)
						{
							switch (linedefTriggerType)
							{
								
								case 21: 
								// Floor lower to lowest neighbor
								case 22:  // Floor lower to nearest lower neighbor
									// I don't know where retriggerability is determined, or whether or not it goes back up.
									if (currentLinedef.Arguments[0] == sectorTag[i])
									{
										Entity newFloor = new Entity("func_door");
										newFloor["angles"] = "90 0 0";
										newFloor["wait"] = "-1";
										newFloor["speed"] = "" + currentLinedef.Arguments[1];
										if (currentLinedef.Arguments[0] == 0)
										{
											newFloor["targetname"] = "sectornum" + i + "lowerfloor";
										}
										else
										{
											newFloor["targetname"] = "sector" + currentLinedef.Arguments[0] + "lowerfloor";
										}
										int lowestNeighborFloorHeight;
										if (linedefTriggerType == 21)
										{
											lowestNeighborFloorHeight = getLowestNeighborFloorHeight(i);
										}
										else
										{
											lowestNeighborFloorHeight = getNextLowestNeighborFloorHeight(i);
										}
										if (lowestNeighborFloorHeight == 32768)
										{
											lowestNeighborFloorHeight = doomMap.Sectors[i].FloorHeight;
										}
										int lip = ZMin - lowestNeighborFloorHeight;
										newFloor["lip"] = "" + System.Math.Abs(lip);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												floorsUsed[k] = true;
												newFloor.Brushes.Add(sectorFloorBrushes[i][k]);
											}
										}
										mapFile.Add(newFloor);
									}
									break;
								case 24: 
								// Floor raise to highest neighbor
								case 25:  // Floor raise to nearest higher neighbor
									// I don't know where retriggerability is determined, or whether or not it goes back up.
									if (currentLinedef.Arguments[0] == sectorTag[i])
									{
										Entity newFloor = new Entity("func_door");
										newFloor["angles"] = "270 0 0";
										newFloor["wait"] = "-1";
										newFloor["speed"] = "" + currentLinedef.Arguments[1];
										if (currentLinedef.Arguments[0] == 0)
										{
											newFloor["targetname"] = "sectornum" + i + "raisefloor";
										}
										else
										{
											newFloor["targetname"] = "sector" + currentLinedef.Arguments[0] + "raisefloor";
										}
										int highestNeighborFloorHeight;
										if (linedefTriggerType == 24)
										{
											highestNeighborFloorHeight = getHighestNeighborFloorHeight(i);
										}
										else
										{
											highestNeighborFloorHeight = getNextHighestNeighborFloorHeight(i);
										}
										if (highestNeighborFloorHeight == - 32768)
										{
											highestNeighborFloorHeight = doomMap.Sectors[i].FloorHeight;
										}
										int lip = ZMin - highestNeighborFloorHeight;
										newFloor["lip"] = "" + System.Math.Abs(lip);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												floorsUsed[k] = true;
												newFloor.Brushes.Add(sectorFloorBrushes[i][k]);
											}
										}
										mapFile.Add(newFloor);
									}
									break;
								}
						}
						else
						{
							if (currentLinedef.Tag == sectorTag[i])
							{
								switch (linedefTriggerType)
								{
									
									case 36: 
									// Line crossed, floor lowers, stays 8 above next lowest
									case 38:  // Line crossed, floor lowers, stays at next lowest
										Entity newFloor = new Entity("func_door");
										newFloor["speed"] = "120";
										newFloor["angles"] = "90 0 0";
										newFloor["targetname"] = "sector" + sectorTag[i] + "lowerfloor";
										newFloor["wait"] = "-1";
										int lowestNeighborFloorHeight = getLowestNeighborFloorHeight(i);
										int lip = ZMin - lowestNeighborFloorHeight;
										if (linedefTriggerType == 36)
										{
											lip -= 8;
										}
										newFloor["lip"] = "" + System.Math.Abs(lip);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												floorsUsed[k] = true;
												newFloor.Brushes.Add(sectorFloorBrushes[i][k]);
											}
										}
										mapFile.Add(newFloor);
										break;
									case 63: 
									// Push button, door opens, waits 4s, closes
									case 103: 
									// Push button, door opens, stays
									case 109:  // Cross line, door opens, stays
										Entity newDoor = new Entity("func_door");
										newDoor["speed"] = "60";
										newDoor["angles"] = "270 0 0";
										newDoor["targetname"] = "sector" + sectorTag[i] + "door";
										newDoor["wait"] = "-1";
										if (sectorTag[i] == 63)
										{
											newDoor["wait"] = "4";
										}
										int lowestNeighborCielingHeight = getLowestNeighborCielingHeight(i);
										lip = ZMax - lowestNeighborCielingHeight + 4;
										newDoor["lip"] = "" + lip;
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!cielingsUsed[k])
											{
												cielingsUsed[k] = true;
												newDoor.Brushes.Add(sectorCielingBrushes[i][k]);
											}
										}
										mapFile.Add(newDoor);
										break;
									case 62: 
									// Push button, Elevator goes down to lowest, wait 4s, goes up
									case 88:  // Elevator goes down to lowest, wait 4s, goes up
										Entity newVator = new Entity("func_door");
										newVator["speed"] = "120";
										newVator["angles"] = "90 0 0";
										newVator["targetname"] = "sector" + sectorTag[i] + "vator";
										newVator["wait"] = "4";
										lowestNeighborFloorHeight = getLowestNeighborFloorHeight(i);
										lip = System.Math.Abs(ZMin - lowestNeighborFloorHeight);
										newVator["lip"] = "" + lip;
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												newVator.Brushes.Add(sectorFloorBrushes[i][k]);
												floorsUsed[k] = true;
											}
										}
										mapFile.Add(newVator);
										break;
									default:  // I'd like to not use this evenutally, all the trigger types ought to be handled
										DecompilerThread.OnMessage(this, "WARNING: Unimplemented linedef trigger type " + linedefTriggerType + " for sector " + i + " tagged " + sectorTag[i]);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												world.Brushes.Add(sectorFloorBrushes[i][k]);
												floorsUsed[k] = true;
											}
											if (!cielingsUsed[k])
											{
												world.Brushes.Add(sectorCielingBrushes[i][k]);
												cielingsUsed[k] = true;
											}
										}
										break;
								}
							}
						}
					}
				}
			}
			for (int j = 0; j < sectorFloorBrushes[i].Length; j++)
			{
				if (!cielingsUsed[j])
				{
					world.Brushes.Add(sectorCielingBrushes[i][j]);
				}
				if (!floorsUsed[j])
				{
					world.Brushes.Add(sectorFloorBrushes[i][j]);
				}
			}
		}
		
		// Convert THINGS
		for (int i = 0; i < doomMap.Things.Count; i++)
		{
			DThing currentThing = doomMap.Things[i];
			// To find the true height of a thing, I need to iterate through nodes until I come to a subsector
			// definition. Then I need to use the floor height of the sector that subsector belongs to.
			Vector3D origin = currentThing.Origin;
			int subsectorIndex = doomMap.Nodes.Count - 1;
			while (subsectorIndex >= 0)
			{
				// Once child is negative, subsector is found
				DNode currentNode = doomMap.Nodes[subsectorIndex];
				Vector3D start = currentNode.VecHead;
				Vector3D end = currentNode.VecHead+currentNode.VecTail;
				Plane currentPlane = new Plane(start, end, new Vector3D(start.X, start.Y, 1));
				if (currentPlane.distance(origin) < 0)
				{
					subsectorIndex = currentNode.Child1;
				}
				else
				{
					subsectorIndex = currentNode.Child2;
				}
			}
			subsectorIndex += 32768;
			int sectorIndex = subsectorSectors[subsectorIndex];
			DSector thingSector = doomMap.Sectors[sectorIndex];
			if (origin.Z == 0)
			{
				origin.Z=thingSector.FloorHeight;
			}
			
			Entity thing = null;
			// Things from both Doom. Currently converting to appropriate Doom 3 entities.
			switch (currentThing.ClassNum)
			{
				case 1: 
				// Single player spawn
				case 2: 
				// coop
				case 3: 
				// coop
				case 4:  // coop
					thing = new Entity("info_player_start");
					if (currentThing.ClassNum > 1)
					{
						thing["targetname"] = "coopspawn" + currentThing.ClassNum;
					}
					playerStartOrigin = origin.X + " " + origin.Y + " " + (origin.Z + 36);
					thing["origin"] = playerStartOrigin;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 11:  // Deathmatch spawn
					thing = new Entity("info_player_deathmatch");
					thing["origin"] = origin.X + " " + origin.Y + " " + (origin.Z + 36);
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 14:  // Teleport destination
					thing = new Entity("info_teleport_destination");
					if (currentThing.ID != 0)
					{
						thing["targetname"] = "teledest" + currentThing.ID;
					}
					else
					{
						thing["targetname"] = "sector" + thingSector.Tag + "teledest";
					}
					thing["origin"] = origin.X + " " + origin.Y + " " + (origin.Z + 36);
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 17:  // Big cell pack
					thing = new Entity("ammo_cells_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 82:  // Super shotgun
					thing = new Entity("weapon_shotgun_double");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2001:  // Shotgun
					thing = new Entity("weapon_shotgun");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2002:  // Chaingun
					thing = new Entity("weapon_chaingun");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2003:  // Rocket launcher
					thing = new Entity("weapon_rocketlauncher");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2004:  // Plasma gun
					thing = new Entity("weapon_plasmagun");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2005:  // Chainsaw
					thing = new Entity("weapon_chainsaw");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2006:  // BFG9000
					thing = new Entity("weapon_bfg");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2007:  // Ammo clip
					thing = new Entity("ammo_clip_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2008:  // Shotgun shells
					thing = new Entity("ammo_shells_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2010:  // Rocket
					thing = new Entity("ammo_rockets_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2046:  // Box of Rockets
					thing = new Entity("ammo_rockets_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2047:  // Cell pack
					thing = new Entity("ammo_cells_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2048:  // Box of ammo
					thing = new Entity("ammo_bullets_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2049:  // Box of shells
					thing = new Entity("ammo_shells_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				}
			
			if (thing != null)
			{
				mapFile.Add(thing);
			}
		}
		
		Entity playerequip = new Entity("game_player_equip");
		playerequip["weapon_pistol"] = "1";
		playerequip["origin"] = playerStartOrigin;
		mapFile.Add(playerequip);
		parent.OnProgress(this, 1.0);
		return mapFile;
	}
Exemplo n.º 10
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);
        }
    }
Exemplo n.º 11
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);
		}
	}
Exemplo n.º 12
0
	// -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);
		}
	}
    // -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);
        }
    }