/// <summary> /// Given a <see cref="Face"/> with a list of <see cref="Vertex"/> objects and triangles, loads those /// vertices into a <see cref="Mesh"/> object and returns it. /// </summary> /// <param name="bsp">The <see cref="BSP"/> object <paramref name="face"/> came from.</param> /// <param name="face">The <see cref="Face"/> to build a <see cref="Mesh"/> from.</param> /// <returns>The <see cref="Mesh"/> generated from the vertices and triangles in <see cref="Face"/>.</returns> public static Mesh LoadVerticesFromFace(BSP bsp, Face face) { Mesh mesh = LoadVertices(bsp.GetReferencedObjects <Vertex>(face, "vertices")); List <long> indices = bsp.GetReferencedObjects <long>(face, "indices"); int[] triangles = new int[indices.Count]; for (int i = 0; i < indices.Count; ++i) { triangles[i] = (int)indices[i]; } mesh.triangles = triangles; return(mesh); }
/// <summary> /// Builds a <see cref="Mesh"/> for all control vertices referenced by <paramref name="face"/> using /// <paramref name="curveTessellationLevel"/> to determine how many vertices will be used to build /// the curves. /// </summary> /// <param name="bsp">The <see cref="BSP"/> which <paramref name="face"/> came from.</param> /// <param name="face">The <see cref="Face"/> whose vertices will be interpreted as curve control points.</param> /// <param name="curveTessellationLevel">The number of times to tessellate the curves in this patch.</param> /// <returns>A <see cref="Mesh"/> built using the curve data in <paramref name="face"/>.</returns> public static Mesh CreatePatchMesh(BSP bsp, Face face, int curveTessellationLevel) { List <Mesh> curveSubmeshes = new List <Mesh>(); List <Vertex> controls = bsp.GetReferencedObjects <Vertex>(face, "vertices"); Vector2 size = face.patchSize; int xSize = (int)Mathf.Round(size[0]); for (int i = 0; i < size[1] - 2; i += 2) { for (int j = 0; j < size[0] - 2; j += 2) { int rowOff = (i * xSize); Vertex[] thisCurveControls = new Vertex[9]; // Store control points thisCurveControls[0] = controls[rowOff + j]; thisCurveControls[1] = controls[rowOff + j + 1]; thisCurveControls[2] = controls[rowOff + j + 2]; rowOff += xSize; thisCurveControls[3] = controls[rowOff + j]; thisCurveControls[4] = controls[rowOff + j + 1]; thisCurveControls[5] = controls[rowOff + j + 2]; rowOff += xSize; thisCurveControls[6] = controls[rowOff + j]; thisCurveControls[7] = controls[rowOff + j + 1]; thisCurveControls[8] = controls[rowOff + j + 2]; curveSubmeshes.Add(CreateQuadraticBezierMesh(thisCurveControls, curveTessellationLevel)); } } return(CombineAllMeshes(curveSubmeshes.ToArray(), true, false)); }
/// <summary> /// Gets all the <see cref="Face"/> objects associated with the given <paramref name="model"/>. /// </summary> /// <param name="bsp">This <see cref="BSP"/> object.</param> /// <param name="model">The <see cref="Model"/> object to get all <see cref="Face"/> objects for.</param> /// <returns> /// A <see cref="List"/><<see cref="Face"/>> containing all the <see cref="Face"/> objects occurring /// within the passed <see cref="Model"/> object, or <c>null</c> if the BSP doesn't have faces. /// </returns> public static List <Face> GetFacesInModel(this BSP bsp, Model model) { if (model.firstFace >= 0) { return(bsp.GetReferencedObjects <Face>(model, "faces")); } return(new List <Face>(0)); }
/// <summary> /// Gets all the <see cref="Brush"/> objects associated with the given <paramref name="model"/>. /// </summary> /// <param name="bsp">This <see cref="BSP"/> object.</param> /// <param name="model">The <see cref="Model"/> object to get all <see cref="Brush"/> objects for.</param> /// <returns> /// A <see cref="List"/><<see cref="Brush"/>> containing all the <see cref="Brush"/> objects occurring /// within the passed <see cref="Model"/> object, or <c>null</c> if the BSP doesn't have brushes. /// </returns> /// <remarks> /// The proper way to go from a <see cref="Model"/> reference to a collection of <see cref="Brush"/> objects /// depends on the type of BSP we're operating on. In the case of Quake 3, for example, models directly reference /// brushes, but Quake 2 and Source require a full tree traversal. /// </remarks> public static List <Brush> GetBrushesInModel(this BSP bsp, Model model) { if (model.firstBrush >= 0) { return(bsp.GetReferencedObjects <Brush>(model, "brushes")); } if (model.firstLeaf >= 0) { List <Leaf> leavesInModel = bsp.GetReferencedObjects <Leaf>(model, "leaves"); return(bsp.GetBrushesInLeafList(leavesInModel)); } if (model.headNode >= 0) { return(bsp.GetBrushesInLeafList(bsp.GetLeavesInTree(bsp.nodes[model.headNode]))); } return(new List <Brush>(0)); }
/// <summary> /// Gets all <see cref="Brush"/> objects referenced from a list of <see cref="Leaf"/> objects. /// </summary> /// <param name="bsp">This <see cref="BSP"/>.</param> /// <param name="leaves">A <see cref="List"/> of <see cref="Leaf"/> objects to get <see cref="Brush"/> references from.</param> /// <returns>All the <see cref="Brush"/> objects referenced from the given <see cref="Leaf"/> objects.</returns> public static List <Brush> GetBrushesInLeafList(this BSP bsp, IEnumerable <Leaf> leaves) { // Use HashSet here. A Brush may be referenced through many Leafs. The default pointer hash should prevent that. HashSet <Brush> brushes = new HashSet <Brush>(); foreach (Leaf leaf in leaves) { List <long> markBrushesInLeaf = bsp.GetReferencedObjects <long>(leaf, "markBrushes"); foreach (long markBrush in markBrushesInLeaf) { brushes.Add(bsp.brushes[(int)markBrush]); } } return(brushes.ToList <Brush>()); }
/// <summary> /// Gets all <see cref="Brush"/> objects referenced from a list of <see cref="Leaf"/> objects. /// </summary> /// <param name="bsp">This <see cref="BSP"/>.</param> /// <param name="leaves">A <see cref="List"/> of <see cref="Leaf"/> objects to get <see cref="Brush"/> references from.</param> /// <returns>All the <see cref="Brush"/> objects referenced from the given <see cref="Leaf"/> objects.</returns> public static List <Brush> GetBrushesInLeafList(this BSP bsp, IEnumerable <Leaf> leaves) { List <Brush> brushes = new List <Brush>(); bool[] brushesUsed = new bool[bsp.brushes.Count]; foreach (Leaf leaf in leaves) { List <long> markBrushesInLeaf = bsp.GetReferencedObjects <long>(leaf, "markBrushes"); foreach (long markBrush in markBrushesInLeaf) { if (!brushesUsed[(int)markBrush]) { brushes.Add(bsp.brushes[(int)markBrush]); brushesUsed[(int)markBrush] = true; } } } return(brushes); }
/// <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"); } }