/// <summary>
        /// Postprocesser to convert the texture referenced by <paramref name="brushSide"/> into one used by GTKRadiant, if necessary.
        /// These textures are produced by the decompiler algorithm itself.
        /// </summary>
        /// <param name="brushSide">The <see cref="MAPBrushSide"/> to have its texture parsed.</param>
        private void PostProcessSpecialTexture(MAPBrushSide brushSide)
        {
            switch (brushSide.texture.ToLower())
            {
            case "**nulltexture**":
            case "**nodrawtexture**": {
                brushSide.texture = "common/nodraw";
                break;
            }

            case "**skiptexture**": {
                brushSide.texture = "common/skip";
                break;
            }

            case "**skytexture**": {
                brushSide.texture = "common/skyportal";
                break;
            }

            case "**hinttexture**": {
                brushSide.texture = "common/hint";
                break;
            }

            case "**cliptexture**": {
                brushSide.texture = "common/clip";
                break;
            }
            }
        }
 /// <summary>
 /// Postprocesser to convert the texture referenced by <paramref name="brushSide"/> into one used by GTKRadiant, if necessary.
 /// </summary>
 /// <param name="brushSide">The <see cref="MAPBrushSide"/> to have its texture parsed.</param>
 private void PostProcessQuake3Texture(MAPBrushSide brushSide)
 {
     if (brushSide.texture.Length >= 9 && brushSide.texture.Substring(0, 9).Equals("textures/", StringComparison.InvariantCultureIgnoreCase))
     {
         brushSide.texture = brushSide.texture.Substring(9);
     }
 }
示例#3
0
    // METHODS

    // Adds the new brush side to the brush, but only if it's not an already defined
    // side. Otherwise you might get "brush with coplanar planes" error.
    public virtual void add(MAPBrushSide newSide)
    {
        if (newSide != null)
        {
            bool duplicate = false;
            int  i         = 0;
            for (i = 0; i < sides.Length; i++)
            {
                if (newSide.Plane.Equals(sides[i].Plane))
                {
                    duplicate = true;
                    break;
                }
            }
            if (!duplicate)
            {
                MAPBrushSide[] newList = new MAPBrushSide[sides.Length + 1];
                for (int j = 0; j < sides.Length; j++)
                {
                    newList[j] = sides[j];
                }
                newList[sides.Length] = newSide;
                sides = newList;
            }
            else
            {
                // If it is a duplicate, one of them probably came form a Doom node subdivision which will always be textured "special/nodraw"
                // but the other one (from a segment) will have a texture which needs to be visible. Select for that one.
                if (sides[i].Texture.ToUpper().Equals("special/nodraw".ToUpper()) && !newSide.Texture.ToUpper().Equals("special/nodraw".ToUpper()))
                {
                    sides[i].Texture = newSide.Texture;
                }
            }
        }
    }
示例#4
0
	// METHODS
	
	// Adds the new brush side to the brush, but only if it's not an already defined
	// side. Otherwise you might get "brush with coplanar planes" error.
	public virtual void add(MAPBrushSide newSide) {
		if (newSide != null) {
			bool duplicate = false;
			int i = 0;
			for (i = 0; i < sides.Length; i++) {
				if (newSide.Plane.Equals(sides[i].Plane)) {
					duplicate = true;
					break;
				}
			}
			if (!duplicate) {
				MAPBrushSide[] newList = new MAPBrushSide[sides.Length + 1];
				for (int j = 0; j < sides.Length; j++) {
					newList[j] = sides[j];
				}
				newList[sides.Length] = newSide;
				sides = newList;
			} else {
				// If it is a duplicate, one of them probably came form a Doom node subdivision which will always be textured "special/nodraw"
				// but the other one (from a segment) will have a texture which needs to be visible. Select for that one.
				if (sides[i].Texture.ToUpper().Equals("special/nodraw".ToUpper()) && !newSide.Texture.ToUpper().Equals("special/nodraw".ToUpper())) {
					sides[i].Texture = newSide.Texture;
				}
			}
		}
	}
示例#5
0
    // The inspiration for this is the BSPSource source code. It's not a direct copy but does essentially
    // the same thing as the algorithm to "create a prism back", and isn't as neatly written.
    public static MAPBrush createBrushFromWind(Vector3D[] froms, Vector3D[] tos, string texture, string backtex, TexInfo scaling)
    {
        Vector3D[]     planepts = new Vector3D[3];
        MAPBrushSide[] sides    = new MAPBrushSide[froms.Length + 2];      // Each edge, plus a front and back side
        planepts[0] = froms[0];
        planepts[1] = tos[0];
        planepts[2] = tos[1];
        Plane    plane         = new Plane(planepts);
        Vector3D reverseNormal = plane.Normal;

        sides[0] = new MAPBrushSide(planepts, texture, scaling.SAxis.Point, scaling.SShift, scaling.TAxis.Point, scaling.TShift, 0, 1, 1, 0, "wld_lightmap", 16, 0);

        Vector3D[] backplanepts = new Vector3D[3];
        backplanepts[0] = froms[0] - (reverseNormal);
        backplanepts[1] = tos[1] - (reverseNormal);
        backplanepts[2] = tos[0] - (reverseNormal);
        Plane backplane = new Plane(backplanepts);

        Vector3D[] backaxes = TexInfo.textureAxisFromPlane(backplane);
        sides[1] = new MAPBrushSide(backplane, backtex, backaxes[0].Point, 0, backaxes[1].Point, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);

        for (int i = 0; i < froms.Length; i++)
        {
            // each edge
            Vector3D[] sideplanepts = new Vector3D[3];
            sideplanepts[0] = froms[i];
            sideplanepts[1] = tos[i];
            sideplanepts[2] = froms[i] + (reverseNormal);
            Plane      sideplane = new Plane(sideplanepts);
            Vector3D[] sideaxes  = TexInfo.textureAxisFromPlane(sideplane);
            sides[i + 2] = new MAPBrushSide(sideplane, backtex, sideaxes[0].Point, 0, sideaxes[1].Point, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
        }

        return(new MAPBrush(sides, 0, 0, false));
    }
示例#6
0
        /// <summary>
        /// Processes a <see cref="Brush"/> into a state where it can be output into a file that map editors can read.
        /// </summary>
        /// <param name="brush">The <see cref="Brush"/> to process.</param>
        /// <param name="worldPosition">The position of the parent <see cref="Entity"/> in the world. This is important for calculating UVs on solids.</param>
        /// <returns>The processed <see cref="MAPBrush"/> object, to be added to an <see cref="Entity"/> object.</returns>
        private MAPBrush ProcessBrush(Brush brush, Vector3d worldPosition)
        {
            List <BrushSide> sides;

            // CoD BSPs store brush sides sequentially so the brush structure doesn't reference a first side.
            if (brush.firstSide < 0)
            {
                sides              = _bsp.brushSides.GetRange(_currentSideIndex, brush.numSides);
                _currentSideIndex += brush.numSides;
            }
            else
            {
                sides = _bsp.GetReferencedObjects <BrushSide>(brush, "brushSides");
            }
            MAPBrush mapBrush = new MAPBrush();

            mapBrush.isDetail = brush.IsDetail(_bsp);
            mapBrush.isWater  = brush.IsWater(_bsp);
            mapBrush.isManVis = brush.IsManVis(_bsp);
            int sideNum = 0;

            foreach (BrushSide side in sides)
            {
                MAPBrushSide mapBrushSide = ProcessBrushSide(side, worldPosition, sideNum);
                if (mapBrushSide != null)
                {
                    mapBrush.sides.Add(mapBrushSide);
                }
                ++sideNum;
            }

            return(mapBrush);
        }
 /// <summary>
 /// Moves this <see cref="MAPBrushSide"/> object using the passed vector <paramref name="v"/>.
 /// </summary>
 /// <param name="mapBrushSide">This <see cref="MAPBrushSide"/>.</param>
 /// <param name="v">Translation vector.</param>
 public static void Translate(this MAPBrushSide mapBrushSide, Vector3d v)
 {
     for (int i = 0; i < mapBrushSide.vertices.Length; ++i)
     {
         mapBrushSide.vertices[i] += v;
     }
     mapBrushSide.plane = new Plane(mapBrushSide.vertices);
 }
 /// <summary>
 /// Moves this <see cref="MAPBrushSide"/> object using the passed vector <paramref name="v"/>.
 /// </summary>
 /// <param name="mapBrushSide">This <see cref="MAPBrushSide"/>.</param>
 /// <param name="v">Translation vector.</param>
 public static void Translate(this MAPBrushSide mapBrushSide, Vector3 v)
 {
     for (int i = 0; i < mapBrushSide.vertices.Length; ++i)
     {
         mapBrushSide.vertices[i] += v;
     }
     mapBrushSide.plane = Plane.CreateFromVertices(mapBrushSide.vertices[0], mapBrushSide.vertices[2], mapBrushSide.vertices[1]);
 }
示例#9
0
	public MAPBrush(MAPBrushSide[] sides, int num, int entnum, bool isDetail) {
		this.sides = new MAPBrushSide[0];
		for (int i = 0; i < sides.Length; i++) {
			add(sides[i]);
		}
		brushnum = num;
		this.entnum = entnum;
		this.isDetailBrush = isDetail;
	}
示例#10
0
	public virtual void delete(int side) {
		MAPBrushSide[] newList = new MAPBrushSide[sides.Length - 1];
		for (int i = 0; i < side; i++) {
			newList[i] = sides[i];
		}
		for (int i = side + 1; i < sides.Length; i++) {
			newList[i - 1] = sides[i];
		}
		sides = newList;
	}
示例#11
0
 /// <summary>
 /// Process the data in a <see cref="MAPBrushSide"/> into the passed <see cref="StringBuilder"/>.
 /// </summary>
 /// <param name="brushside">The <see cref="MAPBrushSide"/> to process.</param>
 /// <param name="sb">A <see cref="StringBuilder"/> object to append processed data from <paramref name="brushside"/> to.</param>
 private void ParseBrushSide(MAPBrushSide brushside, StringBuilder sb)
 {
     sb.Append("\t\tside\r\n\t\t{\r\n\t\t\t\"id\" \"")
     .Append(_nextID)
     .Append("\"\r\n\t\t\t\"plane\" \"(")
     .Append(brushside.vertices[0].x.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.vertices[0].y.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.vertices[0].z.ToString("###0.######", format))
     .Append(") (")
     .Append(brushside.vertices[1].x.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.vertices[1].y.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.vertices[1].z.ToString("###0.######", format))
     .Append(") (")
     .Append(brushside.vertices[2].x.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.vertices[2].y.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.vertices[2].z.ToString("###0.######", format))
     .Append(")\"\r\n\t\t\t\"material\" \"")
     .Append(brushside.texture)
     .Append("\"\r\n\t\t\t\"uaxis\" \"[")
     .Append(brushside.textureInfo.uAxis.x.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.textureInfo.uAxis.y.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.textureInfo.uAxis.z.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.textureInfo.translation.x.ToString("###0.######", format))
     .Append("] ")
     .Append(brushside.textureInfo.scale.x.ToString("###0.####", format))
     .Append("\"\r\n\t\t\t\"vaxis\" \"[")
     .Append(brushside.textureInfo.vAxis.x.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.textureInfo.vAxis.y.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.textureInfo.vAxis.z.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.textureInfo.translation.y.ToString("###0.######", format))
     .Append("] ")
     .Append(brushside.textureInfo.scale.y.ToString("###0.####", format))
     .Append("\"\r\n\t\t\t\"rotation\" \"")
     .Append(brushside.textureInfo.rotation.ToString("###0.####", format))
     .Append("\"\r\n\t\t\t\"lightmapscale\" \"")
     .Append(brushside.lgtScale.ToString("###0.####", format))
     .Append("\"\r\n\t\t\t\"smoothing_groups\" \"0\"\r\n");
     if (brushside.displacement != null)
     {
         ParseDisplacement(brushside.displacement, sb);
     }
     sb.Append("\t\t}\r\n");
 }
示例#12
0
 /// <summary>
 /// Process the data in a <see cref="MAPBrushSide"/> into the passed <see cref="StringBuilder"/>.
 /// </summary>
 /// <param name="brushside">The <see cref="MAPBrushSide"/> to process.</param>
 /// <param name="sb">A <see cref="StringBuilder"/> object to append processed data from <paramref name="brushside"/> to.</param>
 private void ParseBrushSide(MAPBrushSide brushside, StringBuilder sb)
 {
     sb.Append("( ")
     .Append(brushside.vertices[0].x.ToString("###0.000000", format))
     .Append(" ")
     .Append(brushside.vertices[0].y.ToString("###0.000000", format))
     .Append(" ")
     .Append(brushside.vertices[0].z.ToString("###0.000000", format))
     .Append(" ) ( ")
     .Append(brushside.vertices[1].x.ToString("###0.000000", format))
     .Append(" ")
     .Append(brushside.vertices[1].y.ToString("###0.000000", format))
     .Append(" ")
     .Append(brushside.vertices[1].z.ToString("###0.000000", format))
     .Append(" ) ( ")
     .Append(brushside.vertices[2].x.ToString("###0.000000", format))
     .Append(" ")
     .Append(brushside.vertices[2].y.ToString("###0.000000", format))
     .Append(" ")
     .Append(brushside.vertices[2].z.ToString("###0.000000", format))
     .Append(" ) ")
     .Append(brushside.texture)
     .Append(" [ ")
     .Append(brushside.textureInfo.uAxis.x.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.uAxis.y.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.uAxis.z.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.translation.x.ToString("###0.####", format))
     .Append(" ] [ ")
     .Append(brushside.textureInfo.vAxis.x.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.vAxis.y.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.vAxis.z.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.translation.y.ToString("###0.####", format))
     .Append(" ] ")
     .Append(brushside.textureInfo.rotation.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.scale.x.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.scale.y.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.flags)
     .Append(" ")
     .Append(brushside.material)
     .Append(" [ ")
     .Append(brushside.lgtScale.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.lgtRot.ToString("###0.####", format))
     .Append(" ]\r\n");
 }
示例#13
0
 /// <summary>
 /// Process the data in a <see cref="MAPBrushSide"/> into the passed <see cref="StringBuilder"/>.
 /// </summary>
 /// <param name="brushside">The <see cref="MAPBrushSide"/> to process.</param>
 /// <param name="sb">A <see cref="StringBuilder"/> object to append processed data from <paramref name="brushside"/> to.</param>
 private void ParseBrushSide(MAPBrushSide brushside, StringBuilder sb)
 {
     sb.Append("( ")
     .Append(brushside.vertices[0].X.ToString("###0.000000", format))
     .Append(" ")
     .Append(brushside.vertices[0].Y.ToString("###0.000000", format))
     .Append(" ")
     .Append(brushside.vertices[0].Z.ToString("###0.000000", format))
     .Append(" ) ( ")
     .Append(brushside.vertices[1].X.ToString("###0.000000", format))
     .Append(" ")
     .Append(brushside.vertices[1].Y.ToString("###0.000000", format))
     .Append(" ")
     .Append(brushside.vertices[1].Z.ToString("###0.000000", format))
     .Append(" ) ( ")
     .Append(brushside.vertices[2].X.ToString("###0.000000", format))
     .Append(" ")
     .Append(brushside.vertices[2].Y.ToString("###0.000000", format))
     .Append(" ")
     .Append(brushside.vertices[2].Z.ToString("###0.000000", format))
     .Append(" ) ")
     .Append(brushside.texture)
     .Append(" [ ")
     .Append(brushside.textureInfo.UAxis.X.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.UAxis.Y.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.UAxis.Z.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.Translation.X.ToString("###0.####", format))
     .Append(" ] [ ")
     .Append(brushside.textureInfo.VAxis.X.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.VAxis.Y.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.VAxis.Z.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.Translation.Y.ToString("###0.####", format))
     .Append(" ] ")
     .Append(brushside.textureInfo.rotation.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.scale.X.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.scale.Y.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.textureInfo.Flags)
     .Append(" ")
     .Append(brushside.material)
     .Append(" [ ")
     .Append(brushside.lgtScale.ToString("###0.####", format))
     .Append(" ")
     .Append(brushside.lgtRot.ToString("###0.####", format))
     .Append(" ]\r\n");
 }
示例#14
0
 public virtual void delete(int side)
 {
     MAPBrushSide[] newList = new MAPBrushSide[sides.Length - 1];
     for (int i = 0; i < side; i++)
     {
         newList[i] = sides[i];
     }
     for (int i = side + 1; i < sides.Length; i++)
     {
         newList[i - 1] = sides[i];
     }
     sides = newList;
 }
        /// <summary>
        /// Postprocesser to convert the texture referenced by <paramref name="brushSide"/> into one used by GTKRadiant, if necessary.
        /// </summary>
        /// <param name="brushSide">The <see cref="MAPBrushSide"/> to have its texture parsed.</param>
        private void PostProcessNightfireTexture(MAPBrushSide brushSide)
        {
            switch (brushSide.texture.ToLower())
            {
            case "special/nodraw":
            case "special/null": {
                brushSide.texture = "common/nodraw";
                break;
            }

            case "special/clip": {
                brushSide.texture = "common/clip";
                break;
            }

            case "special/sky": {
                brushSide.texture = "common/skyportal";
                break;
            }

            case "special/trigger": {
                brushSide.texture = "common/trigger";
                break;
            }

            case "special/playerclip": {
                brushSide.texture = "common/playerclip";
                break;
            }

            case "special/hint": {
                brushSide.texture = "common/hint";
                break;
            }

            case "special/skip": {
                brushSide.texture = "common/skip";
                break;
            }

            case "special/npcclip":
            case "special/enemyclip": {
                brushSide.texture = "common/tankclip";
                break;
            }
            }
        }
示例#16
0
 /// <summary>
 /// Process the data in a <see cref="MAPBrushSide"/> into the passed <see cref="StringBuilder"/>.
 /// </summary>
 /// <param name="brushside">The <see cref="MAPBrushSide"/> to process.</param>
 /// <param name="sb">A <see cref="StringBuilder"/> object to append processed data from <paramref name="brushside"/> to.</param>
 private void ParseBrushSide(MAPBrushSide brushside, StringBuilder sb)
 {
     sb.Append("  ( ")
     .Append(brushside.plane.normal.x.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.plane.normal.y.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.plane.normal.z.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.plane.distance.ToString("###0.##########", format))
     .Append(" ) ( ( 1 0 ")
     .Append(brushside.textureInfo.translation.x.ToString("###0.##########", format))
     .Append(" ) ( 0 1 ")
     .Append(brushside.textureInfo.translation.y.ToString("###0.##########", format))
     .Append(" ) ) \"")
     .Append(brushside.texture)
     .Append("\" 0 0 0\r\n");
 }
 /// <summary>
 /// Process the data in a <see cref="MAPBrushSide"/> into the passed <see cref="StringBuilder"/>.
 /// </summary>
 /// <param name="brushside">The <see cref="MAPBrushSide"/> to process.</param>
 /// <param name="sb">A <see cref="StringBuilder"/> object to append processed data from <paramref name="brushside"/> to.</param>
 private void ParseBrushSide(MAPBrushSide brushside, StringBuilder sb)
 {
     sb.Append("  ( ")
     .Append(brushside.plane.Normal.X.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.plane.Normal.Y.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.plane.Normal.Z.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.plane.D.ToString("###0.##########", format))
     .Append(" ) ( ( 1 0 ")
     .Append(brushside.textureInfo.Translation.X.ToString("###0.##########", format))
     .Append(" ) ( 0 1 ")
     .Append(brushside.textureInfo.Translation.Y.ToString("###0.##########", format))
     .Append(" ) ) \"")
     .Append(brushside.texture)
     .Append("\" 0 0 0\r\n");
 }
 /// <summary>
 /// Postprocesser to convert the texture referenced by <paramref name="brushSide"/> into one used by Hammer, if necessary.
 /// </summary>
 /// <param name="brushSide">The <see cref="MAPBrushSide"/> to have its texture parsed.</param>
 private void PostProcessQuake2Texture(MAPBrushSide brushSide)
 {
     if (brushSide.texture.Length >= 5 && brushSide.texture.Substring(brushSide.texture.Length - 5).Equals("/clip", StringComparison.InvariantCultureIgnoreCase))
     {
         brushSide.texture = "special/clip";
     }
     else if (brushSide.texture.Length >= 5 && brushSide.texture.Substring(brushSide.texture.Length - 5).Equals("/hint", StringComparison.InvariantCultureIgnoreCase))
     {
         brushSide.texture = "special/hint";
     }
     else if (brushSide.texture.Length >= 8 && brushSide.texture.Substring(brushSide.texture.Length - 8).Equals("/trigger", StringComparison.InvariantCultureIgnoreCase))
     {
         brushSide.texture = "special/trigger";
     }
     else if (brushSide.texture.Equals("*** unsused_texinfo ***", StringComparison.InvariantCultureIgnoreCase))
     {
         brushSide.texture = "special/nodraw";
     }
 }
 /// <summary>
 /// Process the data in a <see cref="MAPBrushSide"/> into the passed <see cref="StringBuilder"/>.
 /// </summary>
 /// <param name="brushside">The <see cref="MAPBrushSide"/> to process.</param>
 /// <param name="sb">A <see cref="StringBuilder"/> object to append processed data from <paramref name="brushside"/> to.</param>
 private void ParseBrushSide(MAPBrushSide brushside, bool isDetail, StringBuilder sb)
 {
     sb.Append("( ")
     .Append(brushside.vertices[0].x.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.vertices[0].y.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.vertices[0].z.ToString("###0.######", format))
     .Append(" ) ( ")
     .Append(brushside.vertices[1].x.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.vertices[1].y.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.vertices[1].z.ToString("###0.######", format))
     .Append(" ) ( ")
     .Append(brushside.vertices[2].x.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.vertices[2].y.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.vertices[2].z.ToString("###0.######", format))
     .Append(" ) ")
     .Append(brushside.texture)
     .Append(" ")
     .Append(brushside.textureInfo.translation.x.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.textureInfo.translation.y.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.textureInfo.rotation.ToString("###0.00", format))
     .Append(" ")
     .Append(brushside.textureInfo.scale.x.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.textureInfo.scale.y.ToString("###0.######", format))
     .Append(" ")
     .Append(brushside.textureInfo.flags)
     .Append(" 0 0");
     if (isDetail)
     {
         sb.Append(" +surfaceparm detail");
     }
     sb.Append("\r\n");
 }
 public MAPBrushSide(MAPBrushSide copy)
 {
     plane           = new Plane(copy.Plane);
     triangle[0]     = new Vector3D(copy.Triangle[0]);
     triangle[1]     = new Vector3D(copy.Triangle[1]);
     triangle[2]     = new Vector3D(copy.Triangle[2]);
     texture         = copy.Texture;
     textureS        = new Vector3D(copy.TextureS);
     textureT        = new Vector3D(copy.TextureT);
     textureShiftS   = copy.TextureShiftS;
     textureShiftT   = copy.TextureShiftT;
     texRot          = copy.TexRot;
     texScaleX       = copy.TexScaleX;
     texScaleY       = copy.TexScaleY;
     flags           = copy.Flags;
     material        = copy.Material;
     lgtScale        = copy.LgtScale;
     lgtRot          = copy.LgtRot;
     planeDefined    = copy.DefinedByPlane;
     triangleDefined = copy.DefinedByTriangle;
 }
 /// <summary>
 /// Process the data in a <see cref="MAPBrushSide"/> into the passed <c>StringBuilder</c>.
 /// </summary>
 /// <param name="brushside">The <see cref="MAPBrushSide"/> to process.</param>
 /// <param name="sb">A <c>StringBuilder</c> object to append processed data from <paramref name="brushside"/> to.</param>
 private void ParseBrushSide(MAPBrushSide brushside, StringBuilder sb)
 {
     sb.Append("( ")
     .Append(brushside.vertices[0].x.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.vertices[0].y.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.vertices[0].z.ToString("###0.##########", format))
     .Append(" ) ( ")
     .Append(brushside.vertices[1].x.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.vertices[1].y.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.vertices[1].z.ToString("###0.##########", format))
     .Append(" ) ( ")
     .Append(brushside.vertices[2].x.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.vertices[2].y.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.vertices[2].z.ToString("###0.##########", format))
     .Append(" ) ")
     .Append(brushside.texture)
     .Append(" ")
     .Append(brushside.textureInfo.translation.x.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.textureInfo.translation.y.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.textureInfo.rotation.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.textureInfo.scale.x.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.textureInfo.scale.y.ToString("###0.##########", format))
     .Append(" ")
     .Append(brushside.textureInfo.flags)
     .Append(" 0 0\r\n");
 }
示例#22
0
    // Create a rectangular brush from mins to maxs, with specified texture
    public static MAPBrush createBrush(Vector3D mins, Vector3D maxs, string texture)
    {
        MAPBrush newBrush = new MAPBrush(-1, 0, false);

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

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

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

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

        cross.normalize();

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

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

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

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

        return(newBrush);
    }
示例#24
0
	// Create a rectangular brush from mins to maxs, with specified texture
	public static MAPBrush createBrush(Vector3D mins, Vector3D maxs, string texture)
	{
		MAPBrush newBrush = new MAPBrush(- 1, 0, false);
		Vector3D[][] planes = new Vector3D[6][];
		for (int i = 0; i < 6; i++)
		{
			planes[i] = new Vector3D[3];
		} // Six planes for a cube brush, three vertices for each plane
		double[][] textureS = new double[6][];
		for (int i2 = 0; i2 < 6; i2++)
		{
			textureS[i2] = new double[3];
		}
		double[][] textureT = new double[6][];
		for (int i3 = 0; i3 < 6; i3++)
		{
			textureT[i3] = new double[3];
		}
		// The planes and their texture scales
		// I got these from an origin brush created by Gearcraft. Don't worry where these numbers came from, they work.
		// Top
		planes[0][0] = new Vector3D(mins.X, maxs.Y, maxs.Z);
		planes[0][1] = new Vector3D(maxs.X, maxs.Y, maxs.Z);
		planes[0][2] = new Vector3D(maxs.X, mins.Y, maxs.Z);
		textureS[0][0] = 1;
		textureT[0][1] = - 1;
		// Bottom
		planes[1][0] = new Vector3D(mins.X, mins.Y, mins.Z);
		planes[1][1] = new Vector3D(maxs.X, mins.Y, mins.Z);
		planes[1][2] = new Vector3D(maxs.X, maxs.Y, mins.Z);
		textureS[1][0] = 1;
		textureT[1][1] = - 1;
		// Left
		planes[2][0] = new Vector3D(mins.X, maxs.Y, maxs.Z);
		planes[2][1] = new Vector3D(mins.X, mins.Y, maxs.Z);
		planes[2][2] = new Vector3D(mins.X, mins.Y, mins.Z);
		textureS[2][1] = 1;
		textureT[2][2] = - 1;
		// Right
		planes[3][0] = new Vector3D(maxs.X, maxs.Y, mins.Z);
		planes[3][1] = new Vector3D(maxs.X, mins.Y, mins.Z);
		planes[3][2] = new Vector3D(maxs.X, mins.Y, maxs.Z);
		textureS[3][1] = 1;
		textureT[3][2] = - 1;
		// Near
		planes[4][0] = new Vector3D(maxs.X, maxs.Y, maxs.Z);
		planes[4][1] = new Vector3D(mins.X, maxs.Y, maxs.Z);
		planes[4][2] = new Vector3D(mins.X, maxs.Y, mins.Z);
		textureS[4][0] = 1;
		textureT[4][2] = - 1;
		// Far
		planes[5][0] = new Vector3D(maxs.X, mins.Y, mins.Z);
		planes[5][1] = new Vector3D(mins.X, mins.Y, mins.Z);
		planes[5][2] = new Vector3D(mins.X, mins.Y, maxs.Z);
		textureS[5][0] = 1;
		textureT[5][2] = - 1;
		
		for (int j = 0; j < 6; j++)
		{
			MAPBrushSide currentEdge = new MAPBrushSide(planes[j], texture, textureS[j], 0, textureT[j], 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			newBrush.add(currentEdge);
		}
		return newBrush;
	}
	// -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);
		}
	}
示例#26
0
        /// <summary>
        /// Processes a <see cref="BrushSide"/> into a state where it can be output into a file that map editors can read.
        /// </summary>
        /// <param name="brushSide">The <see cref="BrushSide"/> to process.</param>
        /// <param name="worldPosition">The position of the parent <see cref="Entity"/> in the world. This is important for calculating UVs on solids.</param>
        /// <param name="sideIndex">The index of this side reference in the parent <see cref="Brush"/>. Important for Call of Duty series maps, since
        /// the first six <see cref="BrushSide"/>s in a <see cref="Brush"/> don't contain <see cref="Plane"/> references.</param>
        /// <returns>The processed <see cref="MAPBrushSode"/> object, to be added to a <see cref="Brush"/> object.</returns>
        private MAPBrushSide ProcessBrushSide(BrushSide brushSide, Vector3d worldPosition, int sideIndex)
        {
            if (brushSide.bevel)
            {
                return(null);
            }
            MAPBrushSide mapBrushSide;
            // The things we'll need to define a .MAP brush side
            string      texture;
            string      material = "wld_lightmap";
            TextureInfo texInfo;

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

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

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

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

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

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

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

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

            TextureInfo outputTexInfo;

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

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

            return(mapBrushSide);
        }
	// -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);
		}
	}
 private string brushSideToString(MAPBrushSide inputData, bool isDetail)
 {
     try {
         Vector3D[] triangle      = inputData.Triangle;
         string     texture       = inputData.Texture;
         Vector3D   textureS      = inputData.TextureS;
         Vector3D   textureT      = inputData.TextureT;
         double     textureShiftS = inputData.TextureShiftS;
         double     textureShiftT = inputData.TextureShiftT;
         float      texRot        = inputData.TexRot;
         double     texScaleX     = inputData.TexScaleX;
         double     texScaleY     = inputData.TexScaleY;
         int        flags         = inputData.Flags;
         string     material      = inputData.Material;
         double     lgtScale      = inputData.LgtScale;
         double     lgtRot        = inputData.LgtRot;
         string     temp          = "";
         // Correct textures here
         try
         {
             if (texture.Substring(0, (9) - (0)).ToUpper().Equals("textures/".ToUpper()))
             {
                 texture = texture.Substring(9);
             }
         }
         catch (System.ArgumentOutOfRangeException)
         {
             ;
         }
         if (BSPVersion == mapType.TYPE_NIGHTFIRE || BSPVersion == mapType.TYPE_DOOM || BSPVersion == mapType.TYPE_HEXEN)
         {
             if (texture.ToUpper().Equals("special/nodraw".ToUpper()) || texture.ToUpper().Equals("special/null".ToUpper()))
             {
                 texture = "common/nodraw";
             }
             else
             {
                 if (texture.ToUpper().Equals("special/clip".ToUpper()))
                 {
                     texture = "common/clip";
                 }
                 else
                 {
                     if (texture.ToUpper().Equals("special/sky".ToUpper()))
                     {
                         texture = "common/skyportal";
                     }
                     else
                     {
                         if (texture.ToUpper().Equals("special/trigger".ToUpper()))
                         {
                             texture = "common/trigger";
                         }
                         else
                         {
                             if (texture.ToUpper().Equals("special/playerclip".ToUpper()))
                             {
                                 texture = "common/playerclip";
                             }
                             else
                             {
                                 if (texture.ToUpper().Equals("special/npcclip".ToUpper()) || texture.ToUpper().Equals("special/enemyclip".ToUpper()))
                                 {
                                     texture = "common/tankclip";
                                 }
                             }
                         }
                     }
                 }
             }
         }
         else
         {
             if (BSPVersion == mapType.TYPE_QUAKE2)
             {
                 try
                 {
                     if (texture.ToUpper().Equals("special/hint".ToUpper()))
                     {
                         texture = "common/hint";
                     }
                     else
                     {
                         if (texture.ToUpper().Equals("special/skip".ToUpper()))
                         {
                             texture = "common/skip";
                         }
                         else
                         {
                             if (texture.ToUpper().Equals("special/sky".ToUpper()))
                             {
                                 texture = "common/skyportal";
                             }
                             else
                             {
                                 if (texture.Substring(texture.Length - 8).ToUpper().Equals("/trigger".ToUpper()))
                                 {
                                     texture = "common/trigger";
                                 }
                                 else
                                 {
                                     if (texture.Substring(texture.Length - 5).ToUpper().Equals("/clip".ToUpper()))
                                     {
                                         texture = "common/clip";
                                     }
                                 }
                             }
                         }
                     }
                 }
                 catch (System.ArgumentOutOfRangeException e)
                 {
                     ;
                 }
             }
             else
             {
                 if (BSPVersion == mapType.TYPE_SOURCE17 || BSPVersion == mapType.TYPE_SOURCE18 || BSPVersion == mapType.TYPE_SOURCE19 || BSPVersion == mapType.TYPE_SOURCE20 || BSPVersion == mapType.TYPE_SOURCE21 || BSPVersion == mapType.TYPE_SOURCE22 || BSPVersion == mapType.TYPE_SOURCE23 || BSPVersion == mapType.TYPE_DMOMAM || BSPVersion == mapType.TYPE_VINDICTUS || BSPVersion == mapType.TYPE_TACTICALINTERVENTION)
                 {
                     try
                     {
                         if (texture.Substring(0, (5) - (0)).ToUpper().Equals("maps/".ToUpper()))
                         {
                             texture = texture.Substring(5);
                             for (int i = 0; i < texture.Length; i++)
                             {
                                 if (texture[i] == '/')
                                 {
                                     texture = texture.Substring(i + 1);
                                     break;
                                 }
                             }
                         }
                     }
                     catch (System.ArgumentOutOfRangeException e)
                     {
                         ;
                     }
                     // Find cubemap textures
                     int  numUnderscores = 0;
                     bool validnumber    = false;
                     for (int i = texture.Length - 1; i > 0; i--)
                     {
                         if (texture[i] <= '9' && texture[i] >= '0')
                         {
                             // Current is a number, start building string
                             validnumber = true;
                         }
                         else
                         {
                             if (texture[i] == '-')
                             {
                                 // Current is a minus sign (-).
                                 if (!validnumber)
                                 {
                                     break;                                         // Make sure there's a number to add the minus sign to. If not, kill the loop.
                                 }
                             }
                             else
                             {
                                 if (texture[i] == '_')
                                 {
                                     // Current is an underscore (_)
                                     if (validnumber)
                                     {
                                         // Make sure there is a number in the current string
                                         numUnderscores++;                                             // before moving on to the next one.
                                         validnumber = false;
                                         if (numUnderscores == 3)
                                         {
                                             // If we've got all our numbers
                                             texture = texture.Substring(0, (i) - (0));             // Cut the texture string
                                             break;                                                 // Kill the loop, we're done
                                         }
                                     }
                                     else
                                     {
                                         // No number after the underscore
                                         break;
                                     }
                                 }
                                 else
                                 {
                                     // Not an acceptable character
                                     break;
                                 }
                             }
                         }
                     }
                 }
             }
         }
         if (Double.IsInfinity(texScaleX) || Double.IsNaN(texScaleX))
         {
             texScaleX = 1;
         }
         if (Double.IsInfinity(texScaleY) || Double.IsNaN(texScaleY))
         {
             texScaleY = 1;
         }
         if (Double.IsInfinity(textureShiftS) || Double.IsNaN(textureShiftS))
         {
             textureShiftS = 0;
         }
         if (Double.IsInfinity(textureShiftT) || Double.IsNaN(textureShiftT))
         {
             textureShiftT = 0;
         }
         if (Double.IsInfinity(textureS.X) || Double.IsNaN(textureS.X) || Double.IsInfinity(textureS.Y) || Double.IsNaN(textureS.Y) || Double.IsInfinity(textureS.Z) || Double.IsNaN(textureS.Z))
         {
             textureS = TexInfo.textureAxisFromPlane(inputData.Plane)[0];
         }
         if (Double.IsInfinity(textureT.X) || Double.IsNaN(textureT.X) || Double.IsInfinity(textureT.Y) || Double.IsNaN(textureT.Y) || Double.IsInfinity(textureT.Z) || Double.IsNaN(textureT.Z))
         {
             textureT = TexInfo.textureAxisFromPlane(inputData.Plane)[1];
         }
         if (Settings.roundNums)
         {
             temp = "( " + MAPMaker.Round(triangle[0].X, 6) +
                    " " + MAPMaker.Round(triangle[0].Y, 6) +
                    " " + MAPMaker.Round(triangle[0].Z, 6) + " ) " +
                    "( " + MAPMaker.Round(triangle[1].X, 6) +
                    " " + MAPMaker.Round(triangle[1].Y, 6) +
                    " " + MAPMaker.Round(triangle[1].Z, 6) + " ) " +
                    "( " + MAPMaker.Round(triangle[2].X, 6) +
                    " " + MAPMaker.Round(triangle[2].Y, 6) +
                    " " + MAPMaker.Round(triangle[2].Z, 6) + " ) " +
                    texture + " " + System.Math.Floor(textureShiftS) + " " + System.Math.Floor(textureShiftT) + " " +
                    MAPMaker.FormattedRound(texRot, 2, "######0.00") + " " +
                    MAPMaker.Round(texScaleX, 6) + " " +
                    MAPMaker.Round(texScaleY, 6) + " " + flags + " 0 0 ";
         }
         else
         {
             temp = "( " + triangle[0].X + " " + triangle[0].Y + " " + triangle[0].Z + " ) " + "( " + triangle[1].X + " " + triangle[1].Y + " " + triangle[1].Z + " ) " + "( " + triangle[2].X + " " + triangle[2].Y + " " + triangle[2].Z + " ) " + texture + " " + textureShiftS + " " + textureShiftT + " " + texRot + " " + texScaleX + " " + texScaleY + " " + flags + " 0 0 ";
         }
         if (isDetail)
         {
             temp += "+surfaceparm detail ";
         }
         return(temp);
     }
     catch (System.NullReferenceException e)
     {
         DecompilerThread.OnMessage(this, "WARNING: Side with bad data! Not exported!");
         return("");
     }
 }
	private string brushSideToString(MAPBrushSide inputData) {
		try {
			string texture = inputData.Texture;
			if (BSPVersion == mapType.TYPE_SOURCE17 || BSPVersion == mapType.TYPE_SOURCE18 || BSPVersion == mapType.TYPE_SOURCE19 || BSPVersion == mapType.TYPE_SOURCE20 || BSPVersion == mapType.TYPE_SOURCE21 || BSPVersion == mapType.TYPE_SOURCE22 || BSPVersion == mapType.TYPE_SOURCE23 || BSPVersion == mapType.TYPE_DMOMAM || BSPVersion == mapType.TYPE_VINDICTUS || BSPVersion == mapType.TYPE_TACTICALINTERVENTION) {
				try {
					if (texture.Substring(0, (5) - (0)).ToUpper().Equals("maps/".ToUpper())) {
						texture = texture.Substring(5);
						for (int i = 0; i < texture.Length; i++) {
							if (texture[i] == '/') {
								texture = texture.Substring(i + 1);
								break;
							}
						}
					}
				}
				catch (System.ArgumentOutOfRangeException) {
					;
				}
				// Find cubemap textures
				int numUnderscores = 0;
				bool validnumber = false;
				for (int i = texture.Length - 1; i > 0; i--) {
					if (texture[i] <= '9' && texture[i] >= '0') {
						// Current is a number, start building string
						validnumber = true;
					} else {
						if (texture[i] == '-') {
							// Current is a minus sign (-).
							if (!validnumber) {
								break; // Make sure there's a number to add the minus sign to. If not, kill the loop.
							}
						} else {
							if (texture[i] == '_') {
								// Current is an underscore (_)
								if (validnumber) {
									// Make sure there is a number in the current string
									numUnderscores++; // before moving on to the next one.
									validnumber = false;
									if (numUnderscores == 3) {
										// If we've got all our numbers
										texture = texture.Substring(0, (i) - (0)); // Cut the texture string
										break; // Kill the loop, we're done
									}
								} else {
									// No number after the underscore
									break;
								}
							} else {
								// Not an acceptable character
								break;
							}
						}
					}
				}
			}
			Plane plane = inputData.Plane;
			Vector3D textureS = inputData.TextureS;
			Vector3D textureT = inputData.TextureT;
			double textureShiftS = inputData.TextureShiftS;
			double textureShiftT = inputData.TextureShiftT;
			double texScaleX = inputData.TexScaleX;
			double texScaleY = inputData.TexScaleY;
			if(Double.IsInfinity(texScaleX) || Double.IsNaN(texScaleX)) {
				texScaleX = 1;
			}
			if(Double.IsInfinity(texScaleY) || Double.IsNaN(texScaleY)) {
				texScaleY = 1;
			}
			if(Double.IsInfinity(textureShiftS) || Double.IsNaN(textureShiftS)) {
				textureShiftS = 0;
			}
			if(Double.IsInfinity(textureShiftT) || Double.IsNaN(textureShiftT)) {
				textureShiftT = 0;
			}
			if(Double.IsInfinity(textureS.X) || Double.IsNaN(textureS.X) || Double.IsInfinity(textureS.Y) || Double.IsNaN(textureS.Y) || Double.IsInfinity(textureS.Z) || Double.IsNaN(textureS.Z)) {
				textureS = TexInfo.textureAxisFromPlane(inputData.Plane)[0];
			}
			if(Double.IsInfinity(textureT.X) || Double.IsNaN(textureT.X) || Double.IsInfinity(textureT.Y) || Double.IsNaN(textureT.Y) || Double.IsInfinity(textureT.Z) || Double.IsNaN(textureT.Z)) {
				textureT = TexInfo.textureAxisFromPlane(inputData.Plane)[1];
			}
			if (Settings.roundNums)
			{
				return "( " + MAPMaker.Round(plane.A, 10) + " " + MAPMaker.Round(plane.B, 10) + " " + MAPMaker.Round(plane.C, 10) + " " + MAPMaker.Round(plane.Dist, 10) + " ) " + "( ( 1 0 " + MAPMaker.Round(textureShiftS, 10) + " ) ( 0 1 " + MAPMaker.Round(textureShiftT, 10) + " ) ) " + "\"" + texture + "\" 0 0 0";
			}
			else
			{
				return "( " + plane.A + " " + plane.B + " " + plane.C + " " + plane.Dist + " ) " + "( ( 1 0 " + textureShiftS + " ) ( 0 1 " + textureShiftT + " ) ) " + "\"" + texture + "\" 0 0 0";
			}
		} catch (System.NullReferenceException e) {
			DecompilerThread.OnMessage(this, "WARNING: Side with bad data! Not exported!");
			return null;
		}
	}
示例#30
0
	// The inspiration for this is the BSPSource source code. It's not a direct copy but does essentially
	// the same thing as the algorithm to "create a prism back", and isn't as neatly written.
	public static MAPBrush createBrushFromWind(Vector3D[] froms, Vector3D[] tos, string texture, string backtex, TexInfo scaling)
	{
		Vector3D[] planepts = new Vector3D[3];
		MAPBrushSide[] sides = new MAPBrushSide[froms.Length + 2]; // Each edge, plus a front and back side
		planepts[0] = froms[0];
		planepts[1] = tos[0];
		planepts[2] = tos[1];
		Plane plane = new Plane(planepts);
		Vector3D reverseNormal = plane.Normal;
		sides[0] = new MAPBrushSide(planepts, texture, scaling.SAxis.Point, scaling.SShift, scaling.TAxis.Point, scaling.TShift, 0, 1, 1, 0, "wld_lightmap", 16, 0);
		
		Vector3D[] backplanepts = new Vector3D[3];
		backplanepts[0] = froms[0]-(reverseNormal);
		backplanepts[1] = tos[1]-(reverseNormal);
		backplanepts[2] = tos[0]-(reverseNormal);
		Plane backplane = new Plane(backplanepts);
		Vector3D[] backaxes = TexInfo.textureAxisFromPlane(backplane);
		sides[1] = new MAPBrushSide(backplane, backtex, backaxes[0].Point, 0, backaxes[1].Point, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
		
		for (int i = 0; i < froms.Length; i++)
		{
			// each edge
			Vector3D[] sideplanepts = new Vector3D[3];
			sideplanepts[0] = froms[i];
			sideplanepts[1] = tos[i];
			sideplanepts[2] = froms[i]+(reverseNormal);
			Plane sideplane = new Plane(sideplanepts);
			Vector3D[] sideaxes = TexInfo.textureAxisFromPlane(sideplane);
			sides[i + 2] = new MAPBrushSide(sideplane, backtex, sideaxes[0].Point, 0, sideaxes[1].Point, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
		}
		
		return new MAPBrush(sides, 0, 0, false);
	}
	private string brushSideToString(MAPBrushSide inputData, bool isDetail) {
		try {
			Vector3D[] triangle = inputData.Triangle;
			string texture = inputData.Texture;
			Vector3D textureS = inputData.TextureS;
			Vector3D textureT = inputData.TextureT;
			double textureShiftS = inputData.TextureShiftS;
			double textureShiftT = inputData.TextureShiftT;
			float texRot = inputData.TexRot;
			double texScaleX = inputData.TexScaleX;
			double texScaleY = inputData.TexScaleY;
			int flags = inputData.Flags;
			string material = inputData.Material;
			double lgtScale = inputData.LgtScale;
			double lgtRot = inputData.LgtRot;
			string temp = "";
			// Correct textures here
			try
			{
				if (texture.Substring(0, (9) - (0)).ToUpper().Equals("textures/".ToUpper()))
				{
					texture = texture.Substring(9);
				}
			}
			catch (System.ArgumentOutOfRangeException)
			{
				;
			}
			if (BSPVersion == mapType.TYPE_NIGHTFIRE || BSPVersion == mapType.TYPE_DOOM || BSPVersion == mapType.TYPE_HEXEN)
			{
				if (texture.ToUpper().Equals("special/nodraw".ToUpper()) || texture.ToUpper().Equals("special/null".ToUpper()))
				{
					texture = "common/nodraw";
				}
				else
				{
					if (texture.ToUpper().Equals("special/clip".ToUpper()))
					{
						texture = "common/clip";
					}
					else
					{
						if (texture.ToUpper().Equals("special/sky".ToUpper()))
						{
							texture = "common/skyportal";
						}
						else
						{
							if (texture.ToUpper().Equals("special/trigger".ToUpper()))
							{
								texture = "common/trigger";
							}
							else
							{
								if (texture.ToUpper().Equals("special/playerclip".ToUpper()))
								{
									texture = "common/playerclip";
								}
								else
								{
									if (texture.ToUpper().Equals("special/npcclip".ToUpper()) || texture.ToUpper().Equals("special/enemyclip".ToUpper()))
									{
										texture = "common/tankclip";
									}
								}
							}
						}
					}
				}
			}
			else
			{
				if (BSPVersion == mapType.TYPE_QUAKE2)
				{
					try
					{
						if (texture.ToUpper().Equals("special/hint".ToUpper()))
						{
							texture = "common/hint";
						}
						else
						{
							if (texture.ToUpper().Equals("special/skip".ToUpper()))
							{
								texture = "common/skip";
							}
							else
							{
								if (texture.ToUpper().Equals("special/sky".ToUpper()))
								{
									texture = "common/skyportal";
								}
								else
								{
									if (texture.Substring(texture.Length - 8).ToUpper().Equals("/trigger".ToUpper()))
									{
										texture = "common/trigger";
									}
									else
									{
										if (texture.Substring(texture.Length - 5).ToUpper().Equals("/clip".ToUpper()))
										{
											texture = "common/clip";
										}
									}
								}
							}
						}
					}
					catch (System.ArgumentOutOfRangeException e)
					{
						;
					}
				}
				else
				{
					if (BSPVersion == mapType.TYPE_SOURCE17 || BSPVersion == mapType.TYPE_SOURCE18 || BSPVersion == mapType.TYPE_SOURCE19 || BSPVersion == mapType.TYPE_SOURCE20 || BSPVersion == mapType.TYPE_SOURCE21 || BSPVersion == mapType.TYPE_SOURCE22 || BSPVersion == mapType.TYPE_SOURCE23 || BSPVersion == mapType.TYPE_DMOMAM || BSPVersion == mapType.TYPE_VINDICTUS || BSPVersion == mapType.TYPE_TACTICALINTERVENTION)
					{
						try
						{
							if (texture.Substring(0, (5) - (0)).ToUpper().Equals("maps/".ToUpper()))
							{
								texture = texture.Substring(5);
								for (int i = 0; i < texture.Length; i++)
								{
									if (texture[i] == '/')
									{
										texture = texture.Substring(i + 1);
										break;
									}
								}
							}
						}
						catch (System.ArgumentOutOfRangeException e)
						{
							;
						}
						// Find cubemap textures
						int numUnderscores = 0;
						bool validnumber = false;
						for (int i = texture.Length - 1; i > 0; i--)
						{
							if (texture[i] <= '9' && texture[i] >= '0')
							{
								// Current is a number, start building string
								validnumber = true;
							}
							else
							{
								if (texture[i] == '-')
								{
									// Current is a minus sign (-).
									if (!validnumber)
									{
										break; // Make sure there's a number to add the minus sign to. If not, kill the loop.
									}
								}
								else
								{
									if (texture[i] == '_')
									{
										// Current is an underscore (_)
										if (validnumber)
										{
											// Make sure there is a number in the current string
											numUnderscores++; // before moving on to the next one.
											validnumber = false;
											if (numUnderscores == 3)
											{
												// If we've got all our numbers
												texture = texture.Substring(0, (i) - (0)); // Cut the texture string
												break; // Kill the loop, we're done
											}
										}
										else
										{
											// No number after the underscore
											break;
										}
									}
									else
									{
										// Not an acceptable character
										break;
									}
								}
							}
						}
					}
				}
			}
			if(Double.IsInfinity(texScaleX) || Double.IsNaN(texScaleX)) {
				texScaleX = 1;
			}
			if(Double.IsInfinity(texScaleY) || Double.IsNaN(texScaleY)) {
				texScaleY = 1;
			}
			if(Double.IsInfinity(textureShiftS) || Double.IsNaN(textureShiftS)) {
				textureShiftS = 0;
			}
			if(Double.IsInfinity(textureShiftT) || Double.IsNaN(textureShiftT)) {
				textureShiftT = 0;
			}
			if(Double.IsInfinity(textureS.X) || Double.IsNaN(textureS.X) || Double.IsInfinity(textureS.Y) || Double.IsNaN(textureS.Y) || Double.IsInfinity(textureS.Z) || Double.IsNaN(textureS.Z)) {
				textureS = TexInfo.textureAxisFromPlane(inputData.Plane)[0];
			}
			if(Double.IsInfinity(textureT.X) || Double.IsNaN(textureT.X) || Double.IsInfinity(textureT.Y) || Double.IsNaN(textureT.Y) || Double.IsInfinity(textureT.Z) || Double.IsNaN(textureT.Z)) {
				textureT = TexInfo.textureAxisFromPlane(inputData.Plane)[1];
			}
			if (Settings.roundNums) {
				temp = "( " + MAPMaker.Round(triangle[0].X, 6) + 
				       " " + MAPMaker.Round(triangle[0].Y, 6) + 
				       " " + MAPMaker.Round(triangle[0].Z, 6) + " ) " + 
				       "( " + MAPMaker.Round(triangle[1].X, 6) + 
				       " " + MAPMaker.Round(triangle[1].Y, 6) + 
				       " " + MAPMaker.Round(triangle[1].Z, 6) + " ) " + 
				       "( " + MAPMaker.Round(triangle[2].X, 6) + 
				       " " + MAPMaker.Round(triangle[2].Y, 6) + 
				       " " + MAPMaker.Round(triangle[2].Z, 6) + " ) " + 
				       texture + " " + System.Math.Floor(textureShiftS) + " " + System.Math.Floor(textureShiftT) + " " + 
				       MAPMaker.FormattedRound(texRot, 2, "######0.00") + " " + 
				       MAPMaker.Round(texScaleX, 6) + " " + 
				       MAPMaker.Round(texScaleY, 6) + " " + flags + " 0 0 ";
			} else {
				temp = "( " + triangle[0].X + " " + triangle[0].Y + " " + triangle[0].Z + " ) " + "( " + triangle[1].X + " " + triangle[1].Y + " " + triangle[1].Z + " ) " + "( " + triangle[2].X + " " + triangle[2].Y + " " + triangle[2].Z + " ) " + texture + " " + textureShiftS + " " + textureShiftT + " " + texRot + " " + texScaleX + " " + texScaleY + " " + flags + " 0 0 ";
			}
			if (isDetail) {
				temp += "+surfaceparm detail ";
			}
			return temp;
		}
		catch (System.NullReferenceException e)
		{
			DecompilerThread.OnMessage(this, "WARNING: Side with bad data! Not exported!");
			return "";
		}
	}
示例#32
0
	private string brushSideToString(MAPBrushSide inputData)
	{
		try
		{
			Vector3D[] triangle = inputData.Triangle;
			string texture = inputData.Texture;
			Vector3D textureS = inputData.TextureS;
			Vector3D textureT = inputData.TextureT;
			double textureShiftS = inputData.TextureShiftS;
			double textureShiftT = inputData.TextureShiftT;
			float texRot = inputData.TexRot;
			double texScaleX = inputData.TexScaleX;
			double texScaleY = inputData.TexScaleY;
			int flags = inputData.Flags;
			string material = inputData.Material;
			double lgtScale = inputData.LgtScale;
			double lgtRot = inputData.LgtRot;
			// Correct special textures on Q2 maps
			if (!Settings.noTexCorrection)
			{
				if (BSPVersion == mapType.TYPE_QUAKE2 || BSPVersion == mapType.TYPE_SIN)
				{
					// Many of the special textures are taken care of in the decompiler method itself
					try
					{
						// using face flags, rather than texture names.
						if (texture.Substring(texture.Length - 8).ToUpper().Equals("/trigger".ToUpper()))
						{
							texture = "special/trigger";
						}
						else
						{
							if (texture.Substring(texture.Length - 5).ToUpper().Equals("/clip".ToUpper()))
							{
								texture = "special/clip";
							}
							else
							{
								if (texture.ToUpper().Equals("*** unsused_texinfo ***".ToUpper()))
								{
									texture = "special/nodraw";
								}
							}
						}
					}
					catch (System.ArgumentOutOfRangeException)
					{
						;
					}
				}
				if (BSPVersion == mapType.TYPE_SOURCE17 || BSPVersion == mapType.TYPE_SOURCE18 || BSPVersion == mapType.TYPE_SOURCE19 || BSPVersion == mapType.TYPE_SOURCE20 || BSPVersion == mapType.TYPE_SOURCE21 || BSPVersion == mapType.TYPE_SOURCE22 || BSPVersion == mapType.TYPE_SOURCE23 || BSPVersion == mapType.TYPE_DMOMAM || BSPVersion == mapType.TYPE_VINDICTUS || BSPVersion == mapType.TYPE_TACTICALINTERVENTION)
				{
					try
					{
						if (texture.ToUpper().Equals("tools/toolshint".ToUpper()))
						{
							texture = "special/hint";
						}
						else
						{
							if (texture.ToUpper().Equals("tools/toolsskip".ToUpper()))
							{
								texture = "special/skip";
							}
							else
							{
								if (texture.ToUpper().Equals("tools/toolsclip".ToUpper()))
								{
									texture = "special/clip";
								}
								else
								{
									if (texture.ToUpper().Equals("tools/toolstrigger".ToUpper()) || texture.ToUpper().Equals("TOOLS/TOOLSFOG".ToUpper()))
									{
										texture = "special/trigger";
									}
									else
									{
										if (texture.ToUpper().Equals("tools/TOOLSSKYBOX".ToUpper()))
										{
											texture = "special/sky";
										}
										else
										{
											if (texture.ToUpper().Equals("tools/toolsnodraw".ToUpper()))
											{
												texture = "special/nodraw";
											}
											else
											{
												if (texture.ToUpper().Equals("TOOLS/TOOLSPLAYERCLIP".ToUpper()))
												{
													texture = "special/playerclip";
												}
												else
												{
													if (texture.ToUpper().Equals("TOOLS/TOOLSNPCCLIP".ToUpper()))
													{
														texture = "special/enemyclip";
													}
													else
													{
														if (texture.ToUpper().Equals("TOOLS/TOOLSBLACK".ToUpper()))
														{
															texture = "special/black";
														}
														else
														{
															if (texture.ToUpper().Equals("TOOLS/TOOLSINVISIBLE".ToUpper()))
															{
																texture = "special/clip";
															}
															else
															{
																if (texture.Substring(0, (5) - (0)).ToUpper().Equals("maps/".ToUpper()))
																{
																	texture = texture.Substring(5);
																	for (int i = 0; i < texture.Length; i++)
																	{
																		if (texture[i] == '/')
																		{
																			texture = texture.Substring(i + 1);
																			break;
																		}
																	}
																}
																// Find cubemap textures
																int numUnderscores = 0;
																bool validnumber = false;
																for (int i = texture.Length - 1; i > 0; i--)
																{
																	if (texture[i] <= '9' && texture[i] >= '0')
																	{
																		// Current is a number, start building string
																		validnumber = true;
																	}
																	else
																	{
																		if (texture[i] == '-')
																		{
																			// Current is a minus sign (-).
																			if (!validnumber)
																			{
																				break; // Make sure there's a number to add the minus sign to. If not, kill the loop.
																			}
																		}
																		else
																		{
																			if (texture[i] == '_')
																			{
																				// Current is an underscore (_)
																				if (validnumber)
																				{
																					// Make sure there is a number in the current string
																					numUnderscores++; // before moving on to the next one.
																					validnumber = false;
																					if (numUnderscores == 3)
																					{
																						// If we've got all our numbers
																						texture = texture.Substring(0, (i) - (0)); // Cut the texture string
																						break; // Kill the loop, we're done
																					}
																				}
																				else
																				{
																					// No number after the underscore
																					break;
																				}
																			}
																			else
																			{
																				// Not an acceptable character
																				break;
																			}
																		}
																	}
																}
															}
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
					catch (System.ArgumentOutOfRangeException)
					{
						;
					}
				}
				if (BSPVersion == mapType.TYPE_QUAKE3 || BSPVersion == mapType.TYPE_MOHAA || BSPVersion == mapType.TYPE_COD || BSPVersion == mapType.TYPE_STEF2 || BSPVersion == mapType.TYPE_STEF2DEMO || BSPVersion == mapType.TYPE_FAKK)
				{
					try
					{
						if (texture.Substring(0, (9) - (0)).ToUpper().Equals("textures/".ToUpper()))
						{
							texture = texture.Substring(9);
						}
					}
					catch (System.ArgumentOutOfRangeException)
					{
						;
					}
					if (texture.ToUpper().Equals("common/clip".ToUpper()))
					{
						texture = "special/clip";
					}
					else
					{
						if (texture.ToUpper().Equals("common/trigger".ToUpper()))
						{
							texture = "special/trigger";
						}
						else
						{
							if (texture.ToUpper().Equals("noshader".ToUpper()))
							{
								texture = "special/nodraw";
							}
							else
							{
								if (texture.ToUpper().Equals("common/physics_clip".ToUpper()))
								{
									texture = "special/clip";
								}
								else
								{
									if (texture.ToUpper().Equals("common/caulk".ToUpper()) || texture.ToUpper().Equals("common/caulkshadow".ToUpper()))
									{
										texture = "special/nodraw";
									}
									else
									{
										if (texture.ToUpper().Equals("common/do_not_enter".ToUpper()) || texture.ToUpper().Equals("common/donotenter".ToUpper()) || texture.ToUpper().Equals("common/monsterclip".ToUpper()))
										{
											texture = "special/npcclip";
										}
										else
										{
											if (texture.ToUpper().Equals("common/caulksky".ToUpper()) || texture.ToUpper().Equals("common/skyportal".ToUpper()))
											{
												texture = "special/sky";
											}
											else
											{
												if (texture.ToUpper().Equals("common/hint".ToUpper()))
												{
													texture = "special/hint";
												}
												else
												{
													if (texture.ToUpper().Equals("common/nodraw".ToUpper()))
													{
														texture = "special/nodraw";
													}
													else
													{
														if (texture.ToUpper().Equals("common/metalclip".ToUpper()))
														{
															texture = "special/clip";
														}
														else
														{
															if (texture.ToUpper().Equals("common/grassclip".ToUpper()))
															{
																texture = "special/clip";
															}
															else
															{
																if (texture.ToUpper().Equals("common/paperclip".ToUpper()))
																{
																	texture = "special/clip";
																}
																else
																{
																	if (texture.ToUpper().Equals("common/woodclip".ToUpper()))
																	{
																		texture = "special/clip";
																	}
																	else
																	{
																		if (texture.ToUpper().Equals("common/waterskip".ToUpper()))
																		{
																			texture = "liquids/!water";
																		}
																		else
																		{
																			if (texture.ToUpper().Equals("common/glassclip".ToUpper()))
																			{
																				texture = "special/clip";
																			}
																			else
																			{
																				if (texture.ToUpper().Equals("common/playerclip".ToUpper()))
																				{
																					texture = "special/playerclip";
																				}
																				else
																				{
																					if (texture.ToUpper().Equals("common/nodrawnonsolid".ToUpper()))
																					{
																						texture = "special/trigger";
																					}
																					else
																					{
																						if (texture.ToUpper().Equals("common/clipfoliage".ToUpper()))
																						{
																							texture = "special/clip";
																						}
																						else
																						{
																							if (texture.ToUpper().Equals("common/foliageclip".ToUpper()))
																							{
																								texture = "special/clip";
																							}
																							else
																							{
																								if (texture.ToUpper().Equals("common/carpetclip".ToUpper()))
																								{
																									texture = "special/clip";
																								}
																								else
																								{
																									if (texture.ToUpper().Equals("common/dirtclip".ToUpper()))
																									{
																										texture = "special/clip";
																									}
																									else
																									{
																										try
																										{
																											if (texture.Substring(0, (4) - (0)).ToUpper().Equals("sky/".ToUpper()))
																											{
																												texture = "special/sky";
																											}
																										}
																										catch (System.ArgumentOutOfRangeException)
																										{
																											; // I couldn't give a f**k
																										}
																									}
																								}
																							}
																						}
																					}
																				}
																			}
																		}
																	}
																}
															}
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
				if (BSPVersion == mapType.TYPE_RAVEN)
				{
					try
					{
						if (texture.Substring(0, (9) - (0)).ToUpper().Equals("textures/".ToUpper()))
						{
							texture = texture.Substring(9);
						}
					}
					catch (System.ArgumentOutOfRangeException)
					{
						;
					}
					if (texture.ToUpper().Equals("system/clip".ToUpper()))
					{
						texture = "special/clip";
					}
					else
					{
						if (texture.ToUpper().Equals("system/trigger".ToUpper()))
						{
							texture = "special/trigger";
						}
						else
						{
							if (texture.ToUpper().Equals("noshader".ToUpper()))
							{
								texture = "special/nodraw";
							}
							else
							{
								if (texture.ToUpper().Equals("system/physics_clip".ToUpper()))
								{
									texture = "special/clip";
								}
								else
								{
									if (texture.ToUpper().Equals("system/caulk".ToUpper()))
									{
										texture = "special/nodraw";
									}
									else
									{
										if (texture.ToUpper().Equals("system/do_not_enter".ToUpper()))
										{
											texture = "special/nodraw";
										}
									}
								}
							}
						}
					}
				}
			}
			if(Double.IsInfinity(texScaleX) || Double.IsNaN(texScaleX)) {
				texScaleX = 1;
			}
			if(Double.IsInfinity(texScaleY) || Double.IsNaN(texScaleY)) {
				texScaleY = 1;
			}
			if(Double.IsInfinity(textureShiftS) || Double.IsNaN(textureShiftS)) {
				textureShiftS = 0;
			}
			if(Double.IsInfinity(textureShiftT) || Double.IsNaN(textureShiftT)) {
				textureShiftT = 0;
			}
			if(Double.IsInfinity(textureS.X) || Double.IsNaN(textureS.X) || Double.IsInfinity(textureS.Y) || Double.IsNaN(textureS.Y) || Double.IsInfinity(textureS.Z) || Double.IsNaN(textureS.Z)) {
				textureS = TexInfo.textureAxisFromPlane(inputData.Plane)[0];
			}
			if(Double.IsInfinity(textureT.X) || Double.IsNaN(textureT.X) || Double.IsInfinity(textureT.Y) || Double.IsNaN(textureT.Y) || Double.IsInfinity(textureT.Z) || Double.IsNaN(textureT.Z)) {
				textureT = TexInfo.textureAxisFromPlane(inputData.Plane)[1];
			}
			if (Settings.roundNums)
			{
				return "( " + MAPMaker.FormattedRound(triangle[0].X, 6, "######0.000000") + " " + MAPMaker.FormattedRound(triangle[0].Y, 6, "######0.000000") + " " + MAPMaker.FormattedRound(triangle[0].Z, 6, "######0.000000") + " ) " + 
				       "( " + MAPMaker.FormattedRound(triangle[1].X, 6, "######0.000000") + " " + MAPMaker.FormattedRound(triangle[1].Y, 6, "######0.000000") + " " + MAPMaker.FormattedRound(triangle[1].Z, 6, "######0.000000") + " ) " + 
				       "( " + MAPMaker.FormattedRound(triangle[2].X, 6, "######0.000000") + " " + MAPMaker.FormattedRound(triangle[2].Y, 6, "######0.000000") + " " + MAPMaker.FormattedRound(triangle[2].Z, 6, "######0.000000") + " ) " + 
				       texture + 
				       " [ " + MAPMaker.FormattedRound(textureS.X, 6, "######0.000000") + " " + MAPMaker.FormattedRound(textureS.Y, 6, "######0.000000") + " " + MAPMaker.FormattedRound(textureS.Z, 6, "######0.000000") + " " + MAPMaker.Round(textureShiftS) + " ]" + 
				       " [ " + MAPMaker.FormattedRound(textureT.X, 6, "######0.000000") + " " + MAPMaker.FormattedRound(textureT.Y, 6, "######0.000000") + " " + MAPMaker.FormattedRound(textureT.Z, 6, "######0.000000") + " " + MAPMaker.Round(textureShiftT) + " ] " + 
				       MAPMaker.FormattedRound(texRot, 4, "######0.####") + " " + MAPMaker.FormattedRound(texScaleX, 4, "######0.####") + " " + MAPMaker.FormattedRound(texScaleY, 4, "######0.####") + " " + flags + " " + material + " [ " + MAPMaker.FormattedRound(lgtScale, 4, "######0.####") + " " + MAPMaker.FormattedRound(lgtRot, 4, "######0.####") + " ]";
			}
			else
			{
				return "( " + triangle[0].X + " " + triangle[0].Y + " " + triangle[0].Z + " ) " + "( " + triangle[1].X + " " + triangle[1].Y + " " + triangle[1].Z + " ) " + "( " + triangle[2].X + " " + triangle[2].Y + " " + triangle[2].Z + " ) " + texture + " [ " + textureS.X + " " + textureS.Y + " " + textureS.Z + " " + textureShiftS + " ]" + " [ " + textureT.X + " " + textureT.Y + " " + textureT.Z + " " + textureShiftT + " ] " + texRot + " " + texScaleX + " " + texScaleY + " " + flags + " " + material + " [ " + lgtScale + " " + lgtRot + " ]";
			}
		}
		catch (System.NullReferenceException)
		{
			DecompilerThread.OnMessage(this, "WARNING: Side with bad data! Not exported!");
			return "";
		}
	}
示例#33
0
	// createFaceBrush(String, String, Vector3D, Vector3D)
	// This creates a rectangular brush. The String is assumed to be a texture for a face, and
	// the two vectors are a bounding box to create a plane with (mins-maxs).
	// The second String is the texture to apply to all other sides.
	public static MAPBrush createFaceBrush(string texture, string backTexture, Vector3D mins, Vector3D maxs, double xoff, double yoff, bool lowerUnpegged, int shiftYCieling, int shiftYFloor)
	{
		//DecompilerThread.OnMessage(this, "Creating brush for face with " + texture);
		MAPBrush newBrush = new MAPBrush(0, 0, false);
		Vector3D[][] planes = new Vector3D[6][];
		for (int i = 0; i < 6; i++)
		{
			planes[i] = new Vector3D[3];
		} // Six planes for a cube brush, three vertices for each plane
		double[][] texS = new double[6][];
		for (int i2 = 0; i2 < 6; i2++)
		{
			texS[i2] = new double[3];
		}
		double[][] texT = new double[6][];
		for (int i3 = 0; i3 < 6; i3++)
		{
			texT[i3] = new double[3];
		}
		
		double sideLengthXY = System.Math.Sqrt(System.Math.Pow(mins.X - maxs.X, 2) + System.Math.Pow(mins.Y - maxs.Y, 2));
		Vector3D diffVec1 = new Vector3D(mins.X, mins.Y, maxs.Z)-mins;
		Vector3D diffVec2 = new Vector3D(maxs.X, maxs.Y, mins.Z)-mins;
		Vector3D cross = diffVec2^diffVec1;
		cross.normalize();
		
		// Face
		planes[0][0] = new Vector3D(mins.X, mins.Y, maxs.Z);
		planes[0][1] = new Vector3D(maxs.X, maxs.Y, mins.Z);
		planes[0][2] = mins;
		texS[0][0] = (- (mins.X - maxs.X)) / sideLengthXY;
		texS[0][1] = (- (mins.Y - maxs.Y)) / sideLengthXY;
		texT[0][2] = - 1;
		// To be fair, to find these properly you ought to take the dot product of these over the length.
		// However, length is always one, and there's only two components (the third sum would turn out to be 0)
		double SShift = xoff - (texS[0][0] * mins.X) - (texS[0][1] * mins.Y);
		double TShift = yoff;
		// One sided linedefs (which this usually makes walls for) are only affected by lower unpegged. Upper is
		// always assumed unless lower is true.
		if (lowerUnpegged)
		{
			TShift += shiftYFloor;
		}
		else
		{
			TShift += shiftYCieling;
		}
		// Far
		planes[1][0] = new Vector3D(mins.X, mins.Y, maxs.Z)-(cross);
		planes[1][1] = mins-(cross);
		planes[1][2] = new Vector3D(maxs.X, maxs.Y, mins.Z)-(cross);
		texS[1][0] = texS[0][0];
		texS[1][1] = texS[0][1];
		texT[1][2] = - 1;
		// Top
		planes[2][0] = new Vector3D(mins.X, mins.Y, maxs.Z);
		planes[2][1] = new Vector3D(mins.X, mins.Y, maxs.Z)-(cross);
		planes[2][2] = maxs;
		texS[2][0] = 1;
		texT[2][1] = 1;
		// Bottom
		planes[3][0] = mins;
		planes[3][1] = new Vector3D(maxs.X, maxs.Y, mins.Z);
		planes[3][2] = new Vector3D(maxs.X, maxs.Y, mins.Z)-(cross);
		texS[3][0] = 1;
		texT[3][1] = 1;
		// Left
		planes[4][0] = mins;
		planes[4][1] = mins-cross;
		planes[4][2] = new Vector3D(mins.X, mins.Y, maxs.Z);
		texS[4][0] = texS[0][1];
		texS[4][1] = texS[0][0];
		texT[4][2] = 1;
		// Right
		planes[5][0] = maxs;
		planes[5][1] = maxs-cross;
		planes[5][2] = new Vector3D(maxs.X, maxs.Y, mins.Z);
		texS[5][0] = texS[0][1];
		texS[5][1] = texS[0][0];
		texT[5][2] = 1;
		
		MAPBrushSide front = new MAPBrushSide(planes[0], texture, texS[0], SShift, texT[0], TShift, 0, 1, 1, 0, "wld_lightmap", 16, 0);
		newBrush.add(front);
		for (int i = 1; i < 6; i++)
		{
			newBrush.add(new MAPBrushSide(planes[i], backTexture, texS[i], 0, texT[i], 0, 0, 1, 1, 32, "wld_lightmap", 16, 0));
		}
		
		return newBrush;
	}
    // METHODS

    // write()
    // Saves the lump to the specified path.
    // Handling file I/O with Strings is generally a bad idea. If you have maybe a couple hundred
    // Strings to write then it'll probably be okay, but when you have on the order of 10,000 Strings
    // it gets VERY slow, even if you concatenate them all before writing.
    public virtual void write()
    {
        // Preprocessing entity corrections
        if (!Settings.noEntCorrection)
        {
            if (BSPVersion == mapType.TYPE_NIGHTFIRE)
            {
                for (int i = 1; i < data.Count; i++)
                {
                    for (int j = 0; j < data[i].Brushes.Count; j++)
                    {
                        if (data[i].Brushes[j].Water)
                        {
                            MAPBrush currentBrush = data[i].Brushes[j];
                            for (int k = 0; k < currentBrush.NumSides; k++)
                            {
                                MAPBrushSide currentBrushSide = currentBrush[k];
                                if (currentBrushSide.Plane.Normal.Equals(Vector3D.UP) || currentBrushSide.Plane.Normal.Equals(-Vector3D.UP))
                                {
                                    currentBrushSide.Texture = "test/omaha_pjspick5";
                                }
                                else
                                {
                                    currentBrushSide.Texture = "common/nodraw";
                                }
                            }
                            data[0].Brushes.Add(data[i].Brushes[j]);
                            // TODO: Textures on this brush
                        }
                    }
                    if (data[i]["classname"].Equals("func_water", StringComparison.InvariantCultureIgnoreCase))
                    {
                        data.RemoveAt(i);
                        i--;
                    }
                }
            }
            // Correct some attributes of entities
            for (int i = 0; i < data.Count; i++)
            {
                Entity current = data[i];
                switch (BSPVersion)
                {
                case mapType.TYPE_NIGHTFIRE:                          // Nightfire
                    current = ent42ToEntRad(current);
                    break;

                case mapType.TYPE_QUAKE2:
                    current = ent38ToEntRad(current);
                    break;

                case mapType.TYPE_DOOM:
                case mapType.TYPE_HEXEN:
                    break;
                }
            }
        }

        byte[][] entityBytes = new byte[data.Count][];
        int      totalLength = 0;

        for (currentEntity = 0; currentEntity < data.Count; currentEntity++)
        {
            try {
                entityBytes[currentEntity] = entityToByteArray(data[currentEntity], currentEntity);
            } catch (System.IndexOutOfRangeException) {
                // This happens when entities are added after the array is made
                byte[][] newList = new byte[data.Count][];                 // Create a new array with the new length
                for (int j = 0; j < entityBytes.Length; j++)
                {
                    newList[j] = entityBytes[j];
                }
                newList[currentEntity] = entityToByteArray(data[currentEntity], currentEntity);
                entityBytes            = newList;
            }
            totalLength += entityBytes[currentEntity].Length;
        }
        byte[] allEnts = new byte[totalLength];
        int    offset  = 0;

        for (int i = 0; i < data.Count; i++)
        {
            for (int j = 0; j < entityBytes[i].Length; j++)
            {
                allEnts[offset + j] = entityBytes[i][j];
            }
            offset += entityBytes[i].Length;
        }
        MAPMaker.write(allEnts, path, false);
    }
	// METHODS
	
	// +decompile()
	// Attempts to convert a map in a Doom WAD into a usable .MAP file. This has many
	// challenges, not the least of which is the fact that the Doom engine didn't use
	// brushes (at least, not in any sane way).
	public virtual Entities decompile()
	{
		DecompilerThread.OnMessage(this, "Decompiling...");
		DecompilerThread.OnMessage(this, doomMap.MapName);
		
		mapFile = new Entities();
		Entity world = new Entity("worldspawn");
		mapFile.Add(world);
		
		string[] lowerWallTextures = new string[doomMap.Sidedefs.Count];
		string[] midWallTextures = new string[doomMap.Sidedefs.Count];
		string[] higherWallTextures = new string[doomMap.Sidedefs.Count];
		
		short[] sectorTag = new short[doomMap.Sectors.Count];
		string playerStartOrigin = "";
		
		// Since Doom relied on sectors to define a cieling and floor height, and nothing else,
		// need to find the minimum and maximum used Z values. This is because the Doom engine
		// is only a pseudo-3D engine. For all it cares, the cieling and floor extend to their
		// respective infinities. For a GC/Hammer map, however, this cannot be the case.
		int ZMin = 32767; // Even though the values in the map will never exceed these, use ints here to avoid
		int ZMax = - 32768; // overflows, in case the map DOES go within 32 units of these values.
		for (int i = 0; i < doomMap.Sectors.Count; i++)
		{
			DSector currentSector = doomMap.Sectors[i];
			sectorTag[i] = currentSector.Tag;
			if (currentSector.FloorHeight < ZMin + 32)
			{
				ZMin = currentSector.FloorHeight - 32; // Can't use the actual value, because that IS the floor
			}
			else
			{
				if (currentSector.CielingHeight > ZMax - 32)
				{
					ZMax = currentSector.CielingHeight + 32; // or the cieling. Subtract or add a sane value to it.
				}
			}
		}
		
		// I need to analyze the binary tree and get more information, particularly the
		// parent nodes of each subsector and node, as well as whether it's the right or
		// left child of that node. These are extremely important, as the parent defines
		// boundaries for the children, as well as inheriting further boundaries from its
		// parents. These boundaries are invaluable for forming brushes.
		int[] nodeparents = new int[doomMap.Nodes.Count];
		bool[] nodeIsLeft = new bool[doomMap.Nodes.Count];
		
		for (int i = 0; i < doomMap.Nodes.Count; i++)
		{
			nodeparents[i] = - 1; // There should only be one node left with -1 as a parent. This SHOULD be the root.
			for (int j = 0; j < doomMap.Nodes.Count; j++)
			{
				if (doomMap.Nodes[j].Child1 == i)
				{
					nodeparents[i] = j;
					break;
				}
				else
				{
					if (doomMap.Nodes[j].Child2 == i)
					{
						nodeparents[i] = j;
						nodeIsLeft[i] = true;
						break;
					}
				}
			}
		}
		
		// Keep a list of what subsectors belong to which sector
		int[] subsectorSectors = new int[doomMap.SubSectors.Count];
		// Keep a list of what sidedefs belong to what subsector as well
		int[][] subsectorSidedefs = new int[doomMap.SubSectors.Count][];
		
		short[][] sideDefShifts = new short[2][];
		for (int i2 = 0; i2 < 2; i2++)
		{
			sideDefShifts[i2] = new short[doomMap.Sidedefs.Count];
		}
		
		// Figure out what sector each subsector belongs to, and what node is its parent.
		// Depending on sector "tags" this will help greatly in creation of brushbased entities,
		// and also helps in finding subsector floor and cieling heights.
		int[] ssparents = new int[doomMap.SubSectors.Count];
		bool[] ssIsLeft = new bool[doomMap.SubSectors.Count];
		for (int i = 0; i < doomMap.SubSectors.Count; i++)
		{
			//DecompilerThread.OnMessage(this, "Populating texture lists for subsector " + i);
			// First, find the subsector's parent and whether it is the left or right child.
			ssparents[i] = - 1; // No subsector should have a -1 in here
			for (int j = 0; j < doomMap.Nodes.Count; j++)
			{
				// When a node references a subsector, it is not referenced by negative
				// index, as future BSP versions do. The bits 0-14 ARE the index, and
				// bit 15 (which is the sign bit in two's compliment math) determines
				// whether or not it is a node or subsector. Therefore, we need to add
				// 2^15 to the number to produce the actual index.
				if (doomMap.Nodes[j].Child1 + 32768 == i)
				{
					ssparents[i] = j;
					break;
				}
				else
				{
					if (doomMap.Nodes[j].Child2 + 32768 == i)
					{
						ssparents[i] = j;
						ssIsLeft[i] = true;
						break;
					}
				}
			}
			
			// Second, figure out what sector a subsector belongs to, and the type of sector it is.
			subsectorSectors[i] = - 1;
			Edge currentsubsector = doomMap.SubSectors[i];
			subsectorSidedefs[i] = new int[currentsubsector.NumSegs];
			for (int j = 0; j < currentsubsector.NumSegs; j++)
			{
				// For each segment the subsector references
				DSegment currentsegment = doomMap.Segments[currentsubsector.FirstSeg + j];
				DLinedef currentlinedef = doomMap.Linedefs[currentsegment.Linedef];
				int currentsidedefIndex;
				int othersideIndex;
				if (currentsegment.Direction == 0)
				{
					currentsidedefIndex = currentlinedef.Right;
					othersideIndex = currentlinedef.Left;
				}
				else
				{
					currentsidedefIndex = currentlinedef.Left;
					othersideIndex = currentlinedef.Right;
				}
				subsectorSidedefs[i][j] = currentsidedefIndex;
				DSidedef currentSidedef = doomMap.Sidedefs[currentsidedefIndex];
				if (currentlinedef.OneSided)
				{
					// A one-sided linedef should always be like this
					midWallTextures[currentsidedefIndex] = doomMap.WadName + "/" + currentSidedef.MidTexture;
					higherWallTextures[currentsidedefIndex] = "special/nodraw";
					lowerWallTextures[currentsidedefIndex] = "special/nodraw";
					sideDefShifts[X][currentsidedefIndex] = currentSidedef.OffsetX;
					sideDefShifts[Y][currentsidedefIndex] = currentSidedef.OffsetY;
				}
				else
				{
					// I don't really get why I need to apply these textures to the other side. But if it works I won't argue...
					if (!currentSidedef.MidTexture.Equals("-"))
					{
						midWallTextures[othersideIndex] = doomMap.WadName + "/" + currentSidedef.MidTexture;
					}
					else
					{
						midWallTextures[othersideIndex] = "special/nodraw";
					}
					if (!currentSidedef.HighTexture.Equals("-"))
					{
						higherWallTextures[othersideIndex] = doomMap.WadName + "/" + currentSidedef.HighTexture;
					}
					else
					{
						higherWallTextures[othersideIndex] = "special/nodraw";
					}
					if (!currentSidedef.LowTexture.Equals("-"))
					{
						lowerWallTextures[othersideIndex] = doomMap.WadName + "/" + currentSidedef.LowTexture;
					}
					else
					{
						lowerWallTextures[othersideIndex] = "special/nodraw";
					}
					sideDefShifts[X][othersideIndex] = currentSidedef.OffsetX;
					sideDefShifts[Y][othersideIndex] = currentSidedef.OffsetY;
				}
				// Sometimes a subsector seems to belong to more than one sector. Only the reference in the first seg is true.
				if (j == 0)
				{
					subsectorSectors[i] = currentSidedef.Sector;
				}
			}
		}
		bool[] linedefFlagsDealtWith = new bool[doomMap.Linedefs.Count];
		bool[] linedefSpecialsDealtWith = new bool[doomMap.Linedefs.Count];
		
		MAPBrush[][] sectorFloorBrushes = new MAPBrush[doomMap.Sectors.Count][];
		for (int i3 = 0; i3 < doomMap.Sectors.Count; i3++)
		{
			sectorFloorBrushes[i3] = new MAPBrush[0];
		}
		MAPBrush[][] sectorCielingBrushes = new MAPBrush[doomMap.Sectors.Count][];
		for (int i4 = 0; i4 < doomMap.Sectors.Count; i4++)
		{
			sectorCielingBrushes[i4] = new MAPBrush[0];
		}
		
		// For one-sided linedefs referenced by more than one subsector
		bool[] outsideBrushAlreadyCreated = new bool[doomMap.Linedefs.Count];
		
		int onePercent = (int)((doomMap.SubSectors.Count)/100);
		if(onePercent < 1) {
			onePercent = 1;
		}
		for (int i = 0; i < doomMap.SubSectors.Count; i++)
		{
			//DecompilerThread.OnMessage(this, "Creating brushes for subsector " + i);
			
			Edge currentsubsector = doomMap.SubSectors[i];
			
			// Third, create a few brushes out of the geometry.
			MAPBrush cielingBrush = new MAPBrush(numBrshs++, 0, false);
			MAPBrush floorBrush = new MAPBrush(numBrshs++, 0, false);
			MAPBrush midBrush = new MAPBrush(numBrshs++, 0, false);
			MAPBrush sectorTriggerBrush = new MAPBrush(numBrshs++, 0, false);
			DSector currentSector = doomMap.Sectors[subsectorSectors[i]];
			
			Vector3D[] roofPlane = new Vector3D[3];
			double[] roofTexS = new double[3];
			double[] roofTexT = new double[3];
			roofPlane[0] = new Vector3D(0, Settings.planePointCoef, ZMax);
			roofPlane[1] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, ZMax);
			roofPlane[2] = new Vector3D(Settings.planePointCoef, 0, ZMax);
			roofTexS[0] = 1;
			roofTexT[1] = - 1;
			
			Vector3D[] cileingPlane = new Vector3D[3];
			double[] cileingTexS = new double[3];
			double[] cileingTexT = new double[3];
			cileingPlane[0] = new Vector3D(0, 0, currentSector.CielingHeight);
			cileingPlane[1] = new Vector3D(Settings.planePointCoef, 0, currentSector.CielingHeight);
			cileingPlane[2] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.CielingHeight);
			cileingTexS[0] = 1;
			cileingTexT[1] = - 1;
			
			Vector3D[] floorPlane = new Vector3D[3];
			double[] floorTexS = new double[3];
			double[] floorTexT = new double[3];
			floorPlane[0] = new Vector3D(0, Settings.planePointCoef, currentSector.FloorHeight);
			floorPlane[1] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.FloorHeight);
			floorPlane[2] = new Vector3D(Settings.planePointCoef, 0, currentSector.FloorHeight);
			floorTexS[0] = 1;
			floorTexT[1] = - 1;
			
			Vector3D[] foundationPlane = new Vector3D[3];
			double[] foundationTexS = new double[3];
			double[] foundationTexT = new double[3];
			foundationPlane[0] = new Vector3D(0, 0, ZMin);
			foundationPlane[1] = new Vector3D(Settings.planePointCoef, 0, ZMin);
			foundationPlane[2] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, ZMin);
			foundationTexS[0] = 1;
			foundationTexT[1] = - 1;
			
			Vector3D[] topPlane = new Vector3D[3];
			double[] topTexS = new double[3];
			double[] topTexT = new double[3];
			topPlane[0] = new Vector3D(0, Settings.planePointCoef, currentSector.CielingHeight);
			topPlane[1] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.CielingHeight);
			topPlane[2] = new Vector3D(Settings.planePointCoef, 0, currentSector.CielingHeight);
			topTexS[0] = 1;
			topTexT[1] = - 1;
			
			Vector3D[] bottomPlane = new Vector3D[3];
			double[] bottomTexS = new double[3];
			double[] bottomTexT = new double[3];
			bottomPlane[0] = new Vector3D(0, 0, currentSector.FloorHeight);
			bottomPlane[1] = new Vector3D(Settings.planePointCoef, 0, currentSector.FloorHeight);
			bottomPlane[2] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.FloorHeight);
			bottomTexS[0] = 1;
			bottomTexT[1] = - 1;
			
			int nextNode = ssparents[i];
			bool leftSide = ssIsLeft[i];
			
			for (int j = 0; j < currentsubsector.NumSegs; j++)
			{
				// Iterate through the sidedefs defined by segments of this subsector
				DSegment currentseg = doomMap.Segments[currentsubsector.FirstSeg + j];
				Vector3D start = doomMap.Vertices[currentseg.StartVertex].Vector;
				Vector3D end = doomMap.Vertices[currentseg.EndVertex].Vector;
				DLinedef currentLinedef = doomMap.Linedefs[(int) currentseg.Linedef];
				
				Vector3D[] plane = new Vector3D[3];
				double[] texS = new double[3];
				double[] texT = new double[3];
				plane[0] = new Vector3D(start.X, start.Y, ZMin);
				plane[1] = new Vector3D(end.X, end.Y, ZMin);
				plane[2] = new Vector3D(end.X, end.Y, ZMax);
				
				Vector3D linestart = new Vector3D(doomMap.Vertices[currentLinedef.Start].Vector.X, doomMap.Vertices[currentLinedef.Start].Vector.Y, ZMin);
				Vector3D lineend = new Vector3D(doomMap.Vertices[currentLinedef.End].Vector.X, doomMap.Vertices[currentLinedef.End].Vector.Y, ZMax);
				
				double sideLength = System.Math.Sqrt(System.Math.Pow(start.X - end.X, 2) + System.Math.Pow(start.Y - end.Y, 2));
				
				bool upperUnpegged = !((currentLinedef.Flags[0] & ((sbyte) 1 << 3)) == 0);
				bool lowerUnpegged = !((currentLinedef.Flags[0] & ((sbyte) 1 << 4)) == 0);
				
				texS[0] = (start.X - end.X) / sideLength;
				texS[1] = (start.Y - end.Y) / sideLength;
				texS[2] = 0;
				texT[0] = 0;
				texT[1] = 0;
				texT[2] = - 1;
				
				double SShift = sideDefShifts[X][subsectorSidedefs[i][j]] - (texS[0] * end.X) - (texS[1] * end.Y);
				double lowTShift = 0;
				double highTShift = 0;
				if (!currentLinedef.OneSided)
				{
					DSector otherSideSector;
					if (doomMap.Sectors[doomMap.Sidedefs[currentLinedef.Left].Sector] == currentSector)
					{
						otherSideSector = doomMap.Sectors[doomMap.Sidedefs[currentLinedef.Right].Sector];
					}
					else
					{
						otherSideSector = doomMap.Sectors[doomMap.Sidedefs[currentLinedef.Left].Sector];
					}
					if (lowerUnpegged)
					{
						lowTShift = otherSideSector.CielingHeight;
					}
					else
					{
						lowTShift = currentSector.FloorHeight;
					}
					if (upperUnpegged)
					{
						highTShift = otherSideSector.CielingHeight;
					}
					else
					{
						highTShift = currentSector.CielingHeight;
					}
					lowTShift += sideDefShifts[Y][subsectorSidedefs[i][j]];
					highTShift += sideDefShifts[Y][subsectorSidedefs[i][j]];
				}
				MAPBrushSide low = new MAPBrushSide(plane, lowerWallTextures[subsectorSidedefs[i][j]], texS, SShift, texT, lowTShift, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide high = new MAPBrushSide(plane, higherWallTextures[subsectorSidedefs[i][j]], texS, SShift, texT, highTShift, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide mid;
				MAPBrushSide damage = new MAPBrushSide(plane, "special/trigger", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				
				if (currentLinedef.OneSided)
				{
					if (!outsideBrushAlreadyCreated[currentseg.Linedef])
					{
						outsideBrushAlreadyCreated[currentseg.Linedef] = true;
						double highestCieling = currentSector.CielingHeight;
						double lowestFloor = currentSector.FloorHeight;
						if (currentSector.Tag != 0)
						{
							double temp = getHighestNeighborCielingHeight(subsectorSectors[i]);
							if (temp > highestCieling)
							{
								highestCieling = temp;
							}
							temp = getLowestNeighborFloorHeight(subsectorSectors[i]);
							if (temp < lowestFloor)
							{
								lowestFloor = temp;
							}
						}
						MAPBrush outsideBrush = null;
						if (lowestFloor <= highestCieling)
						{
							outsideBrush = MAPBrush.createFaceBrush(midWallTextures[subsectorSidedefs[i][j]], "special/nodraw", new Vector3D(linestart.X, linestart.Y, ZMin), new Vector3D(lineend.X, lineend.Y, ZMax), sideDefShifts[X][subsectorSidedefs[i][j]], sideDefShifts[Y][subsectorSidedefs[i][j]], lowerUnpegged, currentSector.CielingHeight, currentSector.FloorHeight);
						}
						else
						{
							outsideBrush = MAPBrush.createFaceBrush(midWallTextures[subsectorSidedefs[i][j]], "special/nodraw", new Vector3D(linestart.X, linestart.Y, lowestFloor), new Vector3D(lineend.X, lineend.Y, highestCieling), sideDefShifts[X][subsectorSidedefs[i][j]], sideDefShifts[Y][subsectorSidedefs[i][j]], lowerUnpegged, currentSector.CielingHeight, currentSector.FloorHeight);
						}
						world.Brushes.Add(outsideBrush);
					}
					mid = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				}
				else
				{
					double midTShift = sideDefShifts[Y][subsectorSidedefs[i][j]];
					if (lowerUnpegged)
					{
						midTShift += currentSector.FloorHeight;
					}
					else
					{
						midTShift += currentSector.CielingHeight;
					}
					mid = new MAPBrushSide(plane, midWallTextures[subsectorSidedefs[i][j]], texS, SShift, texT, midTShift, 0, 1, 1, 0, "wld_masked", 16, 0);
					if (!linedefFlagsDealtWith[currentseg.Linedef])
					{
						linedefFlagsDealtWith[currentseg.Linedef] = true;
						if (!((currentLinedef.Flags[0] & ((sbyte) 1 << 0)) == 0))
						{
							// Flag 0x0001 indicates "solid" but doesn't block bullets. It is assumed for all one-sided.
							MAPBrush solidBrush = MAPBrush.createFaceBrush("special/clip", "special/clip", linestart, lineend, 0, 0, false, 0, 0);
							world.Brushes.Add(solidBrush);
						}
						else
						{
							if (!((currentLinedef.Flags[0] & ((sbyte) 1 << 1)) == 0))
							{
								// Flag 0x0002 indicates "monster clip".
								MAPBrush solidBrush = MAPBrush.createFaceBrush("special/enemyclip", "special/enemyclip", linestart, lineend, 0, 0, false, 0, 0);
								world.Brushes.Add(solidBrush);
							}
						}
					}
					DSidedef otherside = doomMap.Sidedefs[currentLinedef.Left];
					if (currentLinedef.Action != 0 && !linedefSpecialsDealtWith[currentseg.Linedef])
					{
						linedefSpecialsDealtWith[currentseg.Linedef] = true;
						Entity trigger = null;
						MAPBrush triggerBrush = MAPBrush.createFaceBrush("special/trigger", "special/trigger", linestart, lineend, 0, 0, false, 0, 0);
						if (doomMap.Version == mapType.TYPE_HEXEN)
						{
							bool[] bitset = new bool[16];
							for (int k = 0; k < 8; k++)
							{
								bitset[k] = !((currentLinedef.Flags[0] & ((sbyte) k << 1)) == 0);
							}
							for (int k = 0; k < 8; k++)
							{
								bitset[k + 8] = !((currentLinedef.Flags[1] & ((sbyte) k << 1)) == 0);
							}
							if (bitset[10] && bitset[11] && !bitset[12])
							{
								// Triggered when "Used" by player
								trigger = new Entity("func_button");
								trigger["spawnflags"] = "1";
								if (bitset[9])
								{
									trigger["wait"] = "1";
								}
								else
								{
									trigger["wait"] = "-1";
								}
							}
							else
							{
								if (bitset[9])
								{
									// Can be activated more than once
									trigger = new Entity("trigger_multiple");
									trigger["wait"] = "1";
								}
								else
								{
									trigger = new Entity("trigger_once");
								}
							}
							switch (currentLinedef.Action)
							{
								case 21: 
								// Floor lower to lowest surrounding floor
								case 22:  // Floor lower to next lowest surrounding floor
									if (currentLinedef.Arguments[0] != 0)
									{
										trigger["target"] = "sector" + currentLinedef.Arguments[0] + "lowerfloor";
									}
									else
									{
										trigger["target"] = "sectornum" + otherside.Sector + "lowerfloor";
									}
									break;
								case 24: 
								// Floor raise to highest surrounding floor
								case 25:  // Floor raise to next highest surrounding floor
									if (currentLinedef.Arguments[0] != 0)
									{
										trigger["target"] = "sector" + currentLinedef.Arguments[0] + "raisefloor";
									}
									else
									{
										trigger["target"] = "sectornum" + otherside.Sector + "raisefloor";
									}
									break;
								case 70:  // Teleport
									trigger = new Entity("trigger_teleport");
									if (currentLinedef.Arguments[0] != 0)
									{
										trigger["target"] = "teledest" + currentLinedef.Arguments[0];
									}
									else
									{
										trigger["target"] = "sector" + currentLinedef.Tag + "teledest";
									}
									break;
								case 80:  // Exec script
									// This is a toughie. I can't write a script-to-entity converter.
									trigger["target"] = "script" + currentLinedef.Arguments[0];
									trigger["arg0"] = "" + currentLinedef.Arguments[2];
									trigger["arg1"] = "" + currentLinedef.Arguments[3];
									trigger["arg2"] = "" + currentLinedef.Arguments[4];
									break;
								case 181:  // PLANE_ALIGN
									trigger = null;
									if (!leftSide)
									{
										DSidedef getsector = doomMap.Sidedefs[currentLinedef.Left];
										DSector copyheight = doomMap.Sectors[getsector.Sector];
										short newHeight = copyheight.FloorHeight;
										//floorPlane[0]=new Vector3D(0, Settings.getPlanePointCoef(), 2000);
										//floorPlane[1]=new Vector3D(Settings.getPlanePointCoef(), Settings.getPlanePointCoef(), currentSector.getFloorHeight());
										//floorPlane[2]=new Vector3D(Settings.getPlanePointCoef(), 0, currentSector.getFloorHeight());
									}
									else
									{
										linedefSpecialsDealtWith[currentseg.Linedef] = false;
									}
									break;
								default: 
									trigger = null;
									break;
							}
						}
						else
						{
							switch (currentLinedef.Action)
							{
								
								case 1: 
								// Use Door. open, wait, close
								case 31:  // Use Door. Open, stay.
									trigger = new Entity("func_button");
									trigger["wait"] = "1";
									if (currentLinedef.Action == 31)
									{
										trigger["wait"] = "-1";
									}
									trigger["spawnflags"] = "1";
									if (doomMap.Sectors[otherside.Sector].Tag == 0)
									{
										trigger["target"] = "sectornum" + otherside.Sector + "door";
										if (currentLinedef.Action == 1)
										{
											sectorTag[otherside.Sector] = - 1;
										}
										if (currentLinedef.Action == 31)
										{
											sectorTag[otherside.Sector] = - 2;
										}
									}
									else
									{
										trigger["target"] = "sector" + doomMap.Sectors[otherside.Sector].Tag + "door";
									}
									break;
								
								case 36: 
								// Floor lower to 8 above next lowest neighboring sector
								case 38:  // Floor lower to next lowest neighboring sector
									trigger = new Entity("trigger_once");
									trigger["target"] = "sector" + currentLinedef.Tag + "lowerfloor";
									break;
								
								case 62:  // Floor lower to next lowest neighboring sector, wait 4s, goes back up
									trigger = new Entity("func_button");
									trigger["target"] = "sector" + currentLinedef.Tag + "vator";
									trigger["wait"] = "1";
									trigger["spawnflags"] = "1";
									break;
								
								case 63: 
								// Door with button, retriggerable
								case 103:  // Push button, one-time door open stay open
									trigger = new Entity("func_button");
									trigger["target"] = "sector" + currentLinedef.Tag + "door";
									trigger["wait"] = "1";
									if (currentLinedef.Action == 103)
									{
										trigger["wait"] = "-1";
									}
									trigger["spawnflags"] = "1";
									break;
								
								case 88:  // Walkover retriggerable elevator trigger
									trigger = new Entity("trigger_multiple");
									trigger["target"] = "sector" + currentLinedef.Tag + "vator";
									break;
								
								case 97:  // Walkover retriggerable Teleport
									trigger = new Entity("trigger_teleport");
									trigger["target"] = "sector" + currentLinedef.Tag + "teledest";
									break;
								
								case 109:  // Walkover one-time door open stay open
									trigger = new Entity("trigger_once");
									trigger["target"] = "sector" + currentLinedef.Tag + "door";
									break;
							}
						}
						if (trigger != null)
						{
							trigger.Brushes.Add(triggerBrush);
							mapFile.Add(trigger);
						}
					}
				}
				
				cielingBrush.add(high);
				midBrush.add(mid);
				floorBrush.add(low);
				sectorTriggerBrush.add(damage);
			}
			
			MAPBrushSide roof = new MAPBrushSide(roofPlane, "special/nodraw", roofTexS, 0, roofTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide cieling = new MAPBrushSide(cileingPlane, doomMap.WadName + "/" + currentSector.CielingTexture, cileingTexS, 0, cileingTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide floor = new MAPBrushSide(floorPlane, doomMap.WadName + "/" + currentSector.FloorTexture, floorTexS, 0, floorTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide foundation = new MAPBrushSide(foundationPlane, "special/nodraw", foundationTexS, 0, foundationTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide top = new MAPBrushSide(topPlane, "special/nodraw", topTexS, 0, topTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide bottom = new MAPBrushSide(bottomPlane, "special/nodraw", bottomTexS, 0, bottomTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide invertedFloor = new MAPBrushSide(Plane.flip(floorPlane), "special/trigger", floorTexS, 0, floorTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide damageTop = new MAPBrushSide(new Vector3D[]{floorPlane[0]+new Vector3D(0, 0, 1), floorPlane[1]+new Vector3D(0, 0, 1), floorPlane[2]+new Vector3D(0, 0, 1)}, "special/trigger", floorTexS, 0, floorTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			sectorTriggerBrush.add(damageTop);
			sectorTriggerBrush.add(invertedFloor);
			
			midBrush.add(top);
			midBrush.add(bottom);
			
			cielingBrush.add(cieling);
			cielingBrush.add(roof);
			
			floorBrush.add(floor);
			floorBrush.add(foundation);
			
			// Now need to add the data from node subdivisions. Neither segments nor nodes
			// will completely define a usable brush, but both of them together will.
			do 
			{
				DNode currentNode = doomMap.Nodes[nextNode];
				Vector3D start;
				Vector3D end;
				if (leftSide)
				{
					start = currentNode.VecHead+currentNode.VecTail;
					end = currentNode.VecHead;
				}
				else
				{
					start = currentNode.VecHead;
					end = currentNode.VecHead+currentNode.VecTail;
				}
				
				Vector3D[] plane = new Vector3D[3];
				double[] texS = new double[3];
				double[] texT = new double[3];
				// This is somehow always correct. And I'm okay with that.
				plane[0] = new Vector3D(start.X, start.Y, ZMin);
				plane[1] = new Vector3D(end.X, end.Y, ZMin);
				plane[2] = new Vector3D(start.X, start.Y, ZMax);
				
				double sideLength = System.Math.Sqrt(System.Math.Pow(start.X - end.X, 2) + System.Math.Pow(start.Y - end.Y, 2));
				
				texS[0] = (start.X - end.X) / sideLength;
				texS[1] = (start.Y - end.Y) / sideLength;
				texS[2] = 0;
				texT[0] = 0;
				texT[1] = 0;
				texT[2] = 1;
				MAPBrushSide low = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide high = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide mid = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide damage = new MAPBrushSide(plane, "special/trigger", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				
				cielingBrush.add(high);
				midBrush.add(mid);
				floorBrush.add(low);
				sectorTriggerBrush.add(damage);
				
				leftSide = nodeIsLeft[nextNode];
				nextNode = nodeparents[nextNode];
			}
			while (nextNode != - 1);
			// Now we need to get rid of all the sides that aren't used. Get a list of
			// the useless sides from one brush, and delete those sides from all of them,
			// since they all have the same sides.
			int[] badSides = new int[0];
			if (!Settings.dontCull)
			{
				badSides = MAPBrush.findUnusedPlanes(cielingBrush);
				// Need to iterate backward, since these lists go from low indices to high, and
				// the index of all subsequent items changes when something before it is removed.
				if (cielingBrush.NumSides - badSides.Length < 4)
				{
					DecompilerThread.OnMessage(this, "WARNING: Plane cull returned less than 4 sides for subsector " + i);
					badSides = new int[0];
				}
				else
				{
					for (int j = badSides.Length - 1; j > - 1; j--)
					{
						cielingBrush.delete(badSides[j]);
						floorBrush.delete(badSides[j]);
					}
				}
			}
			
			MAPBrush[] newFloorList = new MAPBrush[sectorFloorBrushes[subsectorSectors[i]].Length + 1];
			MAPBrush[] newCielingList = new MAPBrush[sectorCielingBrushes[subsectorSectors[i]].Length + 1];
			for (int j = 0; j < sectorFloorBrushes[subsectorSectors[i]].Length; j++)
			{
				newFloorList[j] = sectorFloorBrushes[subsectorSectors[i]][j];
				newCielingList[j] = sectorCielingBrushes[subsectorSectors[i]][j];
			}
			newFloorList[newFloorList.Length - 1] = floorBrush;
			newCielingList[newCielingList.Length - 1] = cielingBrush;
			sectorFloorBrushes[subsectorSectors[i]] = newFloorList;
			sectorCielingBrushes[subsectorSectors[i]] = newCielingList;
			
			bool containsMiddle = false;
			for (int j = 0; j < midBrush.NumSides; j++)
			{
				if (!midBrush[j].Texture.ToUpper().Equals("special/nodraw".ToUpper()))
				{
					containsMiddle = true;
					break;
				}
			}
			if (containsMiddle && currentSector.CielingHeight > currentSector.FloorHeight)
			{
				Entity middleEnt = new Entity("func_illusionary");
				if (midBrush.NumSides - badSides.Length >= 4)
				{
					for (int j = badSides.Length - 1; j > - 1; j--)
					{
						midBrush.delete(badSides[j]);
					}
				}
				
				middleEnt.Brushes.Add(midBrush);
				mapFile.Add(middleEnt);
			}
			Entity hurtMe = new Entity("trigger_hurt");
			Entity triggerSecret = new Entity("trigger_bondsecret");
			switch (currentSector.Type)
			{
				case 4: 
				// 20% damage/s with lighting properties
				case 11: 
				// 20% damage/s
				case 16:  // 20% damage/s plus end level when player is almost dead
					hurtMe["dmg"] = "40";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					hurtMe.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(hurtMe);
					break;
				case 5:  // 10% damage/s
					hurtMe["dmg"] = "20";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					hurtMe.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(hurtMe);
					break;
				case 7:  // 5% damage/s
					hurtMe["dmg"] = "10";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					hurtMe.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(hurtMe);
					break;
				case 9:  // "secret"
					triggerSecret["Sound"] = "common/mission_success.wav";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					triggerSecret.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(triggerSecret);
					break;
				case 10:  // 30 seconds after level start, "close" like a door
					if (currentSector.Tag == 0)
					{
						sectorTag[subsectorSectors[i]] = - 3;
					}
					break;
				case 14:  // 300 seconds after level start, "open" like a door
					if (currentSector.Tag == 0)
					{
						sectorTag[subsectorSectors[i]] = - 4;
					}
					break;
				default: 
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					if ((currentSector.Type & 96) != 0)
					{
						// "Generalized" pain sectors
						if ((currentSector.Type & 96) == 32)
						{
							hurtMe["dmg"] = "10";
						}
						else
						{
							if ((currentSector.Type & 96) == 64)
							{
								hurtMe["dmg"] = "20";
							}
							else
							{
								if ((currentSector.Type & 96) == 96)
								{
									hurtMe["dmg"] = "40";
								}
							}
						}
						hurtMe.Brushes.Add(sectorTriggerBrush);
						mapFile.Add(hurtMe);
					}
					if ((currentSector.Type & 128) == 128)
					{
						// "Generalized" secret trigger
						triggerSecret["Sound"] = "common/mission_success.wav";
						triggerSecret.Brushes.Add(sectorTriggerBrush);
						mapFile.Add(triggerSecret);
					}
					break;
			}
			if((i+1)%onePercent == 0) {
				parent.OnProgress(this, (i+1)/(double)(doomMap.SubSectors.Count));
			}
			//Settings.setProgress(jobnum, i + 1, doomMap.SubSectors.Count, "Decompiling...");
		}
		
		// Add the brushes to the map, as world by default, or entities if they are supported.
		for (int i = 0; i < doomMap.Sectors.Count; i++)
		{
			bool[] floorsUsed = new bool[sectorFloorBrushes[i].Length];
			bool[] cielingsUsed = new bool[sectorCielingBrushes[i].Length];
			if (sectorTag[i] == 0)
			{
				for (int j = 0; j < sectorFloorBrushes[i].Length; j++)
				{
					world.Brushes.Add(sectorFloorBrushes[i][j]);
					floorsUsed[j] = true;
					world.Brushes.Add(sectorCielingBrushes[i][j]);
					cielingsUsed[j] = true;
				}
			}
			else
			{
				if (sectorTag[i] == - 1 || sectorTag[i] == - 2 || sectorTag[i] == - 3 || sectorTag[i] == - 4)
				{
					// I'm using this to mean a door with no tag number
					Entity newDoor = new Entity("func_door");
					newDoor["speed"] = "60";
					newDoor["angles"] = "270 0 0";
					newDoor["spawnflags"] = "256";
					newDoor["targetname"] = "sectornum" + i + "door";
					if (sectorTag[i] == - 1)
					{
						newDoor["wait"] = "4";
					}
					else
					{
						if (sectorTag[i] == - 2)
						{
							newDoor["wait"] = "-1";
						}
					}
					if (sectorTag[i] == - 3)
					{
						Entity triggerAuto = new Entity("trigger_auto");
						triggerAuto["target"] = "sectornum" + i + "door_mm";
						Entity multiManager = new Entity("multi_manager");
						multiManager["sectornum" + i + "door"] = "30";
						mapFile.Add(triggerAuto);
						mapFile.Add(multiManager);
					}
					if (sectorTag[i] == - 4)
					{
						newDoor["Spawnflags"] = "1";
						Entity triggerAuto = new Entity("trigger_auto");
						triggerAuto["target"] = "sectornum" + i + "door_mm";
						Entity multiManager = new Entity("multi_manager");
						multiManager["sectornum" + i + "door"] = "300";
						mapFile.Add(triggerAuto);
						mapFile.Add(multiManager);
					}
					int lowestNeighborCielingHeight = getLowestNeighborCielingHeight(i);
					int lip = ZMax - lowestNeighborCielingHeight + 4;
					newDoor["lip"] = "" + lip;
					for (int j = 0; j < sectorFloorBrushes[i].Length; j++)
					{
						cielingsUsed[j] = true;
						newDoor.Brushes.Add(sectorCielingBrushes[i][j]);
					}
					mapFile.Add(newDoor);
				}
				else
				{
					for (int j = 0; j < doomMap.Linedefs.Count; j++)
					{
						DLinedef currentLinedef = doomMap.Linedefs[j];
						int linedefTriggerType = currentLinedef.Action;
						if (doomMap.Version == mapType.TYPE_HEXEN)
						{
							switch (linedefTriggerType)
							{
								
								case 21: 
								// Floor lower to lowest neighbor
								case 22:  // Floor lower to nearest lower neighbor
									// I don't know where retriggerability is determined, or whether or not it goes back up.
									if (currentLinedef.Arguments[0] == sectorTag[i])
									{
										Entity newFloor = new Entity("func_door");
										newFloor["angles"] = "90 0 0";
										newFloor["wait"] = "-1";
										newFloor["speed"] = "" + currentLinedef.Arguments[1];
										if (currentLinedef.Arguments[0] == 0)
										{
											newFloor["targetname"] = "sectornum" + i + "lowerfloor";
										}
										else
										{
											newFloor["targetname"] = "sector" + currentLinedef.Arguments[0] + "lowerfloor";
										}
										int lowestNeighborFloorHeight;
										if (linedefTriggerType == 21)
										{
											lowestNeighborFloorHeight = getLowestNeighborFloorHeight(i);
										}
										else
										{
											lowestNeighborFloorHeight = getNextLowestNeighborFloorHeight(i);
										}
										if (lowestNeighborFloorHeight == 32768)
										{
											lowestNeighborFloorHeight = doomMap.Sectors[i].FloorHeight;
										}
										int lip = ZMin - lowestNeighborFloorHeight;
										newFloor["lip"] = "" + System.Math.Abs(lip);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												floorsUsed[k] = true;
												newFloor.Brushes.Add(sectorFloorBrushes[i][k]);
											}
										}
										mapFile.Add(newFloor);
									}
									break;
								case 24: 
								// Floor raise to highest neighbor
								case 25:  // Floor raise to nearest higher neighbor
									// I don't know where retriggerability is determined, or whether or not it goes back up.
									if (currentLinedef.Arguments[0] == sectorTag[i])
									{
										Entity newFloor = new Entity("func_door");
										newFloor["angles"] = "270 0 0";
										newFloor["wait"] = "-1";
										newFloor["speed"] = "" + currentLinedef.Arguments[1];
										if (currentLinedef.Arguments[0] == 0)
										{
											newFloor["targetname"] = "sectornum" + i + "raisefloor";
										}
										else
										{
											newFloor["targetname"] = "sector" + currentLinedef.Arguments[0] + "raisefloor";
										}
										int highestNeighborFloorHeight;
										if (linedefTriggerType == 24)
										{
											highestNeighborFloorHeight = getHighestNeighborFloorHeight(i);
										}
										else
										{
											highestNeighborFloorHeight = getNextHighestNeighborFloorHeight(i);
										}
										if (highestNeighborFloorHeight == - 32768)
										{
											highestNeighborFloorHeight = doomMap.Sectors[i].FloorHeight;
										}
										int lip = ZMin - highestNeighborFloorHeight;
										newFloor["lip"] = "" + System.Math.Abs(lip);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												floorsUsed[k] = true;
												newFloor.Brushes.Add(sectorFloorBrushes[i][k]);
											}
										}
										mapFile.Add(newFloor);
									}
									break;
								}
						}
						else
						{
							if (currentLinedef.Tag == sectorTag[i])
							{
								switch (linedefTriggerType)
								{
									
									case 36: 
									// Line crossed, floor lowers, stays 8 above next lowest
									case 38:  // Line crossed, floor lowers, stays at next lowest
										Entity newFloor = new Entity("func_door");
										newFloor["speed"] = "120";
										newFloor["angles"] = "90 0 0";
										newFloor["targetname"] = "sector" + sectorTag[i] + "lowerfloor";
										newFloor["wait"] = "-1";
										int lowestNeighborFloorHeight = getLowestNeighborFloorHeight(i);
										int lip = ZMin - lowestNeighborFloorHeight;
										if (linedefTriggerType == 36)
										{
											lip -= 8;
										}
										newFloor["lip"] = "" + System.Math.Abs(lip);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												floorsUsed[k] = true;
												newFloor.Brushes.Add(sectorFloorBrushes[i][k]);
											}
										}
										mapFile.Add(newFloor);
										break;
									case 63: 
									// Push button, door opens, waits 4s, closes
									case 103: 
									// Push button, door opens, stays
									case 109:  // Cross line, door opens, stays
										Entity newDoor = new Entity("func_door");
										newDoor["speed"] = "60";
										newDoor["angles"] = "270 0 0";
										newDoor["targetname"] = "sector" + sectorTag[i] + "door";
										newDoor["wait"] = "-1";
										if (sectorTag[i] == 63)
										{
											newDoor["wait"] = "4";
										}
										int lowestNeighborCielingHeight = getLowestNeighborCielingHeight(i);
										lip = ZMax - lowestNeighborCielingHeight + 4;
										newDoor["lip"] = "" + lip;
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!cielingsUsed[k])
											{
												cielingsUsed[k] = true;
												newDoor.Brushes.Add(sectorCielingBrushes[i][k]);
											}
										}
										mapFile.Add(newDoor);
										break;
									case 62: 
									// Push button, Elevator goes down to lowest, wait 4s, goes up
									case 88:  // Elevator goes down to lowest, wait 4s, goes up
										Entity newVator = new Entity("func_door");
										newVator["speed"] = "120";
										newVator["angles"] = "90 0 0";
										newVator["targetname"] = "sector" + sectorTag[i] + "vator";
										newVator["wait"] = "4";
										lowestNeighborFloorHeight = getLowestNeighborFloorHeight(i);
										lip = System.Math.Abs(ZMin - lowestNeighborFloorHeight);
										newVator["lip"] = "" + lip;
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												newVator.Brushes.Add(sectorFloorBrushes[i][k]);
												floorsUsed[k] = true;
											}
										}
										mapFile.Add(newVator);
										break;
									default:  // I'd like to not use this evenutally, all the trigger types ought to be handled
										DecompilerThread.OnMessage(this, "WARNING: Unimplemented linedef trigger type " + linedefTriggerType + " for sector " + i + " tagged " + sectorTag[i]);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												world.Brushes.Add(sectorFloorBrushes[i][k]);
												floorsUsed[k] = true;
											}
											if (!cielingsUsed[k])
											{
												world.Brushes.Add(sectorCielingBrushes[i][k]);
												cielingsUsed[k] = true;
											}
										}
										break;
								}
							}
						}
					}
				}
			}
			for (int j = 0; j < sectorFloorBrushes[i].Length; j++)
			{
				if (!cielingsUsed[j])
				{
					world.Brushes.Add(sectorCielingBrushes[i][j]);
				}
				if (!floorsUsed[j])
				{
					world.Brushes.Add(sectorFloorBrushes[i][j]);
				}
			}
		}
		
		// Convert THINGS
		for (int i = 0; i < doomMap.Things.Count; i++)
		{
			DThing currentThing = doomMap.Things[i];
			// To find the true height of a thing, I need to iterate through nodes until I come to a subsector
			// definition. Then I need to use the floor height of the sector that subsector belongs to.
			Vector3D origin = currentThing.Origin;
			int subsectorIndex = doomMap.Nodes.Count - 1;
			while (subsectorIndex >= 0)
			{
				// Once child is negative, subsector is found
				DNode currentNode = doomMap.Nodes[subsectorIndex];
				Vector3D start = currentNode.VecHead;
				Vector3D end = currentNode.VecHead+currentNode.VecTail;
				Plane currentPlane = new Plane(start, end, new Vector3D(start.X, start.Y, 1));
				if (currentPlane.distance(origin) < 0)
				{
					subsectorIndex = currentNode.Child1;
				}
				else
				{
					subsectorIndex = currentNode.Child2;
				}
			}
			subsectorIndex += 32768;
			int sectorIndex = subsectorSectors[subsectorIndex];
			DSector thingSector = doomMap.Sectors[sectorIndex];
			if (origin.Z == 0)
			{
				origin.Z=thingSector.FloorHeight;
			}
			
			Entity thing = null;
			// Things from both Doom. Currently converting to appropriate Doom 3 entities.
			switch (currentThing.ClassNum)
			{
				case 1: 
				// Single player spawn
				case 2: 
				// coop
				case 3: 
				// coop
				case 4:  // coop
					thing = new Entity("info_player_start");
					if (currentThing.ClassNum > 1)
					{
						thing["targetname"] = "coopspawn" + currentThing.ClassNum;
					}
					playerStartOrigin = origin.X + " " + origin.Y + " " + (origin.Z + 36);
					thing["origin"] = playerStartOrigin;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 11:  // Deathmatch spawn
					thing = new Entity("info_player_deathmatch");
					thing["origin"] = origin.X + " " + origin.Y + " " + (origin.Z + 36);
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 14:  // Teleport destination
					thing = new Entity("info_teleport_destination");
					if (currentThing.ID != 0)
					{
						thing["targetname"] = "teledest" + currentThing.ID;
					}
					else
					{
						thing["targetname"] = "sector" + thingSector.Tag + "teledest";
					}
					thing["origin"] = origin.X + " " + origin.Y + " " + (origin.Z + 36);
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 17:  // Big cell pack
					thing = new Entity("ammo_cells_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 82:  // Super shotgun
					thing = new Entity("weapon_shotgun_double");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2001:  // Shotgun
					thing = new Entity("weapon_shotgun");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2002:  // Chaingun
					thing = new Entity("weapon_chaingun");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2003:  // Rocket launcher
					thing = new Entity("weapon_rocketlauncher");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2004:  // Plasma gun
					thing = new Entity("weapon_plasmagun");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2005:  // Chainsaw
					thing = new Entity("weapon_chainsaw");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2006:  // BFG9000
					thing = new Entity("weapon_bfg");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2007:  // Ammo clip
					thing = new Entity("ammo_clip_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2008:  // Shotgun shells
					thing = new Entity("ammo_shells_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2010:  // Rocket
					thing = new Entity("ammo_rockets_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2046:  // Box of Rockets
					thing = new Entity("ammo_rockets_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2047:  // Cell pack
					thing = new Entity("ammo_cells_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2048:  // Box of ammo
					thing = new Entity("ammo_bullets_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2049:  // Box of shells
					thing = new Entity("ammo_shells_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				}
			
			if (thing != null)
			{
				mapFile.Add(thing);
			}
		}
		
		Entity playerequip = new Entity("game_player_equip");
		playerequip["weapon_pistol"] = "1";
		playerequip["origin"] = playerStartOrigin;
		mapFile.Add(playerequip);
		parent.OnProgress(this, 1.0);
		return mapFile;
	}
    // -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);
        }
    }
示例#37
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);
        }
    }
示例#38
0
 private string brushSideToString(MAPBrushSide inputData)
 {
     try
     {
         Vector3D[] triangle      = inputData.Triangle;
         string     texture       = inputData.Texture;
         Vector3D   textureS      = inputData.TextureS;
         Vector3D   textureT      = inputData.TextureT;
         double     textureShiftS = inputData.TextureShiftS;
         double     textureShiftT = inputData.TextureShiftT;
         float      texRot        = inputData.TexRot;
         double     texScaleX     = inputData.TexScaleX;
         double     texScaleY     = inputData.TexScaleY;
         int        flags         = inputData.Flags;
         string     material      = inputData.Material;
         double     lgtScale      = inputData.LgtScale;
         double     lgtRot        = inputData.LgtRot;
         if (Double.IsInfinity(texScaleX) || Double.IsNaN(texScaleX))
         {
             texScaleX = 1;
         }
         if (Double.IsInfinity(texScaleY) || Double.IsNaN(texScaleY))
         {
             texScaleY = 1;
         }
         if (Double.IsInfinity(textureShiftS) || Double.IsNaN(textureShiftS))
         {
             textureShiftS = 0;
         }
         if (Double.IsInfinity(textureShiftT) || Double.IsNaN(textureShiftT))
         {
             textureShiftT = 0;
         }
         if (Double.IsInfinity(textureS.X) || Double.IsNaN(textureS.X) || Double.IsInfinity(textureS.Y) || Double.IsNaN(textureS.Y) || Double.IsInfinity(textureS.Z) || Double.IsNaN(textureS.Z))
         {
             textureS = TexInfo.textureAxisFromPlane(inputData.Plane)[0];
         }
         if (Double.IsInfinity(textureT.X) || Double.IsNaN(textureT.X) || Double.IsInfinity(textureT.Y) || Double.IsNaN(textureT.Y) || Double.IsInfinity(textureT.Z) || Double.IsNaN(textureT.Z))
         {
             textureT = TexInfo.textureAxisFromPlane(inputData.Plane)[1];
         }
         if (Settings.roundNums)
         {
             return("( " + Math.Round(triangle[0].X, 6, MidpointRounding.AwayFromZero) + " " + Math.Round(triangle[0].Y, 6, MidpointRounding.AwayFromZero) + " " + Math.Round(triangle[0].Z, 6, MidpointRounding.AwayFromZero) + " ) " +
                    "( " + Math.Round(triangle[1].X, 6, MidpointRounding.AwayFromZero) + " " + Math.Round(triangle[1].Y, 6, MidpointRounding.AwayFromZero) + " " + Math.Round(triangle[1].Z, 6, MidpointRounding.AwayFromZero) + " ) " +
                    "( " + Math.Round(triangle[2].X, 6, MidpointRounding.AwayFromZero) + " " + Math.Round(triangle[2].Y, 6, MidpointRounding.AwayFromZero) + " " + Math.Round(triangle[2].Z, 6, MidpointRounding.AwayFromZero) + " ) " +
                    texture +
                    " [ " + Math.Round(textureS.X, 6, MidpointRounding.AwayFromZero) + " " + Math.Round(textureS.Y, 6, MidpointRounding.AwayFromZero) + " " + Math.Round(textureS.Z, 6, MidpointRounding.AwayFromZero) + " " + Math.Round(textureShiftS, MidpointRounding.AwayFromZero) + " ]" +
                    " [ " + Math.Round(textureT.X, 6, MidpointRounding.AwayFromZero) + " " + Math.Round(textureT.Y, 6, MidpointRounding.AwayFromZero) + " " + Math.Round(textureT.Z, 6, MidpointRounding.AwayFromZero) + " " + Math.Round(textureShiftT, MidpointRounding.AwayFromZero) + " ] " +
                    Math.Round(texRot, 4, MidpointRounding.AwayFromZero) + " " + Math.Round(texScaleX, 4, MidpointRounding.AwayFromZero) + " " + Math.Round(texScaleY, 4, MidpointRounding.AwayFromZero) + " " + flags + " " + material + " [ " + Math.Round(lgtScale, 4, MidpointRounding.AwayFromZero) + " " + Math.Round(lgtRot, 4, MidpointRounding.AwayFromZero) + " ]");
         }
         else
         {
             return("( " + triangle[0].X + " " + triangle[0].Y + " " + triangle[0].Z + " ) " + "( " + triangle[1].X + " " + triangle[1].Y + " " + triangle[1].Z + " ) " + "( " + triangle[2].X + " " + triangle[2].Y + " " + triangle[2].Z + " ) " + texture + " [ " + textureS.X + " " + textureS.Y + " " + textureS.Z + " " + textureShiftS + " ]" + " [ " + textureT.X + " " + textureT.Y + " " + textureT.Z + " " + textureShiftT + " ] " + texRot + " " + texScaleX + " " + texScaleY + " " + flags + " " + material + " [ " + lgtScale + " " + lgtRot + " ]");
         }
     }
     catch (System.NullReferenceException)
     {
         Console.WriteLine("WARNING: Side with bad data! Not exported!");
         return("");
     }
 }
	// -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);
		}
	}
示例#40
0
	private string brushSideToString(MAPBrushSide inBrushSide)
	{
		try
		{
			Vector3D[] triangle = inBrushSide.Triangle;
			string texture = inBrushSide.Texture;
			Vector3D textureS = inBrushSide.TextureS;
			Vector3D textureT = inBrushSide.TextureT;
			double textureShiftS = inBrushSide.TextureShiftS;
			double textureShiftT = inBrushSide.TextureShiftT;
			double texScaleX = inBrushSide.TexScaleX;
			double texScaleY = inBrushSide.TexScaleY;
			float texRot = inBrushSide.TexRot;
			double lgtScale = inBrushSide.LgtScale;
			if (BSPVersion == mapType.TYPE_QUAKE3 || BSPVersion == mapType.TYPE_MOHAA || BSPVersion == mapType.TYPE_COD || BSPVersion == mapType.TYPE_STEF2 || BSPVersion == mapType.TYPE_STEF2DEMO || BSPVersion == mapType.TYPE_FAKK) {
				try
				{
					if (texture.Substring(0, (9) - (0)).ToUpper().Equals("textures/".ToUpper()))
					{
						texture = texture.Substring(9);
					}
				}
				catch (System.ArgumentOutOfRangeException)
				{
					;
				}
			} else {
				if (BSPVersion == mapType.TYPE_NIGHTFIRE || BSPVersion == mapType.TYPE_DOOM || BSPVersion == mapType.TYPE_HEXEN)
				{
					if (texture.ToUpper().Equals("special/nodraw".ToUpper()) || texture.ToUpper().Equals("special/null".ToUpper()))
					{
						texture = "tools/toolsnodraw";
					}
					else
					{
						if (texture.ToUpper().Equals("special/clip".ToUpper()))
						{
							texture = "tools/toolsclip";
						}
						else
						{
							if (texture.ToUpper().Equals("special/sky".ToUpper()))
							{
								texture = "tools/toolsskybox";
							}
							else
							{
								if (texture.ToUpper().Equals("special/trigger".ToUpper()))
								{
									texture = "tools/toolstrigger";
								}
								else
								{
									if (texture.ToUpper().Equals("special/playerclip".ToUpper()))
									{
										texture = "tools/toolsplayerclip";
									}
									else
									{
										if (texture.ToUpper().Equals("special/npcclip".ToUpper()) || texture.ToUpper().Equals("special/enemyclip".ToUpper()))
										{
											texture = "tools/toolsnpcclip";
										}
									}
								}
							}
						}
					}
				}
				else
				{
					if (BSPVersion == mapType.TYPE_QUAKE2)
					{
						try
						{
							if (texture.ToUpper().Equals("special/hint".ToUpper()))
							{
								texture = "tools/toolshint";
							}
							else
							{
								if (texture.ToUpper().Equals("special/skip".ToUpper()))
								{
									texture = "tools/toolsskip";
								}
								else
								{
									if (texture.ToUpper().Equals("special/sky".ToUpper()))
									{
										texture = "tools/toolsskybox";
									}
									else
									{
										if (texture.Substring(texture.Length - 8).ToUpper().Equals("/trigger".ToUpper()))
										{
											texture = "tools/toolstrigger";
										}
										else
										{
											if (texture.Substring(texture.Length - 5).ToUpper().Equals("/clip".ToUpper()))
											{
												texture = "tools/toolsclip";
											}
										}
									}
								}
							}
						}
						catch (System.ArgumentOutOfRangeException e)
						{
							;
						}
					}
					else
					{
						if (BSPVersion == mapType.TYPE_SOURCE17 || BSPVersion == mapType.TYPE_SOURCE18 || BSPVersion == mapType.TYPE_SOURCE19 || BSPVersion == mapType.TYPE_SOURCE20 || BSPVersion == mapType.TYPE_SOURCE21 || BSPVersion == mapType.TYPE_SOURCE22 || BSPVersion == mapType.TYPE_SOURCE23 || BSPVersion == mapType.TYPE_DMOMAM || BSPVersion == mapType.TYPE_VINDICTUS || BSPVersion == mapType.TYPE_TACTICALINTERVENTION)
						{
							try
							{
								if (texture.Substring(0, (5) - (0)).ToUpper().Equals("maps/".ToUpper()))
								{
									texture = texture.Substring(5);
									for (int i = 0; i < texture.Length; i++)
									{
										if (texture[i] == '/')
										{
											texture = texture.Substring(i + 1);
											break;
										}
									}
								}
							}
							catch (System.ArgumentOutOfRangeException)
							{
								;
							}
							// Find cubemap textures
							int numUnderscores = 0;
							bool validnumber = false;
							for (int i = texture.Length - 1; i > 0; i--)
							{
								if (texture[i] <= '9' && texture[i] >= '0')
								{
									// Current is a number, start building string
									validnumber = true;
								}
								else
								{
									if (texture[i] == '-')
									{
										// Current is a minus sign (-).
										if (!validnumber)
										{
											break; // Make sure there's a number to add the minus sign to. If not, kill the loop.
										}
									}
									else
									{
										if (texture[i] == '_')
										{
											// Current is an underscore (_)
											if (validnumber)
											{
												// Make sure there is a number in the current string
												numUnderscores++; // before moving on to the next one.
												validnumber = false;
												if (numUnderscores == 3)
												{
													// If we've got all our numbers
													texture = texture.Substring(0, (i) - (0)); // Cut the texture string
													break; // Kill the loop, we're done
												}
											}
											else
											{
												// No number after the underscore
												break;
											}
										}
										else
										{
											// Not an acceptable character
											break;
										}
									}
								}
							}
						}
					}
				}
			}
			if(Double.IsInfinity(texScaleX) || Double.IsNaN(texScaleX)) {
				texScaleX = 1;
			}
			if(Double.IsInfinity(texScaleY) || Double.IsNaN(texScaleY)) {
				texScaleY = 1;
			}
			if(Double.IsInfinity(textureShiftS) || Double.IsNaN(textureShiftS)) {
				textureShiftS = 0;
			}
			if(Double.IsInfinity(textureShiftT) || Double.IsNaN(textureShiftT)) {
				textureShiftT = 0;
			}
			if(Double.IsInfinity(textureS.X) || Double.IsNaN(textureS.X) || Double.IsInfinity(textureS.Y) || Double.IsNaN(textureS.Y) || Double.IsInfinity(textureS.Z) || Double.IsNaN(textureS.Z)) {
				textureS = TexInfo.textureAxisFromPlane(inBrushSide.Plane)[0];
			}
			if(Double.IsInfinity(textureT.X) || Double.IsNaN(textureT.X) || Double.IsInfinity(textureT.Y) || Double.IsNaN(textureT.Y) || Double.IsInfinity(textureT.Z) || Double.IsNaN(textureT.Z)) {
				textureT = TexInfo.textureAxisFromPlane(inBrushSide.Plane)[1];
			}
			string output = "		side" + (char) 0x0D + (char) 0x0A + "		{" + (char) 0x0D + (char) 0x0A;
			output += ("			\"id\" \"" + (nextID++) + "\"" + (char) 0x0D + (char) 0x0A);
			output += ("			\"material\" \"" + texture + "\"" + (char) 0x0D + (char) 0x0A);
			if (Settings.roundNums) {
				output += ("			\"plane\" \"(" + MAPMaker.Round(triangle[0].X, 6) + " " + MAPMaker.Round(triangle[0].Y, 6) + " " + MAPMaker.Round(triangle[0].Z, 6) + ") ");
				output += ("("                      + MAPMaker.Round(triangle[1].X, 6) + " " + MAPMaker.Round(triangle[1].Y, 6) + " " + MAPMaker.Round(triangle[1].Z, 6) + ") ");
				output += ("("                      + MAPMaker.Round(triangle[2].X, 6) + " " + MAPMaker.Round(triangle[2].Y, 6) + " " + MAPMaker.Round(triangle[2].Z, 6) + ")\"" + (char) 0x0D + (char) 0x0A);
				output += ("			\"uaxis\" \"[" + MAPMaker.Round(textureS.X, 6) + " " + MAPMaker.Round(textureS.Y, 6) + " " + MAPMaker.Round(textureS.Z, 6) + " " + MAPMaker.Round(textureShiftS) + "] " + MAPMaker.Round(texScaleX, 4) + "\"" + (char) 0x0D + (char) 0x0A);
				output += ("			\"vaxis\" \"[" + MAPMaker.Round(textureT.X, 6) + " " + MAPMaker.Round(textureT.Y, 6) + " " + MAPMaker.Round(textureT.Z, 6) + " " + MAPMaker.Round(textureShiftT) + "] " + MAPMaker.Round(texScaleY, 4) + "\"" + (char) 0x0D + (char) 0x0A);
				output += ("			\"rotation\" \"" + MAPMaker.Round(texRot, 4) + "\"" + (char) 0x0D + (char) 0x0A);
				output += ("			\"lightmapscale\" \"" + MAPMaker.Round(lgtScale, 4) + "\"" + (char) 0x0D + (char) 0x0A);
			} else {
				output += ("			\"plane\" \"(" + triangle[0].X + " " + triangle[0].Y + " " + triangle[0].Z + ") ");
				output += ("("                      + triangle[1].X + " " + triangle[1].Y + " " + triangle[1].Z + ") ");
				output += ("("                      + triangle[2].X + " " + triangle[2].Y + " " + triangle[2].Z + ")\"" + (char) 0x0D + (char) 0x0A);
				output += ("			\"uaxis\" \"[" + textureS.X + " " + textureS.Y + " " + textureS.Z + " " + textureShiftS + "] " + texScaleX + "\"" + (char) 0x0D + (char) 0x0A);
				output += ("			\"vaxis\" \"[" + textureT.X + " " + textureT.Y + " " + textureT.Z + " " + textureShiftT + "] " + texScaleY + "\"" + (char) 0x0D + (char) 0x0A);
				output += ("			\"rotation\" \"" + texRot + "\"" + (char) 0x0D + (char) 0x0A);
				output += ("			\"lightmapscale\" \"" + lgtScale + "\"" + (char) 0x0D + (char) 0x0A);
			}
			output += ("			\"smoothing_groups\" \"0\"" + (char) 0x0D + (char) 0x0A);
			if (inBrushSide.Displacement != null)
			{
				output += displacementToString(inBrushSide.Displacement);
			}
			output += ("		}" + (char) 0x0D + (char) 0x0A);
			return output;
		}
		catch (System.NullReferenceException)
		{
			DecompilerThread.OnMessage(this, "WARNING: Side with bad data! Not exported!");
			return null;
		}
	}
        /// <summary>
        /// Postprocesser to convert the texture referenced by <paramref name="brushSide"/> into one used by GTKRadiant, if necessary.
        /// </summary>
        /// <param name="brushSide">The <see cref="MAPBrushSide"/> to have its texture parsed.</param>
        private void PostProcessSourceTexture(MAPBrushSide brushSide)
        {
            if (brushSide.texture.Length >= 5 && brushSide.texture.Substring(0, 5).Equals("maps/", StringComparison.InvariantCultureIgnoreCase))
            {
                brushSide.texture = brushSide.texture.Substring(5);
                for (int i = 0; i < brushSide.texture.Length; ++i)
                {
                    if (brushSide.texture[i] == '/')
                    {
                        brushSide.texture = brushSide.texture.Substring(i + 1);
                        break;
                    }
                }
            }

            switch (brushSide.texture.ToLower())
            {
            case "tools/toolshint": {
                brushSide.texture = "common/hint";
                break;
            }

            case "tools/toolsskip": {
                brushSide.texture = "common/skip";
                break;
            }

            case "tools/toolsinvisible":
            case "tools/toolsplayerclip":
            case "tools/toolsclip": {
                brushSide.texture = "common/clip";
                break;
            }

            case "tools/toolstrigger":
            case "tools/toolsfog": {
                brushSide.texture = "common/trigger";
                break;
            }

            case "tools/toolsskybox": {
                brushSide.texture = "common/skyportal";
                break;
            }

            case "tools/toolsnodraw": {
                brushSide.texture = "common/nodraw";
                break;
            }

            case "tools/toolsnpcclip": {
                brushSide.texture = "common/tankclip";
                break;
            }
            }

            // Parse cubemap textures
            // I'm sure this could be done more concisely with regex, but I suck at regex.
            int  numUnderscores = 0;
            bool validnumber    = false;

            for (int i = brushSide.texture.Length - 1; i > 0; --i)
            {
                if (brushSide.texture[i] <= '9' && brushSide.texture[i] >= '0')
                {
                    // Current is a number, this may be a cubemap reference
                    validnumber = true;
                }
                else
                {
                    if (brushSide.texture[i] == '-')
                    {
                        // Current is a minus sign (-).
                        if (!validnumber)
                        {
                            break;                             // Make sure there's a number to add the minus sign to. If not, kill the loop.
                        }
                    }
                    else
                    {
                        if (brushSide.texture[i] == '_')
                        {
                            // Current is an underscore (_)
                            if (validnumber)
                            {
                                // Make sure there is a number in the current string
                                ++numUnderscores;                                 // before moving on to the next one.
                                if (numUnderscores == 3)
                                {
                                    // If we've got all our numbers
                                    brushSide.texture = brushSide.texture.Substring(0, i); // Cut the texture string
                                    break;                                                 // Kill the loop, we're done
                                }
                                validnumber = false;
                            }
                            else
                            {
                                // No number after the underscore
                                break;
                            }
                        }
                        else
                        {
                            // Not an acceptable character
                            break;
                        }
                    }
                }
            }
        }
	// -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);
		}
	}
        /// <summary>
        /// Postprocesser to convert the texture referenced by <paramref name="brushSide"/> into one used by Gearcraft, if necessary.
        /// </summary>
        /// <param name="brushSide">The <see cref="MAPBrushSide"/> to have its texture parsed.</param>
        private void PostProcessQuake3Texture(MAPBrushSide brushSide)
        {
            if (brushSide.texture.Length >= 9 && brushSide.texture.Substring(0, 9).Equals("textures/", StringComparison.InvariantCultureIgnoreCase))
            {
                brushSide.texture = brushSide.texture.Substring(9);
            }
            switch (brushSide.texture.ToLower())
            {
            case "common/physics_clip":
            case "common/metalclip":
            case "common/grassclip":
            case "common/paperclip":
            case "common/woodclip":
            case "common/glassclip":
            case "common/clipfoliage":
            case "common/foliageclip":
            case "common/carpetclip":
            case "common/dirtclip":
            case "system/clip":
            case "system/physics_clip":
            case "common/clip": {
                brushSide.texture = "special/clip";
                break;
            }

            case "common/nodrawnonsolid":
            case "system/trigger":
            case "common/trigger": {
                brushSide.texture = "special/trigger";
                break;
            }

            case "common/nodraw":
            case "common/caulkshadow":
            case "common/caulk":
            case "system/caulk":
            case "noshader": {
                brushSide.texture = "special/nodraw";
                break;
            }

            case "common/do_not_enter":
            case "common/donotenter":
            case "common/monsterclip": {
                brushSide.texture = "special/npcclip";
                break;
            }

            case "common/caulksky":
            case "common/skyportal": {
                brushSide.texture = "special/sky";
                break;
            }

            case "common/hint": {
                brushSide.texture = "special/hint";
                break;
            }

            case "common/waterskip": {
                brushSide.texture = "liquids/!water";
                break;
            }

            case "system/do_not_enter":
            case "common/playerclip": {
                brushSide.texture = "special/playerclip";
                break;
            }
            }
            if (brushSide.texture.Length >= 4 && brushSide.texture.Substring(0, 4).Equals("sky/", StringComparison.InvariantCultureIgnoreCase))
            {
                brushSide.texture = "special/sky";
            }
        }
示例#44
0
 private string brushSideToString(MAPBrushSide inputData)
 {
     try {
         string texture = inputData.Texture;
         if (BSPVersion == mapType.TYPE_SOURCE17 || BSPVersion == mapType.TYPE_SOURCE18 || BSPVersion == mapType.TYPE_SOURCE19 || BSPVersion == mapType.TYPE_SOURCE20 || BSPVersion == mapType.TYPE_SOURCE21 || BSPVersion == mapType.TYPE_SOURCE22 || BSPVersion == mapType.TYPE_SOURCE23 || BSPVersion == mapType.TYPE_DMOMAM || BSPVersion == mapType.TYPE_VINDICTUS || BSPVersion == mapType.TYPE_TACTICALINTERVENTION)
         {
             try {
                 if (texture.Substring(0, (5) - (0)).ToUpper().Equals("maps/".ToUpper()))
                 {
                     texture = texture.Substring(5);
                     for (int i = 0; i < texture.Length; i++)
                     {
                         if (texture[i] == '/')
                         {
                             texture = texture.Substring(i + 1);
                             break;
                         }
                     }
                 }
             }
             catch (System.ArgumentOutOfRangeException) {
                 ;
             }
             // Find cubemap textures
             int  numUnderscores = 0;
             bool validnumber    = false;
             for (int i = texture.Length - 1; i > 0; i--)
             {
                 if (texture[i] <= '9' && texture[i] >= '0')
                 {
                     // Current is a number, start building string
                     validnumber = true;
                 }
                 else
                 {
                     if (texture[i] == '-')
                     {
                         // Current is a minus sign (-).
                         if (!validnumber)
                         {
                             break;                                 // Make sure there's a number to add the minus sign to. If not, kill the loop.
                         }
                     }
                     else
                     {
                         if (texture[i] == '_')
                         {
                             // Current is an underscore (_)
                             if (validnumber)
                             {
                                 // Make sure there is a number in the current string
                                 numUnderscores++;                                     // before moving on to the next one.
                                 validnumber = false;
                                 if (numUnderscores == 3)
                                 {
                                     // If we've got all our numbers
                                     texture = texture.Substring(0, (i) - (0));     // Cut the texture string
                                     break;                                         // Kill the loop, we're done
                                 }
                             }
                             else
                             {
                                 // No number after the underscore
                                 break;
                             }
                         }
                         else
                         {
                             // Not an acceptable character
                             break;
                         }
                     }
                 }
             }
         }
         Plane    plane         = inputData.Plane;
         Vector3D textureS      = inputData.TextureS;
         Vector3D textureT      = inputData.TextureT;
         double   textureShiftS = inputData.TextureShiftS;
         double   textureShiftT = inputData.TextureShiftT;
         double   texScaleX     = inputData.TexScaleX;
         double   texScaleY     = inputData.TexScaleY;
         if (Double.IsInfinity(texScaleX) || Double.IsNaN(texScaleX))
         {
             texScaleX = 1;
         }
         if (Double.IsInfinity(texScaleY) || Double.IsNaN(texScaleY))
         {
             texScaleY = 1;
         }
         if (Double.IsInfinity(textureShiftS) || Double.IsNaN(textureShiftS))
         {
             textureShiftS = 0;
         }
         if (Double.IsInfinity(textureShiftT) || Double.IsNaN(textureShiftT))
         {
             textureShiftT = 0;
         }
         if (Double.IsInfinity(textureS.X) || Double.IsNaN(textureS.X) || Double.IsInfinity(textureS.Y) || Double.IsNaN(textureS.Y) || Double.IsInfinity(textureS.Z) || Double.IsNaN(textureS.Z))
         {
             textureS = TexInfo.textureAxisFromPlane(inputData.Plane)[0];
         }
         if (Double.IsInfinity(textureT.X) || Double.IsNaN(textureT.X) || Double.IsInfinity(textureT.Y) || Double.IsNaN(textureT.Y) || Double.IsInfinity(textureT.Z) || Double.IsNaN(textureT.Z))
         {
             textureT = TexInfo.textureAxisFromPlane(inputData.Plane)[1];
         }
         if (Settings.roundNums)
         {
             return("( " + MAPMaker.Round(plane.A, 10) + " " + MAPMaker.Round(plane.B, 10) + " " + MAPMaker.Round(plane.C, 10) + " " + MAPMaker.Round(plane.Dist, 10) + " ) " + "( ( 1 0 " + MAPMaker.Round(textureShiftS, 10) + " ) ( 0 1 " + MAPMaker.Round(textureShiftT, 10) + " ) ) " + "\"" + texture + "\" 0 0 0");
         }
         else
         {
             return("( " + plane.A + " " + plane.B + " " + plane.C + " " + plane.Dist + " ) " + "( ( 1 0 " + textureShiftS + " ) ( 0 1 " + textureShiftT + " ) ) " + "\"" + texture + "\" 0 0 0");
         }
     } catch (System.NullReferenceException e) {
         DecompilerThread.OnMessage(this, "WARNING: Side with bad data! Not exported!");
         return(null);
     }
 }
        /// <summary>
        /// Creates an axis-aligned cubic brush with bounds from <paramref name="mins"/> to <paramref name="maxs"/>.
        /// </summary>
        /// <param name="mins">The minimum extents of the new brush.</param>
        /// <param name="maxs">The maximum extents of the new brush.</param>
        /// <param name="texture">The texture to use on this brush.</param>
        /// <returns>The resulting <see cref="MAPBrush"/> object.</returns>
        public static MAPBrush CreateCube(Vector3d mins, Vector3d maxs, string texture)
        {
            MAPBrush newBrush = new MAPBrush();

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

            for (int i = 0; i < 6; i++)
            {
                MAPBrushSide currentSide = new MAPBrushSide()
                {
                    vertices    = planes[i],
                    plane       = new Plane(planes[i]),
                    texture     = texture,
                    textureInfo = new TextureInfo(new Vector3d(textureS[i]), new Vector3d(textureT[i]), Vector2d.zero, Vector2d.one, 0, 0, 0),
                    material    = "wld_lightmap",
                    lgtScale    = 16,
                    lgtRot      = 0
                };
                newBrush.sides.Add(currentSide);
            }
            return(newBrush);
        }
示例#46
0
	public MAPBrushSide(MAPBrushSide copy) {
		plane = new Plane(copy.Plane);
		triangle[0] = new Vector3D(copy.Triangle[0]);
		triangle[1] = new Vector3D(copy.Triangle[1]);
		triangle[2] = new Vector3D(copy.Triangle[2]);
		texture = copy.Texture;
		textureS = new Vector3D(copy.TextureS);
		textureT = new Vector3D(copy.TextureT);
		textureShiftS = copy.TextureShiftS;
		textureShiftT = copy.TextureShiftT;
		texRot = copy.TexRot;
		texScaleX = copy.TexScaleX;
		texScaleY = copy.TexScaleY;
		flags = copy.Flags;
		material = copy.Material;
		lgtScale = copy.LgtScale;
		lgtRot = copy.LgtRot;
		planeDefined = copy.DefinedByPlane;
		triangleDefined = copy.DefinedByTriangle;
		disp = copy.Displacement;
	}