Esempio n. 1
0
    private byte[] brushToByteArray(MAPBrush inputData, int num)
    {
        if (inputData.NumSides < 4)
        {
            // Can't create a brush with less than 4 sides
            Console.WriteLine("WARNING: Tried to create brush from " + inputData.NumSides + " sides!");
            return(new byte[0]);
        }
        string brush = "{ // Brush " + num + (char)0x0D + (char)0x0A;

        if ((inputData.Detail) && currentEntity == 0)
        {
            brush += ("\"BRUSHFLAGS\" \"DETAIL\"" + (char)0x0D + (char)0x0A);
        }
        for (int i = 0; i < inputData.NumSides; i++)
        {
            brush += (brushSideToString(inputData[i]) + (char)0x0D + (char)0x0A);
        }
        brush += ("}" + (char)0x0D + (char)0x0A);
        if (brush.Length < 45)
        {
            // Any brush this short contains no sides.
            Console.WriteLine("WARNING: Brush with no sides being written! Oh no!");
            return(new byte[0]);
        }
        else
        {
            byte[] brushbytes = new byte[brush.Length];
            for (int i = 0; i < brush.Length; i++)
            {
                brushbytes[i] = (byte)brush[i];
            }
            return(brushbytes);
        }
    }
    private byte[] brushToByteArray(MAPBrush inData, int num)
    {
        if (inData.Patch != null)
        {
            return(patchToByteArray(inData.Patch, num));
        }
        if (inData.NumSides < 4)
        {
            // Can't create a brush with less than 4 sides
            DecompilerThread.OnMessage(this, "WARNING: Tried to create brush from " + inData.NumSides + " sides!");
            return(new byte[0]);
        }
        string brush = "// brush " + num + (char)0x0D + (char)0x0A + "{" + (char)0x0D + (char)0x0A;

        for (int i = 0; i < inData.NumSides; i++)
        {
            brush += (brushSideToString(inData[i], (inData.Detail || inData[0].Displacement != null)) + (char)0x0D + (char)0x0A);
        }
        brush += ("}" + (char)0x0D + (char)0x0A);
        if (brush.Length < 45)
        {
            // Any brush this short contains no sides.
            DecompilerThread.OnMessage(this, "WARNING: Brush with no sides being written! Oh no!");
            return(new byte[0]);
        }
        else
        {
            byte[] brushbytes = new byte[brush.Length];
            for (int i = 0; i < brush.Length; i++)
            {
                brushbytes[i] = (byte)brush[i];
            }
            return(brushbytes);
        }
    }
Esempio n. 3
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);
        }
Esempio n. 4
0
 /// <summary>
 /// Process the data in a <see cref="MAPBrush"/> into the passed <see cref="StringBuilder"/>.
 /// </summary>
 /// <param name="brush">The <see cref="MAPBrush"/> to process.</param>
 /// <param name="index">The index of <see cref="MAPBrush"/> entity in the <see cref="Entity"/>.</param>
 /// <param name="sb">A <see cref="StringBuilder"/> object to append processed data from <paramref name="brush"/> to.</param>
 private void ParseBrush(MAPBrush brush, int index, StringBuilder sb)
 {
     // Unsupported features. Ignore these completely.
     if (brush.patch != null || brush.ef2Terrain != null || brush.mohTerrain != null)
     {
         return;
     }
     if (brush.sides.Count < 4)
     {
         // Can't create a brush with less than 4 sides
         _master.Print("WARNING: Tried to create brush from " + brush.sides.Count + " sides!");
         return;
     }
     sb.Append("{ // Brush ")
     .Append(index.ToString())
     .Append("\r\n");
     if (brush.isDetail)
     {
         sb.Append("\"BRUSHFLAGS\" \"DETAIL\"\r\n");
     }
     foreach (MAPBrushSide brushSide in brush.sides)
     {
         ParseBrushSide(brushSide, sb);
     }
     sb.Append("}\r\n");
 }
 /// <summary>
 /// Process the data in a <see cref="MAPBrush"/> into the passed <see cref="StringBuilder"/>.
 /// </summary>
 /// <param name="brush">The <see cref="MAPBrush"/> to process.</param>
 /// <param name="index">The index of <see cref="MAPBrush"/> entity in the <see cref="Entity"/>.</param>
 /// <param name="sb">A <see cref="StringBuilder"/> object to append processed data from <paramref name="brush"/> to.</param>
 private void ParseBrush(MAPBrush brush, int index, StringBuilder sb)
 {
     // Unsupported features. Ignore these completely.
     if (brush.ef2Terrain != null)
     {
         return;
     }
     if (brush.sides.Count < 4 && brush.patch == null && brush.mohTerrain == null)
     {
         // Can't create a brush with less than 4 sides
         _master.Print("WARNING: Tried to create brush from " + brush.sides.Count + " sides!");
         return;
     }
     sb.Append("// Brush ")
     .Append(index.ToString())
     .Append("\r\n");
     if (brush.patch != null)
     {
         ParsePatch(brush.patch, sb);
     }
     else if (brush.mohTerrain != null)
     {
         ParseTerrain(brush.mohTerrain, sb);
     }
     else
     {
         sb.Append("{\r\n");
         foreach (MAPBrushSide brushSide in brush.sides)
         {
             ParseBrushSide(brushSide, brush.isDetail, sb);
         }
         sb.Append("}\r\n");
     }
 }
 /// <summary>
 /// For <paramref name="brush"/>, sets the top <see cref="MAPBrushSide"/>'s texture
 /// to a water texture and sets all others to nodraw.
 /// </summary>
 /// <param name="brush">The <see cref="MAPBrush"/> to make into a water brush.</param>
 private void ConvertToWater(MAPBrush brush)
 {
     foreach (MAPBrushSide side in brush.sides)
     {
         side.texture = "common/water";
     }
 }
 /// <summary>
 /// For <paramref name="brush"/>, sets the top <see cref="MAPBrushSide"/>'s texture
 /// to a water texture and sets all others to nodraw.
 /// </summary>
 /// <param name="brush">The <see cref="MAPBrush"/> to make into a water brush.</param>
 private void ConvertToWater(MAPBrush brush)
 {
     foreach (MAPBrushSide side in brush.sides)
     {
         side.texture = "Shaders/liquids/clear_calm1";
     }
 }
 /// <summary>
 /// Sends <paramref name="entity"/> to be postprocessed into the appropriate method based on version.
 /// </summary>
 /// <param name="entity"><see cref="Entity"/> to postprocess.</param>
 private void PostProcessEntity(Entity entity)
 {
     if (entity.IsBrushBased)
     {
         Vector3 origin = entity.Origin;
         entity.Remove("origin");
         entity.Remove("model");
         if (origin != Vector3.Zero)
         {
             // If this brush has an origin
             MAPBrush neworiginBrush = MAPBrushExtensions.CreateCube(new Vector3(-16, -16, -16), new Vector3(16, 16, 16), "common/origin");
             entity.brushes.Add(neworiginBrush);
         }
         foreach (MAPBrush brush in entity.brushes)
         {
             brush.Translate(origin);
         }
     }
     switch (_version)
     {
     case MapType.Nightfire: {
         PostProcessNightfireEntity(entity);
         break;
     }
     }
 }
Esempio n. 9
0
    private byte[] brushToByteArray(MAPBrush inBrush, int num)
    {
        if (inBrush.NumSides < 4)
        {
            // Can't create a brush with less than 4 sides
            DecompilerThread.OnMessage(this, "WARNING: Tried to create brush from " + inBrush.NumSides + " sides!");
            return(new byte[0]);
        }
        string brush = "// primitive " + num + (char)0x0A + "{" + (char)0x0A + " brushDef3" + (char)0x0A + " {" + (char)0x0A;

        for (int i = 0; i < inBrush.NumSides; i++)
        {
            brush += ("  " + brushSideToString(inBrush[i]) + (char)0x0A);
        }
        brush += (" }" + (char)0x0A + "}" + (char)0x0A);
        if (brush.Length < 58)
        {
            // Any brush this short contains no sides.
            DecompilerThread.OnMessage(this, "WARNING: Brush with no sides being written! Oh no!");
            return(new byte[0]);
        }
        else
        {
            byte[] brushbytes = new byte[brush.Length];
            for (int i = 0; i < brush.Length; i++)
            {
                brushbytes[i] = (byte)brush[i];
            }
            return(brushbytes);
        }
    }
 /// <summary>
 /// For <paramref name="brush"/>, sets the top <see cref="MAPBrushSide"/>'s texture
 /// to a water texture and sets all others to nodraw.
 /// </summary>
 /// <param name="brush">The <see cref="MAPBrush"/> to make into a water brush.</param>
 private void ConvertToWater(MAPBrush brush)
 {
     foreach (MAPBrushSide side in brush.sides)
     {
         side.texture = "test/omaha_pjspick5";
     }
 }
Esempio n. 11
0
	public MAPBrush(MAPBrush sides) {
		brushnum = sides.Brushnum;
		entnum = sides.Entnum;
		this.isDetailBrush = sides.Detail;
		this.isWaterBrush = sides.Water;
		this.sides = new MAPBrushSide[sides.NumSides];
		for (int i = 0; i < sides.NumSides; i++) {
			this.sides[i] = new MAPBrushSide(sides[i]);
		}
	}
Esempio n. 12
0
 public MAPBrush(MAPBrush sides)
 {
     brushnum           = sides.Brushnum;
     entnum             = sides.Entnum;
     this.isDetailBrush = sides.Detail;
     this.isWaterBrush  = sides.Water;
     this.sides         = new MAPBrushSide[sides.NumSides];
     for (int i = 0; i < sides.NumSides; i++)
     {
         this.sides[i] = new MAPBrushSide(sides[i]);
     }
 }
        /// <summary>
        /// Postprocesser to convert an <see cref="Entity"/> from a Call of Duty BSP to one for Gearcraft.
        /// </summary>
        /// <param name="entity">The <see cref="Entity"/> to parse.</param>
        private void PostProcessCoDEntity(Entity entity)
        {
            if (entity.IsBrushBased)
            {
                Vector3 origin = entity.Origin;
                entity.Remove("origin");
                entity.Remove("model");
                if (entity["classname"].ToUpper().Equals("func_rotating".ToUpper()))
                {
                    // TODO: What entities require origin brushes in CoD?
                    if (origin == Vector3.Zero)
                    {
                        // If this brush uses the "origin" attribute
                        MAPBrush neworiginBrush = MAPBrushExtensions.CreateCube(new Vector3(-16, -16, -16), new Vector3(16, 16, 16), "special/origin");
                        entity.brushes.Add(neworiginBrush);
                    }
                }
                foreach (MAPBrush brush in entity.brushes)
                {
                    brush.Translate(origin);
                }
            }

            switch (entity["classname"].ToLower())
            {
            case "light": {
                entity["_light"] = "255 255 255 " + entity["light"];
                entity.Remove("light");
                break;
            }

            case "mp_teamdeathmatch_spawn":
            case "mp_deathmatch_spawn": {
                entity["classname"] = "info_player_deathmatch";
                break;
            }

            case "mp_searchanddestroy_spawn_allied": {
                entity["classname"] = "info_player_ctfspawn";
                entity["team_no"]   = "1";
                entity.Remove("model");
                break;
            }

            case "mp_searchanddestroy_spawn_axis": {
                entity["classname"] = "info_player_ctfspawn";
                entity["team_no"]   = "2";
                entity.Remove("model");
                break;
            }
            }
        }
Esempio n. 14
0
    // SimpleCorrectPlanes(MAPBrush, float)
    // Uses all sides' defined points to ensure all planes are flipped correctly.
    public static MAPBrush SimpleCorrectPlanes(MAPBrush brush)
    {
        //DecompilerThread.OnMessage(this, "Plane flip. Method: simple");
        // Find midpoint of triangle, and use that to normalise all other planes.
        int triIndex = -1;                     // So we know which plane the triangle belongs to.

        Vector3D[] triangle = new Vector3D[0]; // This'll cause an exception if the loop fails
        for (int i = 0; i < brush.NumSides; i++)
        {
            if (brush[i].DefinedByTriangle)
            {
                triangle = brush[i].Triangle;
                triIndex = i;
                break;
            }
        }
        double[] normPoint = new double[] { (triangle[0].X + triangle[1].X + triangle[2].X) / 3.0, (triangle[0].Y + triangle[1].Y + triangle[2].Y) / 3.0, (triangle[0].Z + triangle[1].Z + triangle[2].Z) / 3.0 };
        Plane[]  allplanes = brush.Planes;
        //bool foundTriPlane = false;
        for (int iPlane = 0; iPlane < allplanes.Length; iPlane++)
        {
            // For each plane
            double dist = allplanes[iPlane].distance(normPoint);             // calculate distance from point
            if (iPlane == triIndex)
            {
                // if triangle's plane & normals point in opposite direction
                Vector3D tmp = new Plane(triangle[2], triangle[0], triangle[1]).Normal;
                if (allplanes[iPlane].Normal * (new Plane(triangle[2], triangle[0], triangle[1]).Normal) < 0)
                {
                    brush[iPlane].flipSide();                     // flip plane
                }
            }
            else
            {
                // or if not the triangle's plane
                if (dist > Settings.precision)
                {
                    // if point is on positive (outside) side of plane
                    brush[iPlane].flipSide();                     // flip plane
                }
            }
        }

        return(brush);
    }
Esempio n. 15
0
 /// <summary>
 /// Postprocesser to convert an <see cref="Entity"/> from a Nightfire BSP to one for Gearcraft.
 /// </summary>
 /// <param name="entity">The <see cref="Entity"/> to parse.</param>
 private void PostProcessNightfireEntity(Entity entity)
 {
     if (entity.brushBased)
     {
         Vector3d origin = entity.origin;
         entity.Remove("origin");
         entity.Remove("model");
         if (origin != Vector3d.zero)
         {
             // If this brush has an origin
             MAPBrush neworiginBrush = MAPBrushExtensions.CreateCube(new Vector3d(-16, -16, -16), new Vector3d(16, 16, 16), "special/origin");
             entity.brushes.Add(neworiginBrush);
         }
         foreach (MAPBrush brush in entity.brushes)
         {
             brush.Translate(origin);
         }
     }
 }
Esempio n. 16
0
 public virtual Entity ent42ToEntM510(Entity inputData)
 {
     if (inputData.BrushBased)
     {
         Vector3D origin = inputData.Origin;
         inputData.Attributes.Remove("origin");
         inputData.Attributes.Remove("model");
         if ((origin[0] != 0 || origin[1] != 0 || origin[2] != 0) && !Settings.noOriginBrushes)
         {
             // If this brush uses the "origin" attribute
             MAPBrush newOriginBrush = MAPBrush.createBrush(new Vector3D(-Settings.originBrushSize, -Settings.originBrushSize, -Settings.originBrushSize), new Vector3D(Settings.originBrushSize, Settings.originBrushSize, Settings.originBrushSize), "special/origin");
             inputData.Brushes.Add(newOriginBrush);
         }
         for (int i = 0; i < inputData.Brushes.Count; i++)
         {
             MAPBrush currentBrush = inputData.Brushes[i];
             currentBrush.translate(new Vector3D(origin));
         }
     }
     return(inputData);
 }
Esempio n. 17
0
 /// <summary>
 /// Process the data in a <see cref="MAPBrush"/> into the passed <see cref="StringBuilder"/>.
 /// </summary>
 /// <param name="brush">The <see cref="MAPBrush"/> to process.</param>
 /// <param name="sb">A <see cref="StringBuilder"/> object to append processed data from <paramref name="brush"/> to.</param>
 private void ParseBrush(MAPBrush brush, StringBuilder sb)
 {
     // Unsupported features. Ignore these completely.
     if (brush.patch != null || brush.ef2Terrain != null || brush.mohTerrain != null)
     {
         return;
     }
     if (brush.sides.Count < 4)
     {
         // Can't create a brush with less than 4 sides
         _master.Print("WARNING: Tried to create brush from " + brush.sides.Count + " sides!");
         return;
     }
     sb.Append("\tsolid\r\n\t{\r\n\t\t\"id\" \"")
     .Append(_nextID)
     .Append("\"\r\n");
     foreach (MAPBrushSide brushSide in brush.sides)
     {
         ++_nextID;
         ParseBrushSide(brushSide, sb);
     }
     sb.Append("\t}\r\n");
 }
 /// <summary>
 /// Moves this <see cref="MAPBrush"/> object in the world by the vector <paramref name="v"/>.
 /// </summary>
 /// <param name="mapBrush">This <see cref="MAPBrush"/>.</param>
 /// <param name="v">Translation vector.</param>
 public static void Translate(this MAPBrush mapBrush, Vector3d v)
 {
     if (v == Vector3d.zero)
     {
         return;
     }
     foreach (MAPBrushSide side in mapBrush.sides)
     {
         side.Translate(v);
         if (side.displacement != null)
         {
             side.displacement.start += v;
         }
     }
     if (mapBrush.patch != null)
     {
         mapBrush.patch.Translate(v);
     }
     if (mapBrush.ef2Terrain != null)
     {
         mapBrush.ef2Terrain.start += v;
     }
 }
        /// <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);
        }
Esempio n. 20
0
	private byte[] brushToByteArray(MAPBrush inputData, int num) {
		if (inputData.NumSides < 4) {
			// Can't create a brush with less than 4 sides
			DecompilerThread.OnMessage(this, "WARNING: Tried to create brush from " + inputData.NumSides + " sides!");
			return new byte[0];
		}
		string brush = "{ // Brush " + num + (char) 0x0D + (char) 0x0A;
		if ((inputData.Detail || inputData[0].Displacement != null) && currentEntity == 0)
		{
			brush += ("\"BRUSHFLAGS\" \"DETAIL\"" + (char) 0x0D + (char) 0x0A);
		}
		for (int i = 0; i < inputData.NumSides; i++)
		{
			brush += (brushSideToString(inputData[i]) + (char) 0x0D + (char) 0x0A);
		}
		brush += ("}" + (char) 0x0D + (char) 0x0A);
		if (brush.Length < 45) {
			// Any brush this short contains no sides.
			DecompilerThread.OnMessage(this, "WARNING: Brush with no sides being written! Oh no!");
			return new byte[0];
		}
		else
		{
			byte[] brushbytes = new byte[brush.Length];
			for (int i = 0; i < brush.Length; i++)
			{
				brushbytes[i] = (byte) brush[i];
			}
			return brushbytes;
		}
	}
Esempio n. 21
0
        /// <summary>
        /// Processes an <see cref="Entity"/> into a state where it can be output into a file that map editors can read.
        /// </summary>
        /// <param name="entity">The <see cref="Entity"/> to process.</param>
        /// <remarks>This method does not return anything, since the <see cref="Entity"/> object is modified by reference.</remarks>
        private void ProcessEntity(Entity entity)
        {
            int modelNumber = entity.modelNumber;

            // If this Entity has no modelNumber, then this is a no-op. No processing is needed.
            // A modelnumber of 0 indicates the world entity.
            if (modelNumber >= 0)
            {
                Model model = _bsp.models[modelNumber];
                if (_bsp.brushes != null)
                {
                    List <Brush> brushes = _bsp.GetBrushesInModel(model);
                    if (brushes != null)
                    {
                        foreach (Brush brush in brushes)
                        {
                            MAPBrush result = ProcessBrush(brush, entity.origin);
                            result.isWater |= (entity.className == "func_water");
                            if (_master.settings.brushesToWorld)
                            {
                                _bsp.entities.GetWithAttribute("classname", "worldspawn").brushes.Add(result);
                            }
                            else
                            {
                                entity.brushes.Add(result);
                            }
                            ++_itemsProcessed;
                            ReportProgress();
                        }
                    }
                }
                if (model.numLeafPatches > 0 && _bsp.markSurfaces != null && _bsp.patches != null)
                {
                    HashSet <Patch> patches            = new HashSet <Patch>();
                    List <long>     leafPatchesInModel = _bsp.GetReferencedObjects <long>(model, "markSurfaces");
                    foreach (long leafPatch in leafPatchesInModel)
                    {
                        if (leafPatch >= 0)
                        {
                            patches.Add(_bsp.patches[(int)leafPatch]);
                        }
                    }
                    foreach (Patch patch in patches)
                    {
                        if (_bsp.version != MapType.CoD || patch.patchType == 0)
                        {
                            MAPPatch mappatch = ProcessPatch(patch);
                            MAPBrush newBrush = new MAPBrush();
                            newBrush.patch = mappatch;
                            entity.brushes.Add(newBrush);
                        }
                    }
                }
                if (_bsp.faces != null)
                {
                    List <Face> surfaces = _bsp.GetFacesInModel(model);
                    foreach (Face face in surfaces)
                    {
                        if (face.displacement >= 0)
                        {
                            if (modelNumber != 0)
                            {
                                _master.Print("WARNING: Displacement not part of world in " + _bsp.MapNameNoExtension);
                            }
                            MAPDisplacement displacement = ProcessDisplacement(_bsp.dispInfos[face.displacement]);
                            MAPBrush        newBrush     = face.CreateBrush(_bsp, 32);
                            newBrush.sides[0].displacement = displacement;
                            // If we are not decompiling to VMF, vis will need to skip this brush.
                            newBrush.isDetail = true;
                            entity.brushes.Add(newBrush);
                        }
                        else if (face.flags == 2)
                        {
                            MAPPatch patch    = ProcessPatch(face);
                            MAPBrush newBrush = new MAPBrush();
                            newBrush.patch = patch;
                            entity.brushes.Add(newBrush);
                        }
                        else if ((_bsp.version == MapType.STEF2 || _bsp.version == MapType.STEF2Demo) && face.flags == 5)
                        {
                            if (modelNumber != 0)
                            {
                                _master.Print("WARNING: Terrain not part of world in " + _bsp.MapNameNoExtension);
                            }
                            MAPTerrainEF2 terrain  = ProcessEF2Terrain(face);
                            MAPBrush      newBrush = new MAPBrush();
                            newBrush.ef2Terrain = terrain;
                            entity.brushes.Add(newBrush);
                        }
                    }
                }

                // If this is model 0 (worldspawn) there are other things that need to be taken into account.
                if (modelNumber == 0)
                {
                    if (_bsp.lodTerrains != null)
                    {
                        foreach (LODTerrain lodTerrain in _bsp.lodTerrains)
                        {
                            MAPTerrainMoHAA terrain  = ProcessTerrainMoHAA(lodTerrain);
                            MAPBrush        newBrush = new MAPBrush();
                            newBrush.mohTerrain = terrain;
                            entity.brushes.Add(newBrush);
                        }
                    }
                }
                entity.Remove("model");
            }
        }
	// -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);
		}
	}
Esempio n. 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);
    }
Esempio n. 24
0
	// Calculates 3 face corners, to be used to define the plane in ASCII format.
	/// Author:		UltimateSniper
	/// Returns:	List of normalised plane vertex triplets.
	public static MAPBrush CalcBrushVertices(MAPBrush mapBrush) {
		//DecompilerThread.OnMessage(this, "Recalculating vertices");
		Plane[] planes = mapBrush.Planes;
		Vector3D[][] out_Renamed = new Vector3D[planes.Length][];
		// For each triplet of planes, find intersect point.
		for (int iP1 = 0; iP1 < planes.Length; iP1++) {
			for (int iP2 = iP1 + 1; iP2 < planes.Length; iP2++) {
				for (int iP3 = iP2 + 1; iP3 < planes.Length; iP3++) {
					Vector3D testV = planes[iP1].trisect(planes[iP2], planes[iP3]);
					if (testV!=Vector3D.UNDEFINED) {
						bool isCorner = true;
						// If point is not null, test if point is behind/on all planes (if so, it is a corner).
						for (int iTest = 0; iTest < planes.Length; iTest++) {
							if (planes[iTest].Normal!=planes[iP1].Normal && planes[iTest].Normal!=planes[iP2].Normal && planes[iTest].Normal!=planes[iP3].Normal) {
								if (planes[iTest].distance(testV) > Settings.precision) {
									isCorner = false;
									break;
								}
							}
						}
						// If so, check which planes it is on.
						if (isCorner) {
							for (int iChk = 0; iChk < planes.Length; iChk++) {
								// If on this plane, and plane's vertex triplet missing min 1 point (and does not already have this point), add it.
								double dist = planes[iChk].distance(testV);
								if (System.Math.Abs(dist) <= Settings.precision) {
									// If first point on this plane, must create array.
									if (out_Renamed[iChk] == null) {
										out_Renamed[iChk] = new Vector3D[]{new Vector3D(testV), null, null};
									} else {
										// Check each value in the array for open spot OR identical point.
										for (int iChk2 = 0; iChk2 < 3; iChk2++) {
											// Open spot, fill it.
											if (out_Renamed[iChk][iChk2] == null) {
												out_Renamed[iChk][iChk2] = new Vector3D(testV);
												// If this is now a complete plane.
												if (iChk2 == 2) {
													// Order complete triplet to make a plane facing same way as given plane.
													Plane testP = new Plane(out_Renamed[iChk][0], out_Renamed[iChk][1], out_Renamed[iChk][2]);
													// If normals are not pointing in same direction, re-order points.
													if (testP.Normal*planes[iChk].Normal < 0) {
														Vector3D temp = new Vector3D(out_Renamed[iChk][1]);
														out_Renamed[iChk][1] = new Vector3D(out_Renamed[iChk][2]);
														out_Renamed[iChk][2] = temp;
													}
												}
												break;
												// Else, if this list already has this point, skip out (to avoid doubling it).
											} else if (out_Renamed[iChk][iChk2]==testV) {
												break;
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
		for (int i = 0; i < mapBrush.NumSides; i++) {
			mapBrush[i].setSide(mapBrush[i].Plane, out_Renamed[i]);
		}
		return mapBrush;
	}
Esempio n. 25
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;
	}
Esempio n. 26
0
	// SimpleCorrectPlanes(MAPBrush, float)
	// Uses all sides' defined points to ensure all planes are flipped correctly.
	public static MAPBrush SimpleCorrectPlanes(MAPBrush brush)
	{
		//DecompilerThread.OnMessage(this, "Plane flip. Method: simple");
		// Find midpoint of triangle, and use that to normalise all other planes.
		int triIndex = - 1; // So we know which plane the triangle belongs to.
		Vector3D[] triangle = new Vector3D[0]; // This'll cause an exception if the loop fails
		for (int i = 0; i < brush.NumSides; i++)
		{
			if (brush[i].DefinedByTriangle)
			{
				triangle = brush[i].Triangle;
				triIndex = i;
				break;
			}
		}
		double[] normPoint = new double[]{(triangle[0].X + triangle[1].X + triangle[2].X) / 3.0, (triangle[0].Y + triangle[1].Y + triangle[2].Y) / 3.0, (triangle[0].Z + triangle[1].Z + triangle[2].Z) / 3.0};
		Plane[] allplanes = brush.Planes;
		//bool foundTriPlane = false;
		for (int iPlane = 0; iPlane < allplanes.Length; iPlane++)
		{
			// For each plane
			double dist = allplanes[iPlane].distance(normPoint); // calculate distance from point
			if (iPlane == triIndex)
			{
				// if triangle's plane & normals point in opposite direction
				Vector3D tmp = new Plane(triangle[2], triangle[0], triangle[1]).Normal;
				if (allplanes[iPlane].Normal*(new Plane(triangle[2], triangle[0], triangle[1]).Normal) < 0)
				{
					brush[iPlane].flipSide(); // flip plane
				}
			}
			else
			{
				// or if not the triangle's plane
				if (dist > Settings.precision)
				{
					// if point is on positive (outside) side of plane
					brush[iPlane].flipSide(); // flip plane
				}
			}
		}
		
		return brush;
	}
	// -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);
		}
	}
    // -entityToByteArray()
    // Converts the entity and its brushes into byte arrays rather than Strings,
    // which can then be written to a file much faster. Concatenating Strings is
    // a costly operation, especially when hundreds of thousands of Strings are
    // in play. This is one of two parts to writing a file quickly. The second
    // part is to call the FileOutputStream.write() method only once, with a
    // gigantic array, rather than several times with many small arrays. File I/O
    // from a hard drive is another costly operation, best done by handling
    // massive amounts of data in one go, rather than tiny amounts of data thousands
    // of times.
    private byte[] entityToByteArray(Entity inputData, int num)
    {
        byte[]   outputData;
        Vector3D origin;

        if (inputData.BrushBased)
        {
            origin = inputData.Origin;
            inputData.Remove("origin");
            inputData.Remove("model");
            if (origin[0] != 0 || origin[1] != 0 || origin[2] != 0)
            {
                // If this entity uses the "origin" attribute
                MAPBrush newOriginBrush = MAPBrush.createBrush(new Vector3D(-Settings.originBrushSize, -Settings.originBrushSize, -Settings.originBrushSize), new Vector3D(Settings.originBrushSize, Settings.originBrushSize, Settings.originBrushSize), "special/origin");
                inputData.Brushes.Add(newOriginBrush);
            }
        }
        else
        {
            origin = Vector3D.ZERO;
        }
        string temp = "";

        if (!inputData["classname"].Equals("worldspawn", StringComparison.InvariantCultureIgnoreCase))
        {
            temp += "// entity " + num + (char)0x0D + (char)0x0A;
        }
        temp += "{";
        int len = temp.Length + 5;

        // Get the lengths of all attributes together
        foreach (string key in inputData.Attributes.Keys)
        {
            len += key.Length + inputData[key].Length + 7;             // Four quotes, a space and a newline
        }
        outputData = new byte[len];
        int offset = 0;

        for (int i = 0; i < temp.Length; i++)
        {
            outputData[offset++] = (byte)temp[i];
        }
        outputData[offset++] = (byte)0x0D;
        outputData[offset++] = (byte)0x0A;
        foreach (string key in inputData.Attributes.Keys)
        {
            // For each attribute
            outputData[offset++] = (byte)'\"';             // 1
            for (int j = 0; j < key.Length; j++)
            {
                // Then for each byte in the attribute
                outputData[offset++] = (byte)key[j];      // add it to the output array
            }
            outputData[offset++] = (byte)'\"';            // 2
            outputData[offset++] = (byte)' ';             // 3
            outputData[offset++] = (byte)'\"';            // 4
            for (int j = 0; j < inputData.Attributes[key].Length; j++)
            {
                // Then for each byte in the attribute
                outputData[offset++] = (byte)inputData.Attributes[key][j]; // add it to the output array
            }
            outputData[offset++] = (byte)'\"';                             // 5
            outputData[offset++] = (byte)0x0D;                             // 6
            outputData[offset++] = (byte)0x0A;                             // 7
        }
        int brushArraySize = 0;

        byte[][] brushes = new byte[inputData.Brushes.Count][];
        for (int j = 0; j < inputData.Brushes.Count; j++)
        {
            brushes[j]      = brushToByteArray(inputData.Brushes[j], j);
            brushArraySize += brushes[j].Length;
        }
        int brushoffset = 0;

        byte[] brushArray = new byte[brushArraySize];
        for (int j = 0; j < inputData.Brushes.Count; j++)
        {
            // For each brush in the entity
            for (int k = 0; k < brushes[j].Length; k++)
            {
                brushArray[brushoffset + k] = brushes[j][k];
            }
            brushoffset += brushes[j].Length;
        }
        if (brushArray.Length != 0)
        {
            len += brushArray.Length;
            byte[] newOut = new byte[len];
            for (int j = 0; j < outputData.Length; j++)
            {
                newOut[j] = outputData[j];
            }
            for (int j = 0; j < brushArray.Length; j++)
            {
                newOut[j + outputData.Length - 3] = brushArray[j];
            }
            offset    += brushArray.Length;
            outputData = newOut;
        }
        outputData[offset++] = (byte)'}';
        outputData[offset++] = (byte)0x0D;
        outputData[offset++] = (byte)0x0A;
        return(outputData);
    }
	private byte[] brushToByteArray(MAPBrush inData, int num) {
		if (inData.Patch != null) {
			return patchToByteArray(inData.Patch, num);
		}
		if (inData.NumSides < 4) {
			// Can't create a brush with less than 4 sides
			DecompilerThread.OnMessage(this, "WARNING: Tried to create brush from " + inData.NumSides + " sides!");
			return new byte[0];
		}
		string brush = "// brush " + num + (char) 0x0D + (char) 0x0A + "{" + (char) 0x0D + (char) 0x0A;
		for (int i = 0; i < inData.NumSides; i++) {
			brush += (brushSideToString(inData[i], (inData.Detail || inData[0].Displacement != null)) + (char) 0x0D + (char) 0x0A);
		}
		brush += ("}" + (char) 0x0D + (char) 0x0A);
		if (brush.Length < 45) {
			// Any brush this short contains no sides.
			DecompilerThread.OnMessage(this, "WARNING: Brush with no sides being written! Oh no!");
			return new byte[0];
		} else {
			byte[] brushbytes = new byte[brush.Length];
			for (int i = 0; i < brush.Length; i++) {
				brushbytes[i] = (byte) brush[i];
			}
			return brushbytes;
		}
	}
        /// <summary>
        /// Postprocesser to convert an <see cref="Entity"/> from a Quake 3-based BSP to one for Gearcraft.
        /// </summary>
        /// <param name="entity">The <see cref="Entity"/> to parse.</param>
        private void PostProcessQuake3Entity(Entity entity)
        {
            if (entity.IsBrushBased)
            {
                Vector3 origin = entity.Origin;
                entity.Remove("origin");
                entity.Remove("model");
                if (entity.ValueIs("classname", "func_rotating") || entity.ValueIs("classname", "func_rotatingdoor"))
                {
                    // TODO: What entities require origin brushes in Quake 3?
                    if (origin != Vector3.Zero)
                    {
                        MAPBrush neworiginBrush = MAPBrushExtensions.CreateCube(new Vector3(-16, -16, -16), new Vector3(16, 16, 16), "special/origin");
                        entity.brushes.Add(neworiginBrush);
                    }
                }
                foreach (MAPBrush brush in entity.brushes)
                {
                    brush.Translate(origin);
                }
            }

            switch (entity["classname"].ToLower())
            {
            case "worldspawn": {
                if (!entity["suncolor"].Equals(""))
                {
                    Entity light_environment = new Entity("light_environment");
                    light_environment["_light"] = entity["suncolor"];
                    light_environment["angles"] = entity["sundirection"];
                    light_environment["_fade"]  = entity["sundiffuse"];
                    entity.Remove("suncolor");
                    entity.Remove("sundirection");
                    entity.Remove("sundiffuse");
                    entity.Remove("sundiffusecolor");
                    _entities.Add(light_environment);
                }
                break;
            }

            case "team_ctf_blueflag": {
                // Blue flag
                entity["classname"] = "item_ctfflag";
                entity["skin"]      = "1";                    // 0 for PHX, 1 for MI6
                entity["goal_no"]   = "1";                    // 2 for PHX, 1 for MI6
                entity["goal_max"]  = "16 16 72";
                entity["goal_min"]  = "-16 -16 0";
                entity["model"]     = "models/ctf_flag.mdl";
                Entity flagBase = new Entity("item_ctfbase");
                flagBase["origin"]   = entity["origin"];
                flagBase["angles"]   = entity["angles"];
                flagBase["angle"]    = entity["angle"];
                flagBase["goal_no"]  = "1";
                flagBase["model"]    = "models/ctf_flag_stand_mi6.mdl";
                flagBase["goal_max"] = "16 16 72";
                flagBase["goal_min"] = "-16 -16 0";
                _entities.Add(flagBase);
                break;
            }

            case "team_ctf_redflag": {
                // Red flag
                entity["classname"] = "item_ctfflag";
                entity["skin"]      = "0";                    // 0 for PHX, 1 for MI6
                entity["goal_no"]   = "2";                    // 2 for PHX, 1 for MI6
                entity["goal_max"]  = "16 16 72";
                entity["goal_min"]  = "-16 -16 0";
                entity["model"]     = "models/ctf_flag.mdl";
                Entity flagBase = new Entity("item_ctfbase");
                flagBase["origin"]   = entity["origin"];
                flagBase["angles"]   = entity["angles"];
                flagBase["angle"]    = entity["angle"];
                flagBase["goal_no"]  = "2";
                flagBase["model"]    = "models/ctf_flag_stand_phoenix.mdl";
                flagBase["goal_max"] = "16 16 72";
                flagBase["goal_min"] = "-16 -16 0";
                _entities.Add(flagBase);
                break;
            }

            case "team_ctf_redspawn":
            case "info_player_axis": {
                entity["classname"] = "info_ctfspawn";
                entity["team_no"]   = "2";
                goto case "info_player_start";
            }

            case "team_ctf_bluespawn":
            case "info_player_allied": {
                entity["classname"] = "info_ctfspawn";
                entity["team_no"]   = "1";
                goto case "info_player_start";
            }

            case "info_player_start":
            case "info_player_coop":
            case "info_player_deathmatch": {
                Vector3 origin = entity.Origin;
                entity["origin"] = origin.X + " " + origin.Y + " " + (origin.Z + 24);
                break;
            }

            case "light": {
                Vector4 color;
                if (entity.ContainsKey("_color"))
                {
                    color = entity.GetVector("_color");
                }
                else
                {
                    color = Vector4.One;
                }
                color *= 255;
                float intensity = entity.GetFloat("light", 1);
                entity.Remove("_color");
                entity.Remove("light");
                entity["_light"] = color.X + " " + color.Y + " " + color.Z + " " + intensity;
                break;
            }

            case "func_rotatingdoor": {
                entity["classname"] = "func_door_rotating";
                break;
            }

            case "info_pathnode": {
                entity["classname"] = "info_node";
                break;
            }

            case "trigger_ladder": {
                entity["classname"] = "func_ladder";
                break;
            }

            case "trigger_use": {
                entity["classname"]  = "func_button";
                entity["spawnflags"] = "1";
                entity["wait"]       = "1";
                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;
		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);
		}
	}
Esempio n. 32
0
	// Use if brush has no triangles.
	/// Author:		UltimateSniper
	/// Returns:	Ordered list of normalised vertex triplets (ready to feed in to map).
	public static MAPBrush AdvancedCorrectPlanes(MAPBrush mapBrush)
	{
		//DecompilerThread.OnMessage(this, "Plane flip. Method: advanced");
		Plane[] allplanes = mapBrush.Planes;
		// Method:
		//1. Collect all vertices created by plane intercepts.
		//2. Create arrays of these vertices and inputted planes, to access planes via points they intersect and vice versa.
		//  MORE IMPORTANTLY, create an array indicating sides of each plane each vertex is on (-1 for -, 0 for on, 1 for +).
		//3. Run through each possible cavity (each combination of sides of each plane), collecting satisfying vertices.
		//	Correct cavity is found when there are at least 3 vertices on each plane.
		//	If fail, returns Vector3D[0][].
		//4. Generate central point of brush, and use it to produce normalised vertex triplets to return.
		
		// 1. Collect all plane intersects (all possible corners).
		//Find MaxVerts: max = N!/3!(N-3)! = (1/3!) * (N/(N-3)) * ((N-1)/(N-4)) * ((N-2)/(N-5)) * ... * (5/2) * 4 * 3!
		double dmaxVerts = 4.0;
		for (int iP = allplanes.Length; iP > 4; iP--)
		{
			dmaxVerts *= iP / (iP - 3.00);
		}
		//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'"
		Vector3D[] allverts = new Vector3D[(int) dmaxVerts]; // Max possible number of unique plane trisects: nC3.
		int iallverts = 0; // Pointer, so we know next vacant index.
		for (int iP1 = 0; iP1 < allplanes.Length; iP1++)
		{
			for (int iP2 = iP1 + 1; iP2 < allplanes.Length; iP2++)
			{
				for (int iP3 = iP2 + 1; iP3 < allplanes.Length; iP3++)
				{
					Vector3D testV = allplanes[iP1].trisect(allplanes[iP2], allplanes[iP3]);
					if (testV!=Vector3D.UNDEFINED)
					{
						// Arbitrary precision: Just checking if UNDEFINED or real.
						bool hasVtx = false;
						for (int iVtx = 0; iVtx < iallverts; iVtx++)
						{
							if (allverts[iVtx]==testV)
							{
								hasVtx = true;
								break;
							}
						}
						if (!hasVtx)
						{
							allverts[iallverts] = testV;
							iallverts++;
						}
					}
				}
			}
		}
		Vector3D[] tmp = new Vector3D[iallverts];
		Array.Copy(allverts, 0, tmp, 0, iallverts);
		allverts = tmp;
		
		
		// 2. Make array to access verts' sides of planes (can also be used to check if vert is on plane).
		sbyte[][] VertPlaneSides = new sbyte[allverts.Length][];
		for (int iV = 0; iV < allverts.Length; iV++)
		{
			sbyte[] PlaneSides = new sbyte[allplanes.Length];
			for (int iVP = 0; iVP < allplanes.Length; iVP++)
			{
				double dist = allplanes[iVP].distance(allverts[iV]);
				if (System.Math.Abs(dist) < Settings.precision)
				{
					PlaneSides[iVP] = 0;
				}
				else
				{
					PlaneSides[iVP] = ((dist >= Settings.precision)?(sbyte) 1:(sbyte) (- 1));
				}
			}
			VertPlaneSides[iV] = PlaneSides;
		}
		
		// THEORY: Collect vertices that are all either on, or on the same side of all planes.
		//		 If there are at least 3 vertices on each plane, then this is the correct shape.
		//  NOTES: -Some vertices may be included in multiple collections.
		//		 -Must have 3 vertices sharing 1 plane, and a fourth which is not on that plane to start 'cavity'.
		//		 -4 defining vertices cannot make more than 1 'cavity'.
		
		
		// Java is retarded, as it doesn't allow uints. This causes me a serious problem with this, because I am not sure
		// whether or not bitwise operations will return positive values, or switch the signs, or switch the bit order...
		
		// Cannot handle more than the max positive val for a long, and need a bit to represent each plane.
		if (allplanes.Length > 62)
		{
			throw new System.IndexOutOfRangeException("More than 62 planes in brush!");
		}
		
		
		// 3. Find all vertices which satisfy each possible cavity, and break when true brush is found.
		// Let the madness of this one great f*****g for-loop commence...
		int[] TrueCorns = new int[0];
		
		for (long lCav = 0; lCav < (1 << allplanes.Length); lCav++)
		{
			int[] Corns = new int[allverts.Length];
			int iCorns = 0;
			for (int iCorn = 0; iCorn < allverts.Length; iCorn++)
			{
				bool addable = true;
				for (int iPlane = 0; iPlane < allplanes.Length; iPlane++)
				{
					// Get bit value of lCav which represents this plane (true = +, false = -), check if vert is addable.
					if (((lCav >> iPlane) & 1) == 1)
					{
						if (VertPlaneSides[iCorn][iPlane] == - 1)
						{
							addable = false;
						}
					}
					else
					{
						if (VertPlaneSides[iCorn][iPlane] == 1)
						{
							addable = false;
						}
					}
				}
				if (addable)
				{
					Corns[iCorns] = iCorn;
					iCorns++;
				}
			}
			// Check if we already have the brush...
			if (iCorns >= allplanes.Length)
			{
				bool isBrush = true;
				for (int iChkP = 0; iChkP < allplanes.Length; iChkP++)
				{
					// If all planes have at least 3 verts in this solid, IT IS THE BRUSH.
					int numOnPlane = 0;
					for (int iChkC = 0; iChkC < iCorns; iChkC++)
					{
						if (VertPlaneSides[Corns[iChkC]][iChkP] == 0)
						{
							numOnPlane++;
							if (numOnPlane >= 3)
							{
								break;
							}
						}
					}
					if (numOnPlane < 3)
					{
						isBrush = false;
						break;
					}
				}
				if (isBrush)
				{
					// Copy to TrueCorns.
					TrueCorns = new int[iCorns];
					Array.Copy(Corns, 0, TrueCorns, 0, iCorns);
					break;
				}
			}
		}
		
		
		
		
		
		// Idea: Loop all verts on 1 plane.
		//   Collect 1 cavity.
		//	Collect others, but make sure that points are not on same sides of planes as others.
		//	(Start plane side array (make upper-level one, too), and make sure that it doesn't become identical to any previous arrays.)
		
		
		
		
		
		
		/*Vector3D[] corners = new Vector3D[allverts.length];
		
		
		
		// SCREW IT. NEW PLAN...
		// Iterate through all possibilities of all sides of all planes.
		// NEED: byte[vert][plane] = -1, 0, +1, to tell whether or not vertex can be included in decompile.
		// 2^n iterations, sequence of booleans determines sides of planes.
		
		
		
		
		
		Vector3D[][] solidCollection = new Vector3D[allverts.length];
		int isolidCollection = 0;
		
		// METHOD 1: Take 1 starting vert, and find all verts on same side of all planes, and on same side of startplanes as first verts found not on startplanes.
		for (int iStart = 0; iStart < PlaneVerts[0].length; iStart++) {
		
		// Set up list of sides of planes which define this solid. 0=ON 1=+Norm -1=-Norm
		byte[] planeSidesStart = new byte[allplanes.length];
		// Collect info from first point.
		for (int i1Side = 1; i1Side < allplanes.length; i1Side++) {
		if (indexOf(PlaneVerts[i1Side], PlaneVerts[0][iStart]) == -1) {
		planeSidesStart[i1Side] = ((allplanes[i1Side].distance(PlaneVerts[0][iStart]) > 0) ? 1 : -1);
		} else {
		planeSidesStart[i1Side] = 0;
		}
		}
		
		// Collect a list of points which satisfy the conditions in planeSideStart.
		int[] potCorns = new int[allverts.length];
		int ipotCorns = 0;
		for (int iTCorn = 0; iTCorn < allverts.length; iTCorn++) {
		if (iTCorn != indexOf(allverts, PlaneVerts[0][iStart])) {
		// Check if on same side of planes as solid-defining list...
		boolean addable = true;
		for (int iPlanes = 0; iPlanes < allplanes.length; iPlanes++) {
		if (indexOf(PlaneVerts[iPlanes], allpoints[iCorn]) == -1 && planeSidesStart[iPlanes] != 0) {
		double dist = allplanes[iPlanes].distance(allpoints[iCorn]);
		if ((dist > 0 && planeSidesStart[iPlanes] < 0) || (dist < 0 && planeSidesStart[iPlanes] > 0)) {
		addable = false;
		}
		}
		}
		if (addable) {
		potCorns[ipotCorns] = iTCorn;
		ipotCorns++;
		}
		}
		}
		
		int[] basePlanes = new int[VertPlanes[indexOf(allverts, PlaneVerts[0][iStart])].length];
		// A possibility of 2^NumUnknownPlaneSides number of valid solids. Possibly on + or - side of each unknown plane.
		// Need to code something to run over each possibility once.
		for (int iSideCheck = 0; iSideCheck < basePlanes.length; iSideCheck++) {
		
		
		// For all possible solids starting from this point...
		for (int iChkNum = 0; iChkNum < allverts.length; iChkNum++) {
		// Look through all verts & find all on same side of planes.
		int[] blockcorners = new int[allpoints.length];
		blockcorners[0] = indexOf(allverts, PlaneVerts[0][iStart]);
		int iblockcorners = 1;
		
		// Set up list of sides of planes which define this solid. 0=ON 1=+Norm -1=-Norm
		// DEEP COPY IT PLEASE?
		byte[] planeSides = planeSidesStart;
		
		// Screw this complicated shit, doesn't work anyway.
		// INSTEAD, must find valid solid, and verts on opposite side of a plane that startVert is on than other solids.
		
		//
		
		
		int FirstID = 0;
		for (int iCorn = 0; iCorn < allverts.length; iCorn++) {
		boolean checkable = true;
		boolean isFirst = 
		// If is startvert or is contained in any solid also containing startvert, IS NOT CHECKABLE FIRST TIME AROUND.
		// Do for this vert, then reset to find all verts in solid.
		for (int iCheck = 0; iCheck < iSolidCollection; iCheck++) {
		
		if (!allpoints[iCorn].equals(PlaneVerts[0][iStart]) && iCorn != FirstID)
		if (!allpoints[iCorn].equals(PlaneVerts[0][iStart])) {
		// Check if on same side of planes as solid-defining list...
		int[] thisPlaneSides = new int[allplanes.length]
		int ithisPlaneSides = 0;
		boolean addable = true;
		for (int iPlanes = 0; iPlanes < allplanes.length; iPlanes++) {
		if (indexOf(PlaneVerts[iPlanes], allpoints[iCorn]) == -1) {
		double dist = allplanes[iPlanes].distance(allpoints[iCorn]);
		if (planeSides[iPlanes] == 0) {
		// If point is valid at end, temp set vert's planesides to +- plane index.
		thisPlaneSides[ithisPlaneSides] = ((dist > 0) ? iPlanes + 1 : -iPlanes - 1);
		} else if ((dist > 0 && planeSides[iPlanes] < 0) || (dist < 0 && planeSides[iPlanes] > 0)) {
		addable = false;
		}
		}
		}
		if (addable) {
		for (int iNewSides = 0; iNewSides < ithisPlaneSides; iNewSides++) {
		if (thisPlaneSides[iNewSides] > 0) {
		planeSides[thisPlaneSides[iNewSides] - 1] = 1;
		} else {
		planeSides[-thisPlaneSides[iNewSides] - 1] = -1;
		}
		}
		blockcorners[iblockcorners] = iCorn;
		iblockcorners++;
		}
		}
		}
		solidCollection[isolidCollection] = new Vector3D[iblockcorners];
		for (int iKnownCorn = 0; iKnownCorn < iblockcorners; iKnownCorn++) {
		solidCollection[isolidCollection][iKnownCorn] = allverts[blockcorners[iKnownCorn]];
		}
		isolidCollection++;
		}
		
		
		
		
		// For each vert on base plane, find 2 adjacent vertices.
		for (int iV1 = 0; iV1 < PlaneVerts[0].length; iV1++) {
		// For each other vert on base plane, check if 1st vert and this one share 2 planes & are on same sides of all planes.
		for (int iV2 = iV1 + 1; iV2 < PlaneVerts[0].length; iV2++) {
		boolean isOKVert = false;
		// Check if these 2 share 2 planes.
		// For each plane on first vert...
		for (int iChkP = 0; iChkP < VertPlanes[indexOf(allverts, PlaneVerts[0][iV1])].length; iChkP++)
		// If plane is NOT base-plane...
		if (!allplanes[0].getNormal().equals(VertPlanes[indexOf(allverts, PlaneVerts[0][iV1])][iChkP]) || allplanes[0].getDist() != VertPlanes[indexOf(allverts, PlaneVerts[0][iV1])][iChkP].getDist())
		// If this plane is common, VERT IS OK...
		if (indexOf(VertPlanes[indexOf(allverts, PlaneVerts[0][iV2])], VertPlanes[indexOf(allverts, PlaneVerts[0][iV1])][iChkP]) > -1)
		isOKVert = true;
		// Check if these 2 are on same side of all planes they are not on.
		if (isOKVert) {
		isOKVert = false;
		// For each plane, not base-plane...
		for (int iChkP2 = 1; iChkP2 < allplanes.length; iChkP2++)
		// If V1 and V2 are NOT on plane...
		if (indexOf(PlaneVerts[iChkP2], PlaneVerts[0][iV1]) == -1 && indexOf(PlaneVerts[iChkP2], PlaneVerts[0][iV2]) == -1) {
		// If V1 and V2 are on same side of plane...
		double dv1 = allplanes[iChkP2].distance(PlaneVerts[0][iV1]);
		double dv2 = allplanes[iChkP2].distance(PlaneVerts[0][iV2]);
		if ((dv1 > 0 && dv2 > 0) || (dv1 < 0 && dv2 < 0))
		isOKVert = true;
		}
		}
		if (isOKVert) {
		for (int iV3 = iV2 + 1; iV3 < PlaneVerts[0].length; iV3++) {
		
		*/
		
		
		
		// Return null value if method failed.
		if (TrueCorns.Length == 0)
		{
			throw new System.ArithmeticException("No corners found for brush!");
		}
		
		
		// 4. Create brush central point, and use it to create normalised plane triplets.
		// Create central point of brush for normalising vert-planes.
		Vector3D centrePoint = new Vector3D(0.0, 0.0, 0.0);
		for (int iCorn = 0; iCorn < TrueCorns.Length; iCorn++)
		{
			centrePoint = centrePoint+(allverts[TrueCorns[iCorn]]);
		}
		centrePoint = centrePoint/((double) TrueCorns.Length);
		// Use corners to generate brush plane triplets.
		Vector3D[][] output = new Vector3D[allplanes.Length][];
		for (int iPlane = 0; iPlane < allplanes.Length; iPlane++)
		{
			int[] vertPlane = new int[3];
			int ivertPlane = 0;
			for (int iCorn = 0; iCorn < TrueCorns.Length; iCorn++)
			{
				if (VertPlaneSides[TrueCorns[iCorn]][iPlane] == 0)
				{
					vertPlane[ivertPlane] = TrueCorns[iCorn];
					ivertPlane++;
					if (ivertPlane == 3)
					{
						break;
					}
				}
			}
			// Order triplet correctly & save to output array.
			if (new Plane(allverts[vertPlane[0]], allverts[vertPlane[1]], allverts[vertPlane[2]]).distance(centrePoint) > 0)
			{
				output[iPlane] = new Vector3D[]{allverts[vertPlane[0]], allverts[vertPlane[2]], allverts[vertPlane[1]]};
			}
			else
			{
				output[iPlane] = new Vector3D[]{allverts[vertPlane[0]], allverts[vertPlane[1]], allverts[vertPlane[2]]};
			}
		}
		for (int i = 0; i < output.Length; i++)
		{
			// This isn't setSide because the plane definition is no longer reliable; it might be flipped the wrong way
			mapBrush[i].Triangle = output[i];
		}
		return mapBrush;
	}
    // 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);
    }
Esempio n. 34
0
    // Use if brush has no triangles.
    /// Author:		UltimateSniper
    /// Returns:	Ordered list of normalised vertex triplets (ready to feed in to map).
    public static MAPBrush AdvancedCorrectPlanes(MAPBrush mapBrush)
    {
        //DecompilerThread.OnMessage(this, "Plane flip. Method: advanced");
        Plane[] allplanes = mapBrush.Planes;
        // Method:
        //1. Collect all vertices created by plane intercepts.
        //2. Create arrays of these vertices and inputted planes, to access planes via points they intersect and vice versa.
        //  MORE IMPORTANTLY, create an array indicating sides of each plane each vertex is on (-1 for -, 0 for on, 1 for +).
        //3. Run through each possible cavity (each combination of sides of each plane), collecting satisfying vertices.
        //	Correct cavity is found when there are at least 3 vertices on each plane.
        //	If fail, returns Vector3D[0][].
        //4. Generate central point of brush, and use it to produce normalised vertex triplets to return.

        // 1. Collect all plane intersects (all possible corners).
        //Find MaxVerts: max = N!/3!(N-3)! = (1/3!) * (N/(N-3)) * ((N-1)/(N-4)) * ((N-2)/(N-5)) * ... * (5/2) * 4 * 3!
        double dmaxVerts = 4.0;

        for (int iP = allplanes.Length; iP > 4; iP--)
        {
            dmaxVerts *= iP / (iP - 3.00);
        }
        //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'"
        Vector3D[] allverts  = new Vector3D[(int)dmaxVerts]; // Max possible number of unique plane trisects: nC3.
        int        iallverts = 0;                            // Pointer, so we know next vacant index.

        for (int iP1 = 0; iP1 < allplanes.Length; iP1++)
        {
            for (int iP2 = iP1 + 1; iP2 < allplanes.Length; iP2++)
            {
                for (int iP3 = iP2 + 1; iP3 < allplanes.Length; iP3++)
                {
                    Vector3D testV = allplanes[iP1].trisect(allplanes[iP2], allplanes[iP3]);
                    if (testV != Vector3D.UNDEFINED)
                    {
                        // Arbitrary precision: Just checking if UNDEFINED or real.
                        bool hasVtx = false;
                        for (int iVtx = 0; iVtx < iallverts; iVtx++)
                        {
                            if (allverts[iVtx] == testV)
                            {
                                hasVtx = true;
                                break;
                            }
                        }
                        if (!hasVtx)
                        {
                            allverts[iallverts] = testV;
                            iallverts++;
                        }
                    }
                }
            }
        }
        Vector3D[] tmp = new Vector3D[iallverts];
        Array.Copy(allverts, 0, tmp, 0, iallverts);
        allverts = tmp;


        // 2. Make array to access verts' sides of planes (can also be used to check if vert is on plane).
        sbyte[][] VertPlaneSides = new sbyte[allverts.Length][];
        for (int iV = 0; iV < allverts.Length; iV++)
        {
            sbyte[] PlaneSides = new sbyte[allplanes.Length];
            for (int iVP = 0; iVP < allplanes.Length; iVP++)
            {
                double dist = allplanes[iVP].distance(allverts[iV]);
                if (System.Math.Abs(dist) < Settings.precision)
                {
                    PlaneSides[iVP] = 0;
                }
                else
                {
                    PlaneSides[iVP] = ((dist >= Settings.precision)?(sbyte)1:(sbyte)(-1));
                }
            }
            VertPlaneSides[iV] = PlaneSides;
        }

        // THEORY: Collect vertices that are all either on, or on the same side of all planes.
        //		 If there are at least 3 vertices on each plane, then this is the correct shape.
        //  NOTES: -Some vertices may be included in multiple collections.
        //		 -Must have 3 vertices sharing 1 plane, and a fourth which is not on that plane to start 'cavity'.
        //		 -4 defining vertices cannot make more than 1 'cavity'.


        // Java is retarded, as it doesn't allow uints. This causes me a serious problem with this, because I am not sure
        // whether or not bitwise operations will return positive values, or switch the signs, or switch the bit order...

        // Cannot handle more than the max positive val for a long, and need a bit to represent each plane.
        if (allplanes.Length > 62)
        {
            throw new System.IndexOutOfRangeException("More than 62 planes in brush!");
        }


        // 3. Find all vertices which satisfy each possible cavity, and break when true brush is found.
        // Let the madness of this one great f*****g for-loop commence...
        int[] TrueCorns = new int[0];

        for (long lCav = 0; lCav < (1 << allplanes.Length); lCav++)
        {
            int[] Corns  = new int[allverts.Length];
            int   iCorns = 0;
            for (int iCorn = 0; iCorn < allverts.Length; iCorn++)
            {
                bool addable = true;
                for (int iPlane = 0; iPlane < allplanes.Length; iPlane++)
                {
                    // Get bit value of lCav which represents this plane (true = +, false = -), check if vert is addable.
                    if (((lCav >> iPlane) & 1) == 1)
                    {
                        if (VertPlaneSides[iCorn][iPlane] == -1)
                        {
                            addable = false;
                        }
                    }
                    else
                    {
                        if (VertPlaneSides[iCorn][iPlane] == 1)
                        {
                            addable = false;
                        }
                    }
                }
                if (addable)
                {
                    Corns[iCorns] = iCorn;
                    iCorns++;
                }
            }
            // Check if we already have the brush...
            if (iCorns >= allplanes.Length)
            {
                bool isBrush = true;
                for (int iChkP = 0; iChkP < allplanes.Length; iChkP++)
                {
                    // If all planes have at least 3 verts in this solid, IT IS THE BRUSH.
                    int numOnPlane = 0;
                    for (int iChkC = 0; iChkC < iCorns; iChkC++)
                    {
                        if (VertPlaneSides[Corns[iChkC]][iChkP] == 0)
                        {
                            numOnPlane++;
                            if (numOnPlane >= 3)
                            {
                                break;
                            }
                        }
                    }
                    if (numOnPlane < 3)
                    {
                        isBrush = false;
                        break;
                    }
                }
                if (isBrush)
                {
                    // Copy to TrueCorns.
                    TrueCorns = new int[iCorns];
                    Array.Copy(Corns, 0, TrueCorns, 0, iCorns);
                    break;
                }
            }
        }



        // Idea: Loop all verts on 1 plane.
        //   Collect 1 cavity.
        //	Collect others, but make sure that points are not on same sides of planes as others.
        //	(Start plane side array (make upper-level one, too), and make sure that it doesn't become identical to any previous arrays.)



        /*Vector3D[] corners = new Vector3D[allverts.length];
         *
         *
         *
         * // SCREW IT. NEW PLAN...
         * // Iterate through all possibilities of all sides of all planes.
         * // NEED: byte[vert][plane] = -1, 0, +1, to tell whether or not vertex can be included in decompile.
         * // 2^n iterations, sequence of booleans determines sides of planes.
         *
         *
         *
         *
         *
         * Vector3D[][] solidCollection = new Vector3D[allverts.length];
         * int isolidCollection = 0;
         *
         * // METHOD 1: Take 1 starting vert, and find all verts on same side of all planes, and on same side of startplanes as first verts found not on startplanes.
         * for (int iStart = 0; iStart < PlaneVerts[0].length; iStart++) {
         *
         * // Set up list of sides of planes which define this solid. 0=ON 1=+Norm -1=-Norm
         * byte[] planeSidesStart = new byte[allplanes.length];
         * // Collect info from first point.
         * for (int i1Side = 1; i1Side < allplanes.length; i1Side++) {
         * if (indexOf(PlaneVerts[i1Side], PlaneVerts[0][iStart]) == -1) {
         * planeSidesStart[i1Side] = ((allplanes[i1Side].distance(PlaneVerts[0][iStart]) > 0) ? 1 : -1);
         * } else {
         * planeSidesStart[i1Side] = 0;
         * }
         * }
         *
         * // Collect a list of points which satisfy the conditions in planeSideStart.
         * int[] potCorns = new int[allverts.length];
         * int ipotCorns = 0;
         * for (int iTCorn = 0; iTCorn < allverts.length; iTCorn++) {
         * if (iTCorn != indexOf(allverts, PlaneVerts[0][iStart])) {
         * // Check if on same side of planes as solid-defining list...
         * boolean addable = true;
         * for (int iPlanes = 0; iPlanes < allplanes.length; iPlanes++) {
         * if (indexOf(PlaneVerts[iPlanes], allpoints[iCorn]) == -1 && planeSidesStart[iPlanes] != 0) {
         * double dist = allplanes[iPlanes].distance(allpoints[iCorn]);
         * if ((dist > 0 && planeSidesStart[iPlanes] < 0) || (dist < 0 && planeSidesStart[iPlanes] > 0)) {
         * addable = false;
         * }
         * }
         * }
         * if (addable) {
         * potCorns[ipotCorns] = iTCorn;
         * ipotCorns++;
         * }
         * }
         * }
         *
         * int[] basePlanes = new int[VertPlanes[indexOf(allverts, PlaneVerts[0][iStart])].length];
         * // A possibility of 2^NumUnknownPlaneSides number of valid solids. Possibly on + or - side of each unknown plane.
         * // Need to code something to run over each possibility once.
         * for (int iSideCheck = 0; iSideCheck < basePlanes.length; iSideCheck++) {
         *
         *
         * // For all possible solids starting from this point...
         * for (int iChkNum = 0; iChkNum < allverts.length; iChkNum++) {
         * // Look through all verts & find all on same side of planes.
         * int[] blockcorners = new int[allpoints.length];
         * blockcorners[0] = indexOf(allverts, PlaneVerts[0][iStart]);
         * int iblockcorners = 1;
         *
         * // Set up list of sides of planes which define this solid. 0=ON 1=+Norm -1=-Norm
         * // DEEP COPY IT PLEASE?
         * byte[] planeSides = planeSidesStart;
         *
         * // Screw this complicated shit, doesn't work anyway.
         * // INSTEAD, must find valid solid, and verts on opposite side of a plane that startVert is on than other solids.
         *
         * //
         *
         *
         * int FirstID = 0;
         * for (int iCorn = 0; iCorn < allverts.length; iCorn++) {
         * boolean checkable = true;
         * boolean isFirst =
         * // If is startvert or is contained in any solid also containing startvert, IS NOT CHECKABLE FIRST TIME AROUND.
         * // Do for this vert, then reset to find all verts in solid.
         * for (int iCheck = 0; iCheck < iSolidCollection; iCheck++) {
         *
         * if (!allpoints[iCorn].equals(PlaneVerts[0][iStart]) && iCorn != FirstID)
         * if (!allpoints[iCorn].equals(PlaneVerts[0][iStart])) {
         * // Check if on same side of planes as solid-defining list...
         * int[] thisPlaneSides = new int[allplanes.length]
         * int ithisPlaneSides = 0;
         * boolean addable = true;
         * for (int iPlanes = 0; iPlanes < allplanes.length; iPlanes++) {
         * if (indexOf(PlaneVerts[iPlanes], allpoints[iCorn]) == -1) {
         * double dist = allplanes[iPlanes].distance(allpoints[iCorn]);
         * if (planeSides[iPlanes] == 0) {
         * // If point is valid at end, temp set vert's planesides to +- plane index.
         * thisPlaneSides[ithisPlaneSides] = ((dist > 0) ? iPlanes + 1 : -iPlanes - 1);
         * } else if ((dist > 0 && planeSides[iPlanes] < 0) || (dist < 0 && planeSides[iPlanes] > 0)) {
         * addable = false;
         * }
         * }
         * }
         * if (addable) {
         * for (int iNewSides = 0; iNewSides < ithisPlaneSides; iNewSides++) {
         * if (thisPlaneSides[iNewSides] > 0) {
         * planeSides[thisPlaneSides[iNewSides] - 1] = 1;
         * } else {
         * planeSides[-thisPlaneSides[iNewSides] - 1] = -1;
         * }
         * }
         * blockcorners[iblockcorners] = iCorn;
         * iblockcorners++;
         * }
         * }
         * }
         * solidCollection[isolidCollection] = new Vector3D[iblockcorners];
         * for (int iKnownCorn = 0; iKnownCorn < iblockcorners; iKnownCorn++) {
         * solidCollection[isolidCollection][iKnownCorn] = allverts[blockcorners[iKnownCorn]];
         * }
         * isolidCollection++;
         * }
         *
         *
         *
         *
         * // For each vert on base plane, find 2 adjacent vertices.
         * for (int iV1 = 0; iV1 < PlaneVerts[0].length; iV1++) {
         * // For each other vert on base plane, check if 1st vert and this one share 2 planes & are on same sides of all planes.
         * for (int iV2 = iV1 + 1; iV2 < PlaneVerts[0].length; iV2++) {
         * boolean isOKVert = false;
         * // Check if these 2 share 2 planes.
         * // For each plane on first vert...
         * for (int iChkP = 0; iChkP < VertPlanes[indexOf(allverts, PlaneVerts[0][iV1])].length; iChkP++)
         * // If plane is NOT base-plane...
         * if (!allplanes[0].getNormal().equals(VertPlanes[indexOf(allverts, PlaneVerts[0][iV1])][iChkP]) || allplanes[0].getDist() != VertPlanes[indexOf(allverts, PlaneVerts[0][iV1])][iChkP].getDist())
         * // If this plane is common, VERT IS OK...
         * if (indexOf(VertPlanes[indexOf(allverts, PlaneVerts[0][iV2])], VertPlanes[indexOf(allverts, PlaneVerts[0][iV1])][iChkP]) > -1)
         * isOKVert = true;
         * // Check if these 2 are on same side of all planes they are not on.
         * if (isOKVert) {
         * isOKVert = false;
         * // For each plane, not base-plane...
         * for (int iChkP2 = 1; iChkP2 < allplanes.length; iChkP2++)
         * // If V1 and V2 are NOT on plane...
         * if (indexOf(PlaneVerts[iChkP2], PlaneVerts[0][iV1]) == -1 && indexOf(PlaneVerts[iChkP2], PlaneVerts[0][iV2]) == -1) {
         * // If V1 and V2 are on same side of plane...
         * double dv1 = allplanes[iChkP2].distance(PlaneVerts[0][iV1]);
         * double dv2 = allplanes[iChkP2].distance(PlaneVerts[0][iV2]);
         * if ((dv1 > 0 && dv2 > 0) || (dv1 < 0 && dv2 < 0))
         * isOKVert = true;
         * }
         * }
         * if (isOKVert) {
         * for (int iV3 = iV2 + 1; iV3 < PlaneVerts[0].length; iV3++) {
         *
         */



        // Return null value if method failed.
        if (TrueCorns.Length == 0)
        {
            throw new System.ArithmeticException("No corners found for brush!");
        }


        // 4. Create brush central point, and use it to create normalised plane triplets.
        // Create central point of brush for normalising vert-planes.
        Vector3D centrePoint = new Vector3D(0.0, 0.0, 0.0);

        for (int iCorn = 0; iCorn < TrueCorns.Length; iCorn++)
        {
            centrePoint = centrePoint + (allverts[TrueCorns[iCorn]]);
        }
        centrePoint = centrePoint / ((double)TrueCorns.Length);
        // Use corners to generate brush plane triplets.
        Vector3D[][] output = new Vector3D[allplanes.Length][];
        for (int iPlane = 0; iPlane < allplanes.Length; iPlane++)
        {
            int[] vertPlane  = new int[3];
            int   ivertPlane = 0;
            for (int iCorn = 0; iCorn < TrueCorns.Length; iCorn++)
            {
                if (VertPlaneSides[TrueCorns[iCorn]][iPlane] == 0)
                {
                    vertPlane[ivertPlane] = TrueCorns[iCorn];
                    ivertPlane++;
                    if (ivertPlane == 3)
                    {
                        break;
                    }
                }
            }
            // Order triplet correctly & save to output array.
            if (new Plane(allverts[vertPlane[0]], allverts[vertPlane[1]], allverts[vertPlane[2]]).distance(centrePoint) > 0)
            {
                output[iPlane] = new Vector3D[] { allverts[vertPlane[0]], allverts[vertPlane[2]], allverts[vertPlane[1]] };
            }
            else
            {
                output[iPlane] = new Vector3D[] { allverts[vertPlane[0]], allverts[vertPlane[1]], allverts[vertPlane[2]] };
            }
        }
        for (int i = 0; i < output.Length; i++)
        {
            // This isn't setSide because the plane definition is no longer reliable; it might be flipped the wrong way
            mapBrush[i].Triangle = output[i];
        }
        return(mapBrush);
    }
    // -decompileBrush(Brush, int)
    // Decompiles the Brush and adds it to entitiy #currentEntity as MAPBrush classes.
    private void decompileBrush(Brush brush, int currentEntity)
    {
        Vector3D origin    = mapFile[currentEntity].Origin;
        int      firstSide = brush.FirstSide;
        int      numSides  = brush.NumSides;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        // This adds the brush we've been finding and creating to
        // the current entity as an attribute. The way I've coded
        // this whole program and the entities parser, this shouldn't
        // cause any issues at all.
        if (Settings.brushesToWorld)
        {
            mapBrush.Water = false;
            worldspawn.Brushes.Add(mapBrush);
        }
        else
        {
            mapFile[currentEntity].Brushes.Add(mapBrush);
        }
    }
Esempio n. 36
0
    // Some algorithms might produce planes which are correctly normalized, but
    // some don't actually contribute to the solid (such as those solids created
    // by iterating through a binary tree, and keeping track of all node subdivisions).
    // This finds them, returns a list of their indices as an array. I could return
    // a MAPBrush with those planes culled, but oftentimes there are two or three
    // brushes with the same unnecessary planes.
    public static int[] findUnusedPlanes(MAPBrush in_Renamed)
    {
        Plane[] thePlanes = in_Renamed.Planes;
        //Console.Write("Finding unnecessary planes. Before: " + thePlanes.Length);

        // Step 1: Get all points of intersection
        double numVerts = 4;

        // Iterative nCr algorithm; thanks to Alex's code
        for (int i = thePlanes.Length; i > 4; i--)
        {
            numVerts *= (double)i / (double)(i - 3);
        }
        //UPGRADE_TODO: Method 'java.lang.Math.round' was converted to 'System.Math.Round' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextSettingsIndex'&keyword='jlca1073_javalangMathround_double'"
        Vector3D[] theVerts = new Vector3D[(int)System.Math.Round(numVerts)];
        int        index    = 0;

        for (int i = 0; i < thePlanes.Length - 2; i++)
        {
            for (int j = i + 1; j < thePlanes.Length - 1; j++)
            {
                for (int k = j + 1; k < thePlanes.Length; k++)
                {
                    theVerts[index++] = thePlanes[i].trisect(thePlanes[j], thePlanes[k]);
                }
            }
        }

        // Step 2: Throw out all vertices on the wrong side of any plane, since they're
        // all facing the "right" way.
        for (int i = 0; i < theVerts.Length; i++)
        {
            for (int j = 0; j < thePlanes.Length; j++)
            {
                if (thePlanes[j].distance(theVerts[i]) > Settings.precision)
                {
                    theVerts[i] = Vector3D.UNDEFINED;
                    break;                     //break the inner loop, let the outer loop iterate
                }
            }
        }

        // Step 3: Only keep sides which have three or more vertices defined
        int[] badSides = new int[0];
        for (int i = 0; i < thePlanes.Length; i++)
        {
            int        numMatches = 0;
            Vector3D[] matches    = new Vector3D[3];
            for (int j = 0; j < theVerts.Length; j++)
            {
                if (System.Math.Abs(thePlanes[i].distance(theVerts[j])) < Settings.precision)
                {
                    bool duplicate = false;
                    for (int k = 0; k < numMatches; k++)
                    {
                        if (theVerts[j] == matches[k])
                        {
                            duplicate = true;
                        }
                    }
                    if (!duplicate)
                    {
                        matches[numMatches] = theVerts[j];
                        numMatches++;
                    }
                }
                if (numMatches >= 3)
                {
                    // We have enough points.
                    break;
                }
            }
            if (numMatches < 3)
            {
                int[] newList = new int[badSides.Length + 1];
                for (int j = 0; j < badSides.Length; j++)
                {
                    newList[j] = badSides[j];
                }
                newList[newList.Length - 1] = i;
                badSides = newList;
            }
        }
        //DecompilerThread.OnMessage(this, " After: " + (thePlanes.Length - badSides.Length));
        return(badSides);
    }
    // METHODS

    // +decompile()
    // Attempts to convert the BSP file back into a .MAP file.
    public virtual Entities decompile()
    {
        DecompilerThread.OnMessage(this, "Decompiling...");
        // In the decompiler, it is not necessary to copy all entities to a new object, since
        // no writing is ever done back to the BSP file.
        mapFile = BSPObject.Entities;
        int numTotalItems = 0;

        worldspawn = mapFile[mapFile.findAllWithAttribute("classname", "worldspawn")[0]];
        int onePercent = (int)((BSPObject.Brushes.Count + BSPObject.Entities.Count + BSPObject.Faces.Count) / 100);

        if (onePercent < 1)
        {
            onePercent = 1;
        }
        // I need to go through each entity and see if it's brush-based.
        // Worldspawn is brush-based as well as any entity with model *#.
        for (int i = 0; i < BSPObject.Entities.Count; i++)
        {
            // For each entity
            //DecompilerThread.OnMessage(this, "Entity " + i + ": " + mapFile[i]["classname"]);
            numBrshs = 0;             // Reset the brush count for each entity
            // getModelNumber() returns 0 for worldspawn, the *# for brush based entities, and -1 for everything else
            int currentModel = mapFile[i].ModelNumber;
            if (currentModel > -1)
            {
                // If this is still -1 then it's strictly a point-based entity. Move on to the next one.
                int firstBrush = BSPObject.Models[currentModel].FirstBrush;
                int numBrushes = BSPObject.Models[currentModel].NumBrushes;
                numBrshs = 0;
                for (int j = 0; j < numBrushes; j++)
                {
                    // For each brush
                    //Console.Write("Brush " + j);
                    decompileBrush(BSPObject.Brushes[j + firstBrush], i);                     // Decompile the brush
                    numBrshs++;
                    numTotalItems++;
                    if (numTotalItems % onePercent == 0)
                    {
                        parent.OnProgress(this, numTotalItems / (double)(BSPObject.Brushes.Count + BSPObject.Entities.Count + BSPObject.Faces.Count));
                    }
                }
            }
            numTotalItems++;
            if (numTotalItems % onePercent == 0)
            {
                parent.OnProgress(this, numTotalItems / (double)(BSPObject.Brushes.Count + BSPObject.Entities.Count + BSPObject.Faces.Count));
            }
        }
        if (!Settings.skipPlaneFlip)
        {
            DecompilerThread.OnMessage(this, "Num simple corrected brushes: " + numSimpleCorrects);
            DecompilerThread.OnMessage(this, "Num advanced corrected brushes: " + numAdvancedCorrects);
            DecompilerThread.OnMessage(this, "Num good brushes: " + numGoodBrushes);
        }
        foreach (Face face in BSPObject.Faces)
        {
            if (face.Facetype == Face.faceType.PATCH)
            {
                MAPPatch mapPatch = new MAPPatch(face.PatchSize[0], face.PatchSize[1], BSPObject.Textures[face.Texture].Name);
                for (int i = 0; i < face.NumVertices; i++)
                {
                    mapPatch.Add(BSPObject.Vertices[face.FirstVertex + i]);
                }
                MAPBrush mapBrush = new MAPBrush(mapPatch);
                worldspawn.Brushes.Add(mapBrush);
            }
        }
        parent.OnProgress(this, 1.0);
        return(mapFile);
    }
Esempio n. 38
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);
    }
Esempio n. 39
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;
	}
Esempio n. 40
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);
        }
    }
Esempio n. 41
0
	// Some algorithms might produce planes which are correctly normalized, but
	// some don't actually contribute to the solid (such as those solids created
	// by iterating through a binary tree, and keeping track of all node subdivisions).
	// This finds them, returns a list of their indices as an array. I could return
	// a MAPBrush with those planes culled, but oftentimes there are two or three
	// brushes with the same unnecessary planes.
	public static int[] findUnusedPlanes(MAPBrush in_Renamed)
	{
		Plane[] thePlanes = in_Renamed.Planes;
		//Console.Write("Finding unnecessary planes. Before: " + thePlanes.Length);
		
		// Step 1: Get all points of intersection
		double numVerts = 4;
		// Iterative nCr algorithm; thanks to Alex's code
		for (int i = thePlanes.Length; i > 4; i--)
		{
			numVerts *= (double) i / (double) (i - 3);
		}
		//UPGRADE_TODO: Method 'java.lang.Math.round' was converted to 'System.Math.Round' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextSettingsIndex'&keyword='jlca1073_javalangMathround_double'"
		Vector3D[] theVerts = new Vector3D[(int) System.Math.Round(numVerts)];
		int index = 0;
		for (int i = 0; i < thePlanes.Length - 2; i++)
		{
			for (int j = i + 1; j < thePlanes.Length - 1; j++)
			{
				for (int k = j + 1; k < thePlanes.Length; k++)
				{
					theVerts[index++] = thePlanes[i].trisect(thePlanes[j], thePlanes[k]);
				}
			}
		}
		
		// Step 2: Throw out all vertices on the wrong side of any plane, since they're
		// all facing the "right" way.
		for (int i = 0; i < theVerts.Length; i++)
		{
			for (int j = 0; j < thePlanes.Length; j++)
			{
				if (thePlanes[j].distance(theVerts[i]) > Settings.precision)
				{
					theVerts[i] = Vector3D.UNDEFINED;
					break; //break the inner loop, let the outer loop iterate
				}
			}
		}
		
		// Step 3: Only keep sides which have three or more vertices defined
		int[] badSides = new int[0];
		for (int i = 0; i < thePlanes.Length; i++)
		{
			int numMatches = 0;
			Vector3D[] matches = new Vector3D[3];
			for (int j = 0; j < theVerts.Length; j++)
			{
				if (System.Math.Abs(thePlanes[i].distance(theVerts[j])) < Settings.precision)
				{
					bool duplicate = false;
					for (int k = 0; k < numMatches; k++)
					{
						if (theVerts[j]==matches[k])
						{
							duplicate = true;
						}
					}
					if (!duplicate)
					{
						matches[numMatches] = theVerts[j];
						numMatches++;
					}
				}
				if (numMatches >= 3)
				{
					// We have enough points.
					break;
				}
			}
			if (numMatches < 3)
			{
				int[] newList = new int[badSides.Length + 1];
				for (int j = 0; j < badSides.Length; j++)
				{
					newList[j] = badSides[j];
				}
				newList[newList.Length - 1] = i;
				badSides = newList;
			}
		}
		//DecompilerThread.OnMessage(this, " After: " + (thePlanes.Length - badSides.Length));
		return badSides;
	}
Esempio n. 42
0
	// METHODS
	
	// +decompile()
	// Attempts to convert a map in a Doom WAD into a usable .MAP file. This has many
	// challenges, not the least of which is the fact that the Doom engine didn't use
	// brushes (at least, not in any sane way).
	public virtual Entities decompile()
	{
		DecompilerThread.OnMessage(this, "Decompiling...");
		DecompilerThread.OnMessage(this, doomMap.MapName);
		
		mapFile = new Entities();
		Entity world = new Entity("worldspawn");
		mapFile.Add(world);
		
		string[] lowerWallTextures = new string[doomMap.Sidedefs.Count];
		string[] midWallTextures = new string[doomMap.Sidedefs.Count];
		string[] higherWallTextures = new string[doomMap.Sidedefs.Count];
		
		short[] sectorTag = new short[doomMap.Sectors.Count];
		string playerStartOrigin = "";
		
		// Since Doom relied on sectors to define a cieling and floor height, and nothing else,
		// need to find the minimum and maximum used Z values. This is because the Doom engine
		// is only a pseudo-3D engine. For all it cares, the cieling and floor extend to their
		// respective infinities. For a GC/Hammer map, however, this cannot be the case.
		int ZMin = 32767; // Even though the values in the map will never exceed these, use ints here to avoid
		int ZMax = - 32768; // overflows, in case the map DOES go within 32 units of these values.
		for (int i = 0; i < doomMap.Sectors.Count; i++)
		{
			DSector currentSector = doomMap.Sectors[i];
			sectorTag[i] = currentSector.Tag;
			if (currentSector.FloorHeight < ZMin + 32)
			{
				ZMin = currentSector.FloorHeight - 32; // Can't use the actual value, because that IS the floor
			}
			else
			{
				if (currentSector.CielingHeight > ZMax - 32)
				{
					ZMax = currentSector.CielingHeight + 32; // or the cieling. Subtract or add a sane value to it.
				}
			}
		}
		
		// I need to analyze the binary tree and get more information, particularly the
		// parent nodes of each subsector and node, as well as whether it's the right or
		// left child of that node. These are extremely important, as the parent defines
		// boundaries for the children, as well as inheriting further boundaries from its
		// parents. These boundaries are invaluable for forming brushes.
		int[] nodeparents = new int[doomMap.Nodes.Count];
		bool[] nodeIsLeft = new bool[doomMap.Nodes.Count];
		
		for (int i = 0; i < doomMap.Nodes.Count; i++)
		{
			nodeparents[i] = - 1; // There should only be one node left with -1 as a parent. This SHOULD be the root.
			for (int j = 0; j < doomMap.Nodes.Count; j++)
			{
				if (doomMap.Nodes[j].Child1 == i)
				{
					nodeparents[i] = j;
					break;
				}
				else
				{
					if (doomMap.Nodes[j].Child2 == i)
					{
						nodeparents[i] = j;
						nodeIsLeft[i] = true;
						break;
					}
				}
			}
		}
		
		// Keep a list of what subsectors belong to which sector
		int[] subsectorSectors = new int[doomMap.SubSectors.Count];
		// Keep a list of what sidedefs belong to what subsector as well
		int[][] subsectorSidedefs = new int[doomMap.SubSectors.Count][];
		
		short[][] sideDefShifts = new short[2][];
		for (int i2 = 0; i2 < 2; i2++)
		{
			sideDefShifts[i2] = new short[doomMap.Sidedefs.Count];
		}
		
		// Figure out what sector each subsector belongs to, and what node is its parent.
		// Depending on sector "tags" this will help greatly in creation of brushbased entities,
		// and also helps in finding subsector floor and cieling heights.
		int[] ssparents = new int[doomMap.SubSectors.Count];
		bool[] ssIsLeft = new bool[doomMap.SubSectors.Count];
		for (int i = 0; i < doomMap.SubSectors.Count; i++)
		{
			//DecompilerThread.OnMessage(this, "Populating texture lists for subsector " + i);
			// First, find the subsector's parent and whether it is the left or right child.
			ssparents[i] = - 1; // No subsector should have a -1 in here
			for (int j = 0; j < doomMap.Nodes.Count; j++)
			{
				// When a node references a subsector, it is not referenced by negative
				// index, as future BSP versions do. The bits 0-14 ARE the index, and
				// bit 15 (which is the sign bit in two's compliment math) determines
				// whether or not it is a node or subsector. Therefore, we need to add
				// 2^15 to the number to produce the actual index.
				if (doomMap.Nodes[j].Child1 + 32768 == i)
				{
					ssparents[i] = j;
					break;
				}
				else
				{
					if (doomMap.Nodes[j].Child2 + 32768 == i)
					{
						ssparents[i] = j;
						ssIsLeft[i] = true;
						break;
					}
				}
			}
			
			// Second, figure out what sector a subsector belongs to, and the type of sector it is.
			subsectorSectors[i] = - 1;
			Edge currentsubsector = doomMap.SubSectors[i];
			subsectorSidedefs[i] = new int[currentsubsector.NumSegs];
			for (int j = 0; j < currentsubsector.NumSegs; j++)
			{
				// For each segment the subsector references
				DSegment currentsegment = doomMap.Segments[currentsubsector.FirstSeg + j];
				DLinedef currentlinedef = doomMap.Linedefs[currentsegment.Linedef];
				int currentsidedefIndex;
				int othersideIndex;
				if (currentsegment.Direction == 0)
				{
					currentsidedefIndex = currentlinedef.Right;
					othersideIndex = currentlinedef.Left;
				}
				else
				{
					currentsidedefIndex = currentlinedef.Left;
					othersideIndex = currentlinedef.Right;
				}
				subsectorSidedefs[i][j] = currentsidedefIndex;
				DSidedef currentSidedef = doomMap.Sidedefs[currentsidedefIndex];
				if (currentlinedef.OneSided)
				{
					// A one-sided linedef should always be like this
					midWallTextures[currentsidedefIndex] = doomMap.WadName + "/" + currentSidedef.MidTexture;
					higherWallTextures[currentsidedefIndex] = "special/nodraw";
					lowerWallTextures[currentsidedefIndex] = "special/nodraw";
					sideDefShifts[X][currentsidedefIndex] = currentSidedef.OffsetX;
					sideDefShifts[Y][currentsidedefIndex] = currentSidedef.OffsetY;
				}
				else
				{
					// I don't really get why I need to apply these textures to the other side. But if it works I won't argue...
					if (!currentSidedef.MidTexture.Equals("-"))
					{
						midWallTextures[othersideIndex] = doomMap.WadName + "/" + currentSidedef.MidTexture;
					}
					else
					{
						midWallTextures[othersideIndex] = "special/nodraw";
					}
					if (!currentSidedef.HighTexture.Equals("-"))
					{
						higherWallTextures[othersideIndex] = doomMap.WadName + "/" + currentSidedef.HighTexture;
					}
					else
					{
						higherWallTextures[othersideIndex] = "special/nodraw";
					}
					if (!currentSidedef.LowTexture.Equals("-"))
					{
						lowerWallTextures[othersideIndex] = doomMap.WadName + "/" + currentSidedef.LowTexture;
					}
					else
					{
						lowerWallTextures[othersideIndex] = "special/nodraw";
					}
					sideDefShifts[X][othersideIndex] = currentSidedef.OffsetX;
					sideDefShifts[Y][othersideIndex] = currentSidedef.OffsetY;
				}
				// Sometimes a subsector seems to belong to more than one sector. Only the reference in the first seg is true.
				if (j == 0)
				{
					subsectorSectors[i] = currentSidedef.Sector;
				}
			}
		}
		bool[] linedefFlagsDealtWith = new bool[doomMap.Linedefs.Count];
		bool[] linedefSpecialsDealtWith = new bool[doomMap.Linedefs.Count];
		
		MAPBrush[][] sectorFloorBrushes = new MAPBrush[doomMap.Sectors.Count][];
		for (int i3 = 0; i3 < doomMap.Sectors.Count; i3++)
		{
			sectorFloorBrushes[i3] = new MAPBrush[0];
		}
		MAPBrush[][] sectorCielingBrushes = new MAPBrush[doomMap.Sectors.Count][];
		for (int i4 = 0; i4 < doomMap.Sectors.Count; i4++)
		{
			sectorCielingBrushes[i4] = new MAPBrush[0];
		}
		
		// For one-sided linedefs referenced by more than one subsector
		bool[] outsideBrushAlreadyCreated = new bool[doomMap.Linedefs.Count];
		
		int onePercent = (int)((doomMap.SubSectors.Count)/100);
		if(onePercent < 1) {
			onePercent = 1;
		}
		for (int i = 0; i < doomMap.SubSectors.Count; i++)
		{
			//DecompilerThread.OnMessage(this, "Creating brushes for subsector " + i);
			
			Edge currentsubsector = doomMap.SubSectors[i];
			
			// Third, create a few brushes out of the geometry.
			MAPBrush cielingBrush = new MAPBrush(numBrshs++, 0, false);
			MAPBrush floorBrush = new MAPBrush(numBrshs++, 0, false);
			MAPBrush midBrush = new MAPBrush(numBrshs++, 0, false);
			MAPBrush sectorTriggerBrush = new MAPBrush(numBrshs++, 0, false);
			DSector currentSector = doomMap.Sectors[subsectorSectors[i]];
			
			Vector3D[] roofPlane = new Vector3D[3];
			double[] roofTexS = new double[3];
			double[] roofTexT = new double[3];
			roofPlane[0] = new Vector3D(0, Settings.planePointCoef, ZMax);
			roofPlane[1] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, ZMax);
			roofPlane[2] = new Vector3D(Settings.planePointCoef, 0, ZMax);
			roofTexS[0] = 1;
			roofTexT[1] = - 1;
			
			Vector3D[] cileingPlane = new Vector3D[3];
			double[] cileingTexS = new double[3];
			double[] cileingTexT = new double[3];
			cileingPlane[0] = new Vector3D(0, 0, currentSector.CielingHeight);
			cileingPlane[1] = new Vector3D(Settings.planePointCoef, 0, currentSector.CielingHeight);
			cileingPlane[2] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.CielingHeight);
			cileingTexS[0] = 1;
			cileingTexT[1] = - 1;
			
			Vector3D[] floorPlane = new Vector3D[3];
			double[] floorTexS = new double[3];
			double[] floorTexT = new double[3];
			floorPlane[0] = new Vector3D(0, Settings.planePointCoef, currentSector.FloorHeight);
			floorPlane[1] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.FloorHeight);
			floorPlane[2] = new Vector3D(Settings.planePointCoef, 0, currentSector.FloorHeight);
			floorTexS[0] = 1;
			floorTexT[1] = - 1;
			
			Vector3D[] foundationPlane = new Vector3D[3];
			double[] foundationTexS = new double[3];
			double[] foundationTexT = new double[3];
			foundationPlane[0] = new Vector3D(0, 0, ZMin);
			foundationPlane[1] = new Vector3D(Settings.planePointCoef, 0, ZMin);
			foundationPlane[2] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, ZMin);
			foundationTexS[0] = 1;
			foundationTexT[1] = - 1;
			
			Vector3D[] topPlane = new Vector3D[3];
			double[] topTexS = new double[3];
			double[] topTexT = new double[3];
			topPlane[0] = new Vector3D(0, Settings.planePointCoef, currentSector.CielingHeight);
			topPlane[1] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.CielingHeight);
			topPlane[2] = new Vector3D(Settings.planePointCoef, 0, currentSector.CielingHeight);
			topTexS[0] = 1;
			topTexT[1] = - 1;
			
			Vector3D[] bottomPlane = new Vector3D[3];
			double[] bottomTexS = new double[3];
			double[] bottomTexT = new double[3];
			bottomPlane[0] = new Vector3D(0, 0, currentSector.FloorHeight);
			bottomPlane[1] = new Vector3D(Settings.planePointCoef, 0, currentSector.FloorHeight);
			bottomPlane[2] = new Vector3D(Settings.planePointCoef, Settings.planePointCoef, currentSector.FloorHeight);
			bottomTexS[0] = 1;
			bottomTexT[1] = - 1;
			
			int nextNode = ssparents[i];
			bool leftSide = ssIsLeft[i];
			
			for (int j = 0; j < currentsubsector.NumSegs; j++)
			{
				// Iterate through the sidedefs defined by segments of this subsector
				DSegment currentseg = doomMap.Segments[currentsubsector.FirstSeg + j];
				Vector3D start = doomMap.Vertices[currentseg.StartVertex].Vector;
				Vector3D end = doomMap.Vertices[currentseg.EndVertex].Vector;
				DLinedef currentLinedef = doomMap.Linedefs[(int) currentseg.Linedef];
				
				Vector3D[] plane = new Vector3D[3];
				double[] texS = new double[3];
				double[] texT = new double[3];
				plane[0] = new Vector3D(start.X, start.Y, ZMin);
				plane[1] = new Vector3D(end.X, end.Y, ZMin);
				plane[2] = new Vector3D(end.X, end.Y, ZMax);
				
				Vector3D linestart = new Vector3D(doomMap.Vertices[currentLinedef.Start].Vector.X, doomMap.Vertices[currentLinedef.Start].Vector.Y, ZMin);
				Vector3D lineend = new Vector3D(doomMap.Vertices[currentLinedef.End].Vector.X, doomMap.Vertices[currentLinedef.End].Vector.Y, ZMax);
				
				double sideLength = System.Math.Sqrt(System.Math.Pow(start.X - end.X, 2) + System.Math.Pow(start.Y - end.Y, 2));
				
				bool upperUnpegged = !((currentLinedef.Flags[0] & ((sbyte) 1 << 3)) == 0);
				bool lowerUnpegged = !((currentLinedef.Flags[0] & ((sbyte) 1 << 4)) == 0);
				
				texS[0] = (start.X - end.X) / sideLength;
				texS[1] = (start.Y - end.Y) / sideLength;
				texS[2] = 0;
				texT[0] = 0;
				texT[1] = 0;
				texT[2] = - 1;
				
				double SShift = sideDefShifts[X][subsectorSidedefs[i][j]] - (texS[0] * end.X) - (texS[1] * end.Y);
				double lowTShift = 0;
				double highTShift = 0;
				if (!currentLinedef.OneSided)
				{
					DSector otherSideSector;
					if (doomMap.Sectors[doomMap.Sidedefs[currentLinedef.Left].Sector] == currentSector)
					{
						otherSideSector = doomMap.Sectors[doomMap.Sidedefs[currentLinedef.Right].Sector];
					}
					else
					{
						otherSideSector = doomMap.Sectors[doomMap.Sidedefs[currentLinedef.Left].Sector];
					}
					if (lowerUnpegged)
					{
						lowTShift = otherSideSector.CielingHeight;
					}
					else
					{
						lowTShift = currentSector.FloorHeight;
					}
					if (upperUnpegged)
					{
						highTShift = otherSideSector.CielingHeight;
					}
					else
					{
						highTShift = currentSector.CielingHeight;
					}
					lowTShift += sideDefShifts[Y][subsectorSidedefs[i][j]];
					highTShift += sideDefShifts[Y][subsectorSidedefs[i][j]];
				}
				MAPBrushSide low = new MAPBrushSide(plane, lowerWallTextures[subsectorSidedefs[i][j]], texS, SShift, texT, lowTShift, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide high = new MAPBrushSide(plane, higherWallTextures[subsectorSidedefs[i][j]], texS, SShift, texT, highTShift, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide mid;
				MAPBrushSide damage = new MAPBrushSide(plane, "special/trigger", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				
				if (currentLinedef.OneSided)
				{
					if (!outsideBrushAlreadyCreated[currentseg.Linedef])
					{
						outsideBrushAlreadyCreated[currentseg.Linedef] = true;
						double highestCieling = currentSector.CielingHeight;
						double lowestFloor = currentSector.FloorHeight;
						if (currentSector.Tag != 0)
						{
							double temp = getHighestNeighborCielingHeight(subsectorSectors[i]);
							if (temp > highestCieling)
							{
								highestCieling = temp;
							}
							temp = getLowestNeighborFloorHeight(subsectorSectors[i]);
							if (temp < lowestFloor)
							{
								lowestFloor = temp;
							}
						}
						MAPBrush outsideBrush = null;
						if (lowestFloor <= highestCieling)
						{
							outsideBrush = MAPBrush.createFaceBrush(midWallTextures[subsectorSidedefs[i][j]], "special/nodraw", new Vector3D(linestart.X, linestart.Y, ZMin), new Vector3D(lineend.X, lineend.Y, ZMax), sideDefShifts[X][subsectorSidedefs[i][j]], sideDefShifts[Y][subsectorSidedefs[i][j]], lowerUnpegged, currentSector.CielingHeight, currentSector.FloorHeight);
						}
						else
						{
							outsideBrush = MAPBrush.createFaceBrush(midWallTextures[subsectorSidedefs[i][j]], "special/nodraw", new Vector3D(linestart.X, linestart.Y, lowestFloor), new Vector3D(lineend.X, lineend.Y, highestCieling), sideDefShifts[X][subsectorSidedefs[i][j]], sideDefShifts[Y][subsectorSidedefs[i][j]], lowerUnpegged, currentSector.CielingHeight, currentSector.FloorHeight);
						}
						world.Brushes.Add(outsideBrush);
					}
					mid = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				}
				else
				{
					double midTShift = sideDefShifts[Y][subsectorSidedefs[i][j]];
					if (lowerUnpegged)
					{
						midTShift += currentSector.FloorHeight;
					}
					else
					{
						midTShift += currentSector.CielingHeight;
					}
					mid = new MAPBrushSide(plane, midWallTextures[subsectorSidedefs[i][j]], texS, SShift, texT, midTShift, 0, 1, 1, 0, "wld_masked", 16, 0);
					if (!linedefFlagsDealtWith[currentseg.Linedef])
					{
						linedefFlagsDealtWith[currentseg.Linedef] = true;
						if (!((currentLinedef.Flags[0] & ((sbyte) 1 << 0)) == 0))
						{
							// Flag 0x0001 indicates "solid" but doesn't block bullets. It is assumed for all one-sided.
							MAPBrush solidBrush = MAPBrush.createFaceBrush("special/clip", "special/clip", linestart, lineend, 0, 0, false, 0, 0);
							world.Brushes.Add(solidBrush);
						}
						else
						{
							if (!((currentLinedef.Flags[0] & ((sbyte) 1 << 1)) == 0))
							{
								// Flag 0x0002 indicates "monster clip".
								MAPBrush solidBrush = MAPBrush.createFaceBrush("special/enemyclip", "special/enemyclip", linestart, lineend, 0, 0, false, 0, 0);
								world.Brushes.Add(solidBrush);
							}
						}
					}
					DSidedef otherside = doomMap.Sidedefs[currentLinedef.Left];
					if (currentLinedef.Action != 0 && !linedefSpecialsDealtWith[currentseg.Linedef])
					{
						linedefSpecialsDealtWith[currentseg.Linedef] = true;
						Entity trigger = null;
						MAPBrush triggerBrush = MAPBrush.createFaceBrush("special/trigger", "special/trigger", linestart, lineend, 0, 0, false, 0, 0);
						if (doomMap.Version == mapType.TYPE_HEXEN)
						{
							bool[] bitset = new bool[16];
							for (int k = 0; k < 8; k++)
							{
								bitset[k] = !((currentLinedef.Flags[0] & ((sbyte) k << 1)) == 0);
							}
							for (int k = 0; k < 8; k++)
							{
								bitset[k + 8] = !((currentLinedef.Flags[1] & ((sbyte) k << 1)) == 0);
							}
							if (bitset[10] && bitset[11] && !bitset[12])
							{
								// Triggered when "Used" by player
								trigger = new Entity("func_button");
								trigger["spawnflags"] = "1";
								if (bitset[9])
								{
									trigger["wait"] = "1";
								}
								else
								{
									trigger["wait"] = "-1";
								}
							}
							else
							{
								if (bitset[9])
								{
									// Can be activated more than once
									trigger = new Entity("trigger_multiple");
									trigger["wait"] = "1";
								}
								else
								{
									trigger = new Entity("trigger_once");
								}
							}
							switch (currentLinedef.Action)
							{
								case 21: 
								// Floor lower to lowest surrounding floor
								case 22:  // Floor lower to next lowest surrounding floor
									if (currentLinedef.Arguments[0] != 0)
									{
										trigger["target"] = "sector" + currentLinedef.Arguments[0] + "lowerfloor";
									}
									else
									{
										trigger["target"] = "sectornum" + otherside.Sector + "lowerfloor";
									}
									break;
								case 24: 
								// Floor raise to highest surrounding floor
								case 25:  // Floor raise to next highest surrounding floor
									if (currentLinedef.Arguments[0] != 0)
									{
										trigger["target"] = "sector" + currentLinedef.Arguments[0] + "raisefloor";
									}
									else
									{
										trigger["target"] = "sectornum" + otherside.Sector + "raisefloor";
									}
									break;
								case 70:  // Teleport
									trigger = new Entity("trigger_teleport");
									if (currentLinedef.Arguments[0] != 0)
									{
										trigger["target"] = "teledest" + currentLinedef.Arguments[0];
									}
									else
									{
										trigger["target"] = "sector" + currentLinedef.Tag + "teledest";
									}
									break;
								case 80:  // Exec script
									// This is a toughie. I can't write a script-to-entity converter.
									trigger["target"] = "script" + currentLinedef.Arguments[0];
									trigger["arg0"] = "" + currentLinedef.Arguments[2];
									trigger["arg1"] = "" + currentLinedef.Arguments[3];
									trigger["arg2"] = "" + currentLinedef.Arguments[4];
									break;
								case 181:  // PLANE_ALIGN
									trigger = null;
									if (!leftSide)
									{
										DSidedef getsector = doomMap.Sidedefs[currentLinedef.Left];
										DSector copyheight = doomMap.Sectors[getsector.Sector];
										short newHeight = copyheight.FloorHeight;
										//floorPlane[0]=new Vector3D(0, Settings.getPlanePointCoef(), 2000);
										//floorPlane[1]=new Vector3D(Settings.getPlanePointCoef(), Settings.getPlanePointCoef(), currentSector.getFloorHeight());
										//floorPlane[2]=new Vector3D(Settings.getPlanePointCoef(), 0, currentSector.getFloorHeight());
									}
									else
									{
										linedefSpecialsDealtWith[currentseg.Linedef] = false;
									}
									break;
								default: 
									trigger = null;
									break;
							}
						}
						else
						{
							switch (currentLinedef.Action)
							{
								
								case 1: 
								// Use Door. open, wait, close
								case 31:  // Use Door. Open, stay.
									trigger = new Entity("func_button");
									trigger["wait"] = "1";
									if (currentLinedef.Action == 31)
									{
										trigger["wait"] = "-1";
									}
									trigger["spawnflags"] = "1";
									if (doomMap.Sectors[otherside.Sector].Tag == 0)
									{
										trigger["target"] = "sectornum" + otherside.Sector + "door";
										if (currentLinedef.Action == 1)
										{
											sectorTag[otherside.Sector] = - 1;
										}
										if (currentLinedef.Action == 31)
										{
											sectorTag[otherside.Sector] = - 2;
										}
									}
									else
									{
										trigger["target"] = "sector" + doomMap.Sectors[otherside.Sector].Tag + "door";
									}
									break;
								
								case 36: 
								// Floor lower to 8 above next lowest neighboring sector
								case 38:  // Floor lower to next lowest neighboring sector
									trigger = new Entity("trigger_once");
									trigger["target"] = "sector" + currentLinedef.Tag + "lowerfloor";
									break;
								
								case 62:  // Floor lower to next lowest neighboring sector, wait 4s, goes back up
									trigger = new Entity("func_button");
									trigger["target"] = "sector" + currentLinedef.Tag + "vator";
									trigger["wait"] = "1";
									trigger["spawnflags"] = "1";
									break;
								
								case 63: 
								// Door with button, retriggerable
								case 103:  // Push button, one-time door open stay open
									trigger = new Entity("func_button");
									trigger["target"] = "sector" + currentLinedef.Tag + "door";
									trigger["wait"] = "1";
									if (currentLinedef.Action == 103)
									{
										trigger["wait"] = "-1";
									}
									trigger["spawnflags"] = "1";
									break;
								
								case 88:  // Walkover retriggerable elevator trigger
									trigger = new Entity("trigger_multiple");
									trigger["target"] = "sector" + currentLinedef.Tag + "vator";
									break;
								
								case 97:  // Walkover retriggerable Teleport
									trigger = new Entity("trigger_teleport");
									trigger["target"] = "sector" + currentLinedef.Tag + "teledest";
									break;
								
								case 109:  // Walkover one-time door open stay open
									trigger = new Entity("trigger_once");
									trigger["target"] = "sector" + currentLinedef.Tag + "door";
									break;
							}
						}
						if (trigger != null)
						{
							trigger.Brushes.Add(triggerBrush);
							mapFile.Add(trigger);
						}
					}
				}
				
				cielingBrush.add(high);
				midBrush.add(mid);
				floorBrush.add(low);
				sectorTriggerBrush.add(damage);
			}
			
			MAPBrushSide roof = new MAPBrushSide(roofPlane, "special/nodraw", roofTexS, 0, roofTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide cieling = new MAPBrushSide(cileingPlane, doomMap.WadName + "/" + currentSector.CielingTexture, cileingTexS, 0, cileingTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide floor = new MAPBrushSide(floorPlane, doomMap.WadName + "/" + currentSector.FloorTexture, floorTexS, 0, floorTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide foundation = new MAPBrushSide(foundationPlane, "special/nodraw", foundationTexS, 0, foundationTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide top = new MAPBrushSide(topPlane, "special/nodraw", topTexS, 0, topTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide bottom = new MAPBrushSide(bottomPlane, "special/nodraw", bottomTexS, 0, bottomTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide invertedFloor = new MAPBrushSide(Plane.flip(floorPlane), "special/trigger", floorTexS, 0, floorTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			MAPBrushSide damageTop = new MAPBrushSide(new Vector3D[]{floorPlane[0]+new Vector3D(0, 0, 1), floorPlane[1]+new Vector3D(0, 0, 1), floorPlane[2]+new Vector3D(0, 0, 1)}, "special/trigger", floorTexS, 0, floorTexT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
			sectorTriggerBrush.add(damageTop);
			sectorTriggerBrush.add(invertedFloor);
			
			midBrush.add(top);
			midBrush.add(bottom);
			
			cielingBrush.add(cieling);
			cielingBrush.add(roof);
			
			floorBrush.add(floor);
			floorBrush.add(foundation);
			
			// Now need to add the data from node subdivisions. Neither segments nor nodes
			// will completely define a usable brush, but both of them together will.
			do 
			{
				DNode currentNode = doomMap.Nodes[nextNode];
				Vector3D start;
				Vector3D end;
				if (leftSide)
				{
					start = currentNode.VecHead+currentNode.VecTail;
					end = currentNode.VecHead;
				}
				else
				{
					start = currentNode.VecHead;
					end = currentNode.VecHead+currentNode.VecTail;
				}
				
				Vector3D[] plane = new Vector3D[3];
				double[] texS = new double[3];
				double[] texT = new double[3];
				// This is somehow always correct. And I'm okay with that.
				plane[0] = new Vector3D(start.X, start.Y, ZMin);
				plane[1] = new Vector3D(end.X, end.Y, ZMin);
				plane[2] = new Vector3D(start.X, start.Y, ZMax);
				
				double sideLength = System.Math.Sqrt(System.Math.Pow(start.X - end.X, 2) + System.Math.Pow(start.Y - end.Y, 2));
				
				texS[0] = (start.X - end.X) / sideLength;
				texS[1] = (start.Y - end.Y) / sideLength;
				texS[2] = 0;
				texT[0] = 0;
				texT[1] = 0;
				texT[2] = 1;
				MAPBrushSide low = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide high = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide mid = new MAPBrushSide(plane, "special/nodraw", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				MAPBrushSide damage = new MAPBrushSide(plane, "special/trigger", texS, 0, texT, 0, 0, 1, 1, 0, "wld_lightmap", 16, 0);
				
				cielingBrush.add(high);
				midBrush.add(mid);
				floorBrush.add(low);
				sectorTriggerBrush.add(damage);
				
				leftSide = nodeIsLeft[nextNode];
				nextNode = nodeparents[nextNode];
			}
			while (nextNode != - 1);
			// Now we need to get rid of all the sides that aren't used. Get a list of
			// the useless sides from one brush, and delete those sides from all of them,
			// since they all have the same sides.
			int[] badSides = new int[0];
			if (!Settings.dontCull)
			{
				badSides = MAPBrush.findUnusedPlanes(cielingBrush);
				// Need to iterate backward, since these lists go from low indices to high, and
				// the index of all subsequent items changes when something before it is removed.
				if (cielingBrush.NumSides - badSides.Length < 4)
				{
					DecompilerThread.OnMessage(this, "WARNING: Plane cull returned less than 4 sides for subsector " + i);
					badSides = new int[0];
				}
				else
				{
					for (int j = badSides.Length - 1; j > - 1; j--)
					{
						cielingBrush.delete(badSides[j]);
						floorBrush.delete(badSides[j]);
					}
				}
			}
			
			MAPBrush[] newFloorList = new MAPBrush[sectorFloorBrushes[subsectorSectors[i]].Length + 1];
			MAPBrush[] newCielingList = new MAPBrush[sectorCielingBrushes[subsectorSectors[i]].Length + 1];
			for (int j = 0; j < sectorFloorBrushes[subsectorSectors[i]].Length; j++)
			{
				newFloorList[j] = sectorFloorBrushes[subsectorSectors[i]][j];
				newCielingList[j] = sectorCielingBrushes[subsectorSectors[i]][j];
			}
			newFloorList[newFloorList.Length - 1] = floorBrush;
			newCielingList[newCielingList.Length - 1] = cielingBrush;
			sectorFloorBrushes[subsectorSectors[i]] = newFloorList;
			sectorCielingBrushes[subsectorSectors[i]] = newCielingList;
			
			bool containsMiddle = false;
			for (int j = 0; j < midBrush.NumSides; j++)
			{
				if (!midBrush[j].Texture.ToUpper().Equals("special/nodraw".ToUpper()))
				{
					containsMiddle = true;
					break;
				}
			}
			if (containsMiddle && currentSector.CielingHeight > currentSector.FloorHeight)
			{
				Entity middleEnt = new Entity("func_illusionary");
				if (midBrush.NumSides - badSides.Length >= 4)
				{
					for (int j = badSides.Length - 1; j > - 1; j--)
					{
						midBrush.delete(badSides[j]);
					}
				}
				
				middleEnt.Brushes.Add(midBrush);
				mapFile.Add(middleEnt);
			}
			Entity hurtMe = new Entity("trigger_hurt");
			Entity triggerSecret = new Entity("trigger_bondsecret");
			switch (currentSector.Type)
			{
				case 4: 
				// 20% damage/s with lighting properties
				case 11: 
				// 20% damage/s
				case 16:  // 20% damage/s plus end level when player is almost dead
					hurtMe["dmg"] = "40";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					hurtMe.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(hurtMe);
					break;
				case 5:  // 10% damage/s
					hurtMe["dmg"] = "20";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					hurtMe.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(hurtMe);
					break;
				case 7:  // 5% damage/s
					hurtMe["dmg"] = "10";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					hurtMe.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(hurtMe);
					break;
				case 9:  // "secret"
					triggerSecret["Sound"] = "common/mission_success.wav";
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					triggerSecret.Brushes.Add(sectorTriggerBrush);
					mapFile.Add(triggerSecret);
					break;
				case 10:  // 30 seconds after level start, "close" like a door
					if (currentSector.Tag == 0)
					{
						sectorTag[subsectorSectors[i]] = - 3;
					}
					break;
				case 14:  // 300 seconds after level start, "open" like a door
					if (currentSector.Tag == 0)
					{
						sectorTag[subsectorSectors[i]] = - 4;
					}
					break;
				default: 
					if (!Settings.dontCull)
					{
						if (sectorTriggerBrush.NumSides - badSides.Length >= 4)
						{
							for (int j = badSides.Length - 1; j > - 1; j--)
							{
								sectorTriggerBrush.delete(badSides[j]);
							}
						}
					}
					if ((currentSector.Type & 96) != 0)
					{
						// "Generalized" pain sectors
						if ((currentSector.Type & 96) == 32)
						{
							hurtMe["dmg"] = "10";
						}
						else
						{
							if ((currentSector.Type & 96) == 64)
							{
								hurtMe["dmg"] = "20";
							}
							else
							{
								if ((currentSector.Type & 96) == 96)
								{
									hurtMe["dmg"] = "40";
								}
							}
						}
						hurtMe.Brushes.Add(sectorTriggerBrush);
						mapFile.Add(hurtMe);
					}
					if ((currentSector.Type & 128) == 128)
					{
						// "Generalized" secret trigger
						triggerSecret["Sound"] = "common/mission_success.wav";
						triggerSecret.Brushes.Add(sectorTriggerBrush);
						mapFile.Add(triggerSecret);
					}
					break;
			}
			if((i+1)%onePercent == 0) {
				parent.OnProgress(this, (i+1)/(double)(doomMap.SubSectors.Count));
			}
			//Settings.setProgress(jobnum, i + 1, doomMap.SubSectors.Count, "Decompiling...");
		}
		
		// Add the brushes to the map, as world by default, or entities if they are supported.
		for (int i = 0; i < doomMap.Sectors.Count; i++)
		{
			bool[] floorsUsed = new bool[sectorFloorBrushes[i].Length];
			bool[] cielingsUsed = new bool[sectorCielingBrushes[i].Length];
			if (sectorTag[i] == 0)
			{
				for (int j = 0; j < sectorFloorBrushes[i].Length; j++)
				{
					world.Brushes.Add(sectorFloorBrushes[i][j]);
					floorsUsed[j] = true;
					world.Brushes.Add(sectorCielingBrushes[i][j]);
					cielingsUsed[j] = true;
				}
			}
			else
			{
				if (sectorTag[i] == - 1 || sectorTag[i] == - 2 || sectorTag[i] == - 3 || sectorTag[i] == - 4)
				{
					// I'm using this to mean a door with no tag number
					Entity newDoor = new Entity("func_door");
					newDoor["speed"] = "60";
					newDoor["angles"] = "270 0 0";
					newDoor["spawnflags"] = "256";
					newDoor["targetname"] = "sectornum" + i + "door";
					if (sectorTag[i] == - 1)
					{
						newDoor["wait"] = "4";
					}
					else
					{
						if (sectorTag[i] == - 2)
						{
							newDoor["wait"] = "-1";
						}
					}
					if (sectorTag[i] == - 3)
					{
						Entity triggerAuto = new Entity("trigger_auto");
						triggerAuto["target"] = "sectornum" + i + "door_mm";
						Entity multiManager = new Entity("multi_manager");
						multiManager["sectornum" + i + "door"] = "30";
						mapFile.Add(triggerAuto);
						mapFile.Add(multiManager);
					}
					if (sectorTag[i] == - 4)
					{
						newDoor["Spawnflags"] = "1";
						Entity triggerAuto = new Entity("trigger_auto");
						triggerAuto["target"] = "sectornum" + i + "door_mm";
						Entity multiManager = new Entity("multi_manager");
						multiManager["sectornum" + i + "door"] = "300";
						mapFile.Add(triggerAuto);
						mapFile.Add(multiManager);
					}
					int lowestNeighborCielingHeight = getLowestNeighborCielingHeight(i);
					int lip = ZMax - lowestNeighborCielingHeight + 4;
					newDoor["lip"] = "" + lip;
					for (int j = 0; j < sectorFloorBrushes[i].Length; j++)
					{
						cielingsUsed[j] = true;
						newDoor.Brushes.Add(sectorCielingBrushes[i][j]);
					}
					mapFile.Add(newDoor);
				}
				else
				{
					for (int j = 0; j < doomMap.Linedefs.Count; j++)
					{
						DLinedef currentLinedef = doomMap.Linedefs[j];
						int linedefTriggerType = currentLinedef.Action;
						if (doomMap.Version == mapType.TYPE_HEXEN)
						{
							switch (linedefTriggerType)
							{
								
								case 21: 
								// Floor lower to lowest neighbor
								case 22:  // Floor lower to nearest lower neighbor
									// I don't know where retriggerability is determined, or whether or not it goes back up.
									if (currentLinedef.Arguments[0] == sectorTag[i])
									{
										Entity newFloor = new Entity("func_door");
										newFloor["angles"] = "90 0 0";
										newFloor["wait"] = "-1";
										newFloor["speed"] = "" + currentLinedef.Arguments[1];
										if (currentLinedef.Arguments[0] == 0)
										{
											newFloor["targetname"] = "sectornum" + i + "lowerfloor";
										}
										else
										{
											newFloor["targetname"] = "sector" + currentLinedef.Arguments[0] + "lowerfloor";
										}
										int lowestNeighborFloorHeight;
										if (linedefTriggerType == 21)
										{
											lowestNeighborFloorHeight = getLowestNeighborFloorHeight(i);
										}
										else
										{
											lowestNeighborFloorHeight = getNextLowestNeighborFloorHeight(i);
										}
										if (lowestNeighborFloorHeight == 32768)
										{
											lowestNeighborFloorHeight = doomMap.Sectors[i].FloorHeight;
										}
										int lip = ZMin - lowestNeighborFloorHeight;
										newFloor["lip"] = "" + System.Math.Abs(lip);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												floorsUsed[k] = true;
												newFloor.Brushes.Add(sectorFloorBrushes[i][k]);
											}
										}
										mapFile.Add(newFloor);
									}
									break;
								case 24: 
								// Floor raise to highest neighbor
								case 25:  // Floor raise to nearest higher neighbor
									// I don't know where retriggerability is determined, or whether or not it goes back up.
									if (currentLinedef.Arguments[0] == sectorTag[i])
									{
										Entity newFloor = new Entity("func_door");
										newFloor["angles"] = "270 0 0";
										newFloor["wait"] = "-1";
										newFloor["speed"] = "" + currentLinedef.Arguments[1];
										if (currentLinedef.Arguments[0] == 0)
										{
											newFloor["targetname"] = "sectornum" + i + "raisefloor";
										}
										else
										{
											newFloor["targetname"] = "sector" + currentLinedef.Arguments[0] + "raisefloor";
										}
										int highestNeighborFloorHeight;
										if (linedefTriggerType == 24)
										{
											highestNeighborFloorHeight = getHighestNeighborFloorHeight(i);
										}
										else
										{
											highestNeighborFloorHeight = getNextHighestNeighborFloorHeight(i);
										}
										if (highestNeighborFloorHeight == - 32768)
										{
											highestNeighborFloorHeight = doomMap.Sectors[i].FloorHeight;
										}
										int lip = ZMin - highestNeighborFloorHeight;
										newFloor["lip"] = "" + System.Math.Abs(lip);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												floorsUsed[k] = true;
												newFloor.Brushes.Add(sectorFloorBrushes[i][k]);
											}
										}
										mapFile.Add(newFloor);
									}
									break;
								}
						}
						else
						{
							if (currentLinedef.Tag == sectorTag[i])
							{
								switch (linedefTriggerType)
								{
									
									case 36: 
									// Line crossed, floor lowers, stays 8 above next lowest
									case 38:  // Line crossed, floor lowers, stays at next lowest
										Entity newFloor = new Entity("func_door");
										newFloor["speed"] = "120";
										newFloor["angles"] = "90 0 0";
										newFloor["targetname"] = "sector" + sectorTag[i] + "lowerfloor";
										newFloor["wait"] = "-1";
										int lowestNeighborFloorHeight = getLowestNeighborFloorHeight(i);
										int lip = ZMin - lowestNeighborFloorHeight;
										if (linedefTriggerType == 36)
										{
											lip -= 8;
										}
										newFloor["lip"] = "" + System.Math.Abs(lip);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												floorsUsed[k] = true;
												newFloor.Brushes.Add(sectorFloorBrushes[i][k]);
											}
										}
										mapFile.Add(newFloor);
										break;
									case 63: 
									// Push button, door opens, waits 4s, closes
									case 103: 
									// Push button, door opens, stays
									case 109:  // Cross line, door opens, stays
										Entity newDoor = new Entity("func_door");
										newDoor["speed"] = "60";
										newDoor["angles"] = "270 0 0";
										newDoor["targetname"] = "sector" + sectorTag[i] + "door";
										newDoor["wait"] = "-1";
										if (sectorTag[i] == 63)
										{
											newDoor["wait"] = "4";
										}
										int lowestNeighborCielingHeight = getLowestNeighborCielingHeight(i);
										lip = ZMax - lowestNeighborCielingHeight + 4;
										newDoor["lip"] = "" + lip;
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!cielingsUsed[k])
											{
												cielingsUsed[k] = true;
												newDoor.Brushes.Add(sectorCielingBrushes[i][k]);
											}
										}
										mapFile.Add(newDoor);
										break;
									case 62: 
									// Push button, Elevator goes down to lowest, wait 4s, goes up
									case 88:  // Elevator goes down to lowest, wait 4s, goes up
										Entity newVator = new Entity("func_door");
										newVator["speed"] = "120";
										newVator["angles"] = "90 0 0";
										newVator["targetname"] = "sector" + sectorTag[i] + "vator";
										newVator["wait"] = "4";
										lowestNeighborFloorHeight = getLowestNeighborFloorHeight(i);
										lip = System.Math.Abs(ZMin - lowestNeighborFloorHeight);
										newVator["lip"] = "" + lip;
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												newVator.Brushes.Add(sectorFloorBrushes[i][k]);
												floorsUsed[k] = true;
											}
										}
										mapFile.Add(newVator);
										break;
									default:  // I'd like to not use this evenutally, all the trigger types ought to be handled
										DecompilerThread.OnMessage(this, "WARNING: Unimplemented linedef trigger type " + linedefTriggerType + " for sector " + i + " tagged " + sectorTag[i]);
										for (int k = 0; k < sectorFloorBrushes[i].Length; k++)
										{
											if (!floorsUsed[k])
											{
												world.Brushes.Add(sectorFloorBrushes[i][k]);
												floorsUsed[k] = true;
											}
											if (!cielingsUsed[k])
											{
												world.Brushes.Add(sectorCielingBrushes[i][k]);
												cielingsUsed[k] = true;
											}
										}
										break;
								}
							}
						}
					}
				}
			}
			for (int j = 0; j < sectorFloorBrushes[i].Length; j++)
			{
				if (!cielingsUsed[j])
				{
					world.Brushes.Add(sectorCielingBrushes[i][j]);
				}
				if (!floorsUsed[j])
				{
					world.Brushes.Add(sectorFloorBrushes[i][j]);
				}
			}
		}
		
		// Convert THINGS
		for (int i = 0; i < doomMap.Things.Count; i++)
		{
			DThing currentThing = doomMap.Things[i];
			// To find the true height of a thing, I need to iterate through nodes until I come to a subsector
			// definition. Then I need to use the floor height of the sector that subsector belongs to.
			Vector3D origin = currentThing.Origin;
			int subsectorIndex = doomMap.Nodes.Count - 1;
			while (subsectorIndex >= 0)
			{
				// Once child is negative, subsector is found
				DNode currentNode = doomMap.Nodes[subsectorIndex];
				Vector3D start = currentNode.VecHead;
				Vector3D end = currentNode.VecHead+currentNode.VecTail;
				Plane currentPlane = new Plane(start, end, new Vector3D(start.X, start.Y, 1));
				if (currentPlane.distance(origin) < 0)
				{
					subsectorIndex = currentNode.Child1;
				}
				else
				{
					subsectorIndex = currentNode.Child2;
				}
			}
			subsectorIndex += 32768;
			int sectorIndex = subsectorSectors[subsectorIndex];
			DSector thingSector = doomMap.Sectors[sectorIndex];
			if (origin.Z == 0)
			{
				origin.Z=thingSector.FloorHeight;
			}
			
			Entity thing = null;
			// Things from both Doom. Currently converting to appropriate Doom 3 entities.
			switch (currentThing.ClassNum)
			{
				case 1: 
				// Single player spawn
				case 2: 
				// coop
				case 3: 
				// coop
				case 4:  // coop
					thing = new Entity("info_player_start");
					if (currentThing.ClassNum > 1)
					{
						thing["targetname"] = "coopspawn" + currentThing.ClassNum;
					}
					playerStartOrigin = origin.X + " " + origin.Y + " " + (origin.Z + 36);
					thing["origin"] = playerStartOrigin;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 11:  // Deathmatch spawn
					thing = new Entity("info_player_deathmatch");
					thing["origin"] = origin.X + " " + origin.Y + " " + (origin.Z + 36);
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 14:  // Teleport destination
					thing = new Entity("info_teleport_destination");
					if (currentThing.ID != 0)
					{
						thing["targetname"] = "teledest" + currentThing.ID;
					}
					else
					{
						thing["targetname"] = "sector" + thingSector.Tag + "teledest";
					}
					thing["origin"] = origin.X + " " + origin.Y + " " + (origin.Z + 36);
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 17:  // Big cell pack
					thing = new Entity("ammo_cells_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 82:  // Super shotgun
					thing = new Entity("weapon_shotgun_double");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2001:  // Shotgun
					thing = new Entity("weapon_shotgun");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2002:  // Chaingun
					thing = new Entity("weapon_chaingun");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2003:  // Rocket launcher
					thing = new Entity("weapon_rocketlauncher");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2004:  // Plasma gun
					thing = new Entity("weapon_plasmagun");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2005:  // Chainsaw
					thing = new Entity("weapon_chainsaw");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2006:  // BFG9000
					thing = new Entity("weapon_bfg");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2007:  // Ammo clip
					thing = new Entity("ammo_clip_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2008:  // Shotgun shells
					thing = new Entity("ammo_shells_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2010:  // Rocket
					thing = new Entity("ammo_rockets_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2046:  // Box of Rockets
					thing = new Entity("ammo_rockets_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2047:  // Cell pack
					thing = new Entity("ammo_cells_small");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2048:  // Box of ammo
					thing = new Entity("ammo_bullets_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				
				case 2049:  // Box of shells
					thing = new Entity("ammo_shells_large");
					thing["origin"] = origin.X + " " + origin.Y + " " + origin.Z;
					thing["angles"] = "0 " + currentThing.Angle + " 0";
					break;
				}
			
			if (thing != null)
			{
				mapFile.Add(thing);
			}
		}
		
		Entity playerequip = new Entity("game_player_equip");
		playerequip["weapon_pistol"] = "1";
		playerequip["origin"] = playerStartOrigin;
		mapFile.Add(playerequip);
		parent.OnProgress(this, 1.0);
		return mapFile;
	}
Esempio n. 43
0
	private byte[] brushToByteArray(MAPBrush inMapBrush)
	{
		if (inMapBrush.NumSides < 4)
		{
			// Can't create a brush with less than 4 sides
			DecompilerThread.OnMessage(this, "WARNING: Tried to create brush from " + inMapBrush.NumSides + " sides!");
			return new byte[0];
		}
		string brush = (char) 0x09 + "solid" + (char) 0x0D + (char) 0x0A + (char) 0x09 + "{" + (char) 0x0D + (char) 0x0A + (char) 0x09 + (char) 0x09 + "\"id\" \"" + (nextID++) + "\"" + (char) 0x0D + (char) 0x0A;
		for (int i = 0; i < inMapBrush.NumSides; i++)
		{
			brush += brushSideToString(inMapBrush[i]);
		}
		brush += ((char) 0x09 + "}" + (char) 0x0D + (char) 0x0A);
		if (brush.Length < 40)
		{
			// Any brush this short contains no sides.
			DecompilerThread.OnMessage(this, "WARNING: Brush with no sides being written! Oh no!");
			return new byte[0];
		}
		else
		{
			byte[] brushbytes = new byte[brush.Length];
			for (int i = 0; i < brush.Length; i++)
			{
				brushbytes[i] = (byte) brush[i];
			}
			return brushbytes;
		}
	}
	// METHODS
	
	// +decompile()
	// Attempts to convert the BSP file back into a .MAP file.
	public virtual Entities decompile() {
		DecompilerThread.OnMessage(this, "Decompiling...");
		// In the decompiler, it is not necessary to copy all entities to a new object, since
		// no writing is ever done back to the BSP file.
		mapFile = BSPObject.Entities;
		int numTotalItems = 0;
		worldspawn = mapFile[mapFile.findAllWithAttribute("classname", "worldspawn")[0]];
		int onePercent = (int)((BSPObject.Brushes.Count + BSPObject.Entities.Count + BSPObject.Faces.Count)/100);
		if(onePercent < 1) {
			onePercent = 1;
		}
		// I need to go through each entity and see if it's brush-based.
		// Worldspawn is brush-based as well as any entity with model *#.
		for (int i = 0; i < BSPObject.Entities.Count; i++) {
			// For each entity
			//DecompilerThread.OnMessage(this, "Entity " + i + ": " + mapFile[i]["classname"]);
			numBrshs = 0; // Reset the brush count for each entity
			// getModelNumber() returns 0 for worldspawn, the *# for brush based entities, and -1 for everything else
			int currentModel = mapFile[i].ModelNumber;
			if (currentModel > - 1) {
				// If this is still -1 then it's strictly a point-based entity. Move on to the next one.
				int firstBrush = BSPObject.Models[currentModel].FirstBrush;
				int numBrushes = BSPObject.Models[currentModel].NumBrushes;
				numBrshs = 0;
				for (int j = 0; j < numBrushes; j++) {
					// For each brush
					//Console.Write("Brush " + j);
					decompileBrush(BSPObject.Brushes[j + firstBrush], i); // Decompile the brush
					numBrshs++;
					numTotalItems++;
					if(numTotalItems%onePercent == 0) {
						parent.OnProgress(this, numTotalItems/(double)(BSPObject.Brushes.Count + BSPObject.Entities.Count + BSPObject.Faces.Count));
					}
				}
			}
			numTotalItems++;
			if(numTotalItems%onePercent == 0) {
				parent.OnProgress(this, numTotalItems/(double)(BSPObject.Brushes.Count + BSPObject.Entities.Count + BSPObject.Faces.Count));
			}
		}
		if (!Settings.skipPlaneFlip) {
			DecompilerThread.OnMessage(this, "Num simple corrected brushes: " + numSimpleCorrects);
			DecompilerThread.OnMessage(this, "Num advanced corrected brushes: " + numAdvancedCorrects);
			DecompilerThread.OnMessage(this, "Num good brushes: " + numGoodBrushes);
		}
		foreach(Face face in BSPObject.Faces) {
			if(face.Facetype == Face.faceType.PATCH) {
				MAPPatch mapPatch = new MAPPatch(face.PatchSize[0], face.PatchSize[1], BSPObject.Textures[face.Texture].Name);
				for(int i=0; i<face.NumVertices; i++) {
					mapPatch.Add(BSPObject.Vertices[face.FirstVertex+i]);
				}
				MAPBrush mapBrush = new MAPBrush(mapPatch);
				worldspawn.Brushes.Add(mapBrush);
			}
		}
		parent.OnProgress(this, 1.0);
		return mapFile;
	}
Esempio n. 45
0
 // Calculates 3 face corners, to be used to define the plane in ASCII format.
 /// Author:		UltimateSniper
 /// Returns:	List of normalised plane vertex triplets.
 public static MAPBrush CalcBrushVertices(MAPBrush mapBrush)
 {
     //DecompilerThread.OnMessage(this, "Recalculating vertices");
     Plane[]      planes      = mapBrush.Planes;
     Vector3D[][] out_Renamed = new Vector3D[planes.Length][];
     // For each triplet of planes, find intersect point.
     for (int iP1 = 0; iP1 < planes.Length; iP1++)
     {
         for (int iP2 = iP1 + 1; iP2 < planes.Length; iP2++)
         {
             for (int iP3 = iP2 + 1; iP3 < planes.Length; iP3++)
             {
                 Vector3D testV = planes[iP1].trisect(planes[iP2], planes[iP3]);
                 if (testV != Vector3D.UNDEFINED)
                 {
                     bool isCorner = true;
                     // If point is not null, test if point is behind/on all planes (if so, it is a corner).
                     for (int iTest = 0; iTest < planes.Length; iTest++)
                     {
                         if (planes[iTest].Normal != planes[iP1].Normal && planes[iTest].Normal != planes[iP2].Normal && planes[iTest].Normal != planes[iP3].Normal)
                         {
                             if (planes[iTest].distance(testV) > Settings.precision)
                             {
                                 isCorner = false;
                                 break;
                             }
                         }
                     }
                     // If so, check which planes it is on.
                     if (isCorner)
                     {
                         for (int iChk = 0; iChk < planes.Length; iChk++)
                         {
                             // If on this plane, and plane's vertex triplet missing min 1 point (and does not already have this point), add it.
                             double dist = planes[iChk].distance(testV);
                             if (System.Math.Abs(dist) <= Settings.precision)
                             {
                                 // If first point on this plane, must create array.
                                 if (out_Renamed[iChk] == null)
                                 {
                                     out_Renamed[iChk] = new Vector3D[] { new Vector3D(testV), null, null };
                                 }
                                 else
                                 {
                                     // Check each value in the array for open spot OR identical point.
                                     for (int iChk2 = 0; iChk2 < 3; iChk2++)
                                     {
                                         // Open spot, fill it.
                                         if (out_Renamed[iChk][iChk2] == null)
                                         {
                                             out_Renamed[iChk][iChk2] = new Vector3D(testV);
                                             // If this is now a complete plane.
                                             if (iChk2 == 2)
                                             {
                                                 // Order complete triplet to make a plane facing same way as given plane.
                                                 Plane testP = new Plane(out_Renamed[iChk][0], out_Renamed[iChk][1], out_Renamed[iChk][2]);
                                                 // If normals are not pointing in same direction, re-order points.
                                                 if (testP.Normal * planes[iChk].Normal < 0)
                                                 {
                                                     Vector3D temp = new Vector3D(out_Renamed[iChk][1]);
                                                     out_Renamed[iChk][1] = new Vector3D(out_Renamed[iChk][2]);
                                                     out_Renamed[iChk][2] = temp;
                                                 }
                                             }
                                             break;
                                             // Else, if this list already has this point, skip out (to avoid doubling it).
                                         }
                                         else if (out_Renamed[iChk][iChk2] == testV)
                                         {
                                             break;
                                         }
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
     for (int i = 0; i < mapBrush.NumSides; i++)
     {
         mapBrush[i].setSide(mapBrush[i].Plane, out_Renamed[i]);
     }
     return(mapBrush);
 }
	private byte[] brushToByteArray(MAPBrush inBrush, int num) {
		if (inBrush.NumSides < 4) {
			// Can't create a brush with less than 4 sides
			DecompilerThread.OnMessage(this, "WARNING: Tried to create brush from " + inBrush.NumSides + " sides!");
			return new byte[0];
		}
		string brush = "// primitive " + num + (char) 0x0A + "{" + (char) 0x0A + " brushDef3" + (char) 0x0A + " {" + (char) 0x0A;
		for (int i = 0; i < inBrush.NumSides; i++) {
			brush += ("  " + brushSideToString(inBrush[i]) + (char) 0x0A);
		}
		brush += (" }" + (char) 0x0A + "}" + (char) 0x0A);
		if (brush.Length < 58) {
			// Any brush this short contains no sides.
			DecompilerThread.OnMessage(this, "WARNING: Brush with no sides being written! Oh no!");
			return new byte[0];
		} else {
			byte[] brushbytes = new byte[brush.Length];
			for (int i = 0; i < brush.Length; i++) {
				brushbytes[i] = (byte) brush[i];
			}
			return brushbytes;
		}
	}
	// -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);
		}
	}