/// <summary> /// Converts the passed <see cref="MAPTerrainMoHAA"/> into a <see cref="MAPTerrainEF2"/> /// with the same heightmap. /// </summary> /// <param name="mohTerrain">The <see cref="MAPTerrainMoHAA"/> to convert.</param> /// <returns><see cref="MAPTerrainEF2"/> with the same shader and heightmap as <paramref name="mohTerrain"/>.</returns> private MAPTerrainEF2 ConvertToEF2Terrain(MAPTerrainMoHAA mohTerrain) { if (mohTerrain.size == new Vector2(9, 9)) { MAPTerrainMoHAA.Partition partition = mohTerrain.partitions[0]; MAPTerrainEF2 ef2Terrain = new MAPTerrainEF2() { side = 9, texture = partition.shader, textureShiftS = partition.textureShift[0], textureShiftT = partition.textureShift[1], texRot = (float)partition.rotation, texScaleX = partition.textureScale[0], texScaleY = partition.textureScale[1], flags = 1024, // maybe don't hardcode this? sideLength = 512, start = new Vector3(mohTerrain.origin.X, mohTerrain.origin.Y, 0), IF = Vector4.Zero, LF = Vector4.Zero, heightMap = new float[9, 9], alphaMap = new float[9, 9] }; for (int y = 0; y < ef2Terrain.heightMap.GetLength(0); ++y) { for (int x = 0; x < ef2Terrain.heightMap.GetLength(1); ++x) { ef2Terrain.heightMap[y, x] = mohTerrain.vertices[(y * (int)mohTerrain.size.Y) + x].height + (float)mohTerrain.origin.Z; } } return(ef2Terrain); } return(null); }
/// <summary> /// Processes a <see cref="LODTerrain"/> in a <see cref="MAPTerrainMoHAA"/>. /// For MoHAA forks only. /// </summary> /// <param name="lodTerrain">The <see cref="LODTerrain"/> object to process.</param> /// <returns>A <see cref="MAPTerrainMoHAA"/> object to be added to a <see cref="MAPBrush"/> object.</returns> private MAPTerrainMoHAA ProcessTerrainMoHAA(LODTerrain lodTerrain) { string shader = _bsp.textures[lodTerrain.texture].name; MAPTerrainMoHAA.Partition partition = new MAPTerrainMoHAA.Partition() { shader = shader, textureScale = new double[] { 1, 1 }, }; MAPTerrainMoHAA terrain = new MAPTerrainMoHAA() { size = new Vector2d(9, 9), flags = ((lodTerrain.flags & (1 << 6)) > 0) ? 1 : 0, origin = new Vector3d(lodTerrain.x * 64, lodTerrain.y * 64, lodTerrain.baseZ), }; terrain.partitions.Add(partition); terrain.partitions.Add(partition); terrain.partitions.Add(partition); terrain.partitions.Add(partition); for (int i = 0; i < 9; ++i) { for (int j = 0; j < 9; ++j) { MAPTerrainMoHAA.Vertex vertex = new MAPTerrainMoHAA.Vertex() { height = lodTerrain.heightmap[i, j] * 2, }; terrain.vertices.Add(vertex); } } return(terrain); }
/// <summary> /// Processes a <see cref="LODTerrain"/> in a <see cref="MAPTerrainMoHAA"/>. /// For MoHAA forks only. /// </summary> /// <param name="lodTerrain">The <see cref="LODTerrain"/> object to process.</param> /// <returns>A <see cref="MAPTerrainMoHAA"/> object to be added to a <see cref="MAPBrush"/> object.</returns> private MAPTerrainMoHAA ProcessTerrainMoHAA(LODTerrain lodTerrain) { string shader = _bsp.textures[lodTerrain.TextureIndex].Name; MAPTerrainMoHAA.Partition partition = new MAPTerrainMoHAA.Partition() { shader = shader, textureScale = new float[] { 1, 1 }, }; MAPTerrainMoHAA terrain = new MAPTerrainMoHAA() { size = new Vector2(9, 9), flags = ((lodTerrain.Flags & (1 << 6)) > 0) ? 1 : 0, origin = new Vector3(lodTerrain.X * 64, lodTerrain.Y * 64, lodTerrain.BaseZ), }; terrain.partitions.Add(partition); terrain.partitions.Add(partition); terrain.partitions.Add(partition); terrain.partitions.Add(partition); for (int i = 0; i < 9; ++i) { for (int j = 0; j < 9; ++j) { MAPTerrainMoHAA.Vertex vertex = new MAPTerrainMoHAA.Vertex() { height = lodTerrain.Heightmap[i, j] * 2, }; terrain.vertices.Add(vertex); } } return(terrain); }
/// <summary> /// Postprocesser to convert the texture referenced by <paramref name="terrain"/> into one used by MOHRadiant, if necessary. /// </summary> /// <param name="patch">The <see cref="MAPTerrainMoHAA"/> to have its texture parsed.</param> private void PostProcessQuake3Texture(MAPTerrainMoHAA terrain) { foreach (MAPTerrainMoHAA.Partition partition in terrain.partitions) { if (partition.shader.StartsWith("textures/", StringComparison.InvariantCultureIgnoreCase)) { partition.shader = partition.shader.Substring(9); } } }
/// <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"); } }
/// <summary> /// Process the data in a <see cref="MAPTerrainMoHAA"/> into the passed <c>StringBuilder</c>. /// </summary> /// <param name="terrain">The <see cref="MAPTerrainMoHAA"/> to process.</param> /// <param name="sb">A <c>StringBuilder</c> object to append processed data from <paramref name="terrain"/> to.</param> private void ParseTerrain(MAPTerrainMoHAA terrain, StringBuilder sb) { sb.Append(" {\r\n terrainDef\r\n {\r\n ") .Append(terrain.size[0]) .Append(" ") .Append(terrain.size[1]) .Append(" ") .Append(terrain.flags) .Append("\r\n ") .Append(terrain.origin.x.ToString("###0.000000")) .Append(" ") .Append(terrain.origin.y.ToString("###0.000000")) .Append(" ") .Append(terrain.origin.z.ToString("###0.000000")) .Append("\r\n\t\t{\r\n"); foreach (MAPTerrainMoHAA.Partition partition in terrain.partitions) { sb.Append("\t\t\t") .Append(partition.unknown1) .Append(" ") .Append(partition.unknown2) .Append(" ( ") .Append(partition.shader) .Append(" ") .Append(partition.textureShift[0]) .Append(" ") .Append(partition.textureShift[1]) .Append(" ") .Append(partition.rotation.ToString("###0.00")) .Append(" ") .Append(partition.unknown3) .Append(" ") .Append(partition.textureScale[0]) .Append(" ") .Append(partition.textureScale[1]) .Append(" ") .Append(partition.unknown4) .Append(" ") .Append(partition.flags) .Append(" ") .Append(partition.unknown5); if (!string.IsNullOrEmpty(partition.properties)) { sb.Append(" ") .Append(partition.properties); } sb.Append(" )\r\n"); } sb.Append("\t\t}\r\n\t\t{\r\n"); foreach (MAPTerrainMoHAA.Vertex vertex in terrain.vertices) { sb.Append("\t\t\t") .Append(vertex.height.ToString("###0.000000")) .Append(" ( "); if (!string.IsNullOrEmpty(vertex.unknown1)) { sb.Append(vertex.unknown1) .Append(" "); } sb.Append(") ( "); if (!string.IsNullOrEmpty(vertex.unknown2)) { sb.Append(vertex.unknown2) .Append(" "); } sb.Append(")\r\n"); } sb.Append("\t\t}\r\n }\r\n }\r\n"); }