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