/// <summary> /// Builds all <see cref="Mesh"/> objects for the <see cref="Entity"/> in <paramref name="instance"/> instance, /// combines them if necessary using <see cref="settings"/>.meshCombineOptions and adds the meshes to the /// <see cref="GameObject"/> in <paramref name="instance"/>. /// </summary> /// <param name="instance">The <see cref="EntityInstance"/> to build <see cref="Mesh"/>es for.</param> protected void BuildMesh(EntityInstance instance) { int modelNumber = instance.entity.modelNumber; Model model = bsp.models[modelNumber]; Dictionary <string, List <Mesh> > textureMeshMap = new Dictionary <string, List <Mesh> >(); GameObject gameObject = instance.gameObject; List <Face> faces = bsp.GetFacesInModel(model); int i = 0; for (i = 0; i < faces.Count; ++i) { Face face = faces[i]; if (face.numEdges <= 0 && face.numVertices <= 0) { continue; } int textureIndex = bsp.GetTextureIndex(face); string textureName = ""; if (textureIndex >= 0) { LibBSP.Texture texture = bsp.textures[textureIndex]; textureName = texture.name; if (!textureMeshMap.ContainsKey(textureName) || textureMeshMap[textureName] == null) { textureMeshMap[textureName] = new List <Mesh>(); } textureMeshMap[textureName].Add(CreateFaceMesh(face, textureName)); } } if (modelNumber == 0) { if (bsp.lodTerrains != null) { foreach (LODTerrain lodTerrain in bsp.lodTerrains) { if (lodTerrain.texture >= 0) { LibBSP.Texture texture = bsp.textures[lodTerrain.texture]; string textureName = texture.name; if (!textureMeshMap.ContainsKey(textureName) || textureMeshMap[textureName] == null) { textureMeshMap[textureName] = new List <Mesh>(); } textureMeshMap[textureName].Add(CreateLoDTerrainMesh(lodTerrain, textureName)); } } } } if (settings.meshCombineOptions != MeshCombineOptions.None) { Mesh[] textureMeshes = new Mesh[textureMeshMap.Count]; Material[] materials = new Material[textureMeshes.Length]; i = 0; foreach (KeyValuePair <string, List <Mesh> > pair in textureMeshMap) { textureMeshes[i] = MeshUtils.CombineAllMeshes(pair.Value.ToArray(), true, false); if (materialDirectory.ContainsKey(pair.Key)) { materials[i] = materialDirectory[pair.Key]; } if (settings.meshCombineOptions == MeshCombineOptions.PerMaterial) { GameObject textureGameObject = new GameObject(pair.Key); textureGameObject.transform.parent = gameObject.transform; textureGameObject.transform.localPosition = Vector3.zero; if (textureMeshes[i].normals.Length == 0 || textureMeshes[i].normals[0] == Vector3.zero) { textureMeshes[i].RecalculateNormals(); } textureMeshes[i].AddMeshToGameObject(new Material[] { materials[i] }, textureGameObject); #if UNITY_EDITOR if (!IsRuntime && (settings.assetSavingOptions & AssetSavingOptions.Meshes) > 0) { string meshPath = Path.Combine(Path.Combine(Path.Combine("Assets", settings.meshPath), bsp.MapNameNoExtension), "mesh_" + textureMeshes[i].GetHashCode() + ".asset").Replace('\\', '/'); Directory.CreateDirectory(Path.GetDirectoryName(meshPath)); AssetDatabase.CreateAsset(textureMeshes[i], meshPath); } #endif } ++i; } if (settings.meshCombineOptions != MeshCombineOptions.PerMaterial) { Mesh mesh = MeshUtils.CombineAllMeshes(textureMeshes, false, false); mesh.TransformVertices(gameObject.transform.localToWorldMatrix); if (mesh.normals.Length == 0 || mesh.normals[0] == Vector3.zero) { mesh.RecalculateNormals(); } mesh.AddMeshToGameObject(materials, gameObject); #if UNITY_EDITOR if (!IsRuntime && (settings.assetSavingOptions & AssetSavingOptions.Meshes) > 0) { string meshPath = Path.Combine(Path.Combine(Path.Combine("Assets", settings.meshPath), bsp.MapNameNoExtension), "mesh_" + mesh.GetHashCode() + ".asset").Replace('\\', '/'); Directory.CreateDirectory(Path.GetDirectoryName(meshPath)); AssetDatabase.CreateAsset(mesh, meshPath); } #endif } } else { i = 0; foreach (KeyValuePair <string, List <Mesh> > pair in textureMeshMap) { GameObject textureGameObject = new GameObject(pair.Key); textureGameObject.transform.parent = gameObject.transform; textureGameObject.transform.localPosition = Vector3.zero; Material material = materialDirectory[pair.Key]; foreach (Mesh mesh in pair.Value) { GameObject faceGameObject = new GameObject("Face"); faceGameObject.transform.parent = textureGameObject.transform; faceGameObject.transform.localPosition = Vector3.zero; if (mesh.normals.Length == 0 || mesh.normals[0] == Vector3.zero) { mesh.RecalculateNormals(); } mesh.AddMeshToGameObject(new Material[] { material }, faceGameObject); #if UNITY_EDITOR if (!IsRuntime && (settings.assetSavingOptions & AssetSavingOptions.Meshes) > 0) { string meshPath = Path.Combine(Path.Combine(Path.Combine("Assets", settings.meshPath), bsp.MapNameNoExtension), "mesh_" + mesh.GetHashCode() + ".asset").Replace('\\', '/'); Directory.CreateDirectory(Path.GetDirectoryName(meshPath)); AssetDatabase.CreateAsset(mesh, meshPath); } #endif } ++i; } } }
/// <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"); } }