/* =========================================================================== Copyright (C) 2025 the OpenMoHAA team This file is part of OpenMoHAA source code. OpenMoHAA source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. OpenMoHAA source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenMoHAA source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /** * @file navigation_bsp.cpp * @brief Generate vertices and indices from brushes, terrain and patches. * * 1. All shaders are parsed, they are used for solidity check. * 2. Planes, brush sides and brushes are loaded. * 3. Triangles are created by calculating brush windings * 4. Patches are parsed and rendered into triangles * 5. The LOD terrain is fully renderer into triangles * */ #include "g_local.h" #include "navigation_bsp.h" #include "navigation_bsp_lump.h" #include "../qcommon/qfiles.h" #include "../qcommon/container.h" #include "../qcommon/vector.h" #include "../script/scriptexception.h" /* ================= navVertice_t::navVertice_t ================= */ navVertice_t::navVertice_t() {} /* ================= navVertice_t::navVertice_t ================= */ navVertice_t::navVertice_t(const vec3_t& inXyz) : xyz(inXyz) {} /* ================= navVertice_t::navVertice_t ================= */ navVertice_t::navVertice_t(const Vector& inXyz) : xyz(inXyz) {} /* ================= navModel_t::AddVertice ================= */ void navSurface_t::AddVertice(const navVertice_t& vert) { vertices.AddObject(vert); AddPointToBounds(vert.xyz, bounds[0], bounds[1]); } /* ================= navModel_t::AddIndex ================= */ void navSurface_t::AddIndex(navIndice_t index) { if (index >= vertices.NumObjects()) { throw ScriptException("Attempt to add an index to a non-existent vertice (%d)", (int)index); } indices.AddObject(index); } /* ================= navModel_t::CalculateBounds ================= */ void navModel_t::CalculateBounds() { int i, j; for (i = 1; i <= surfaces.NumObjects(); i++) { const navSurface_t& surface = surfaces.ObjectAt(i); for (j = 1; j <= surface.vertices.NumObjects(); j++) { AddPointToBounds(surface.vertices.ObjectAt(j).xyz, bounds[0], bounds[1]); } } } /* ================= navModel_t::GetWorldMap ================= */ navModel_t& navMap_t::GetWorldMap() { return worldMap; } /* ================= navModel_t::GetWorldMap ================= */ const navModel_t& navMap_t::GetWorldMap() const { return worldMap; } /* ================= navModel_t::CreateModel ================= */ navModel_t& navMap_t::CreateModel() { int index = subModels.AddObject({}); return subModels.ObjectAt(index); } /* ================= navModel_t::GetNumSubmodels ================= */ int navMap_t::GetNumSubmodels() const { return subModels.NumObjects(); } /* ================= navModel_t::GetSubmodel ================= */ const navModel_t& navMap_t::GetSubmodel(int index) const { return subModels[index]; } /* ================= NavigationBSP::LoadShaders ================= */ void NavigationBSP::LoadShaders(const gameLump_c& lump, Container& shaders) { dshader_t *in; int i, count; in = (dshader_t *)lump.buffer; if (lump.length % sizeof(dshader_t)) { throw ScriptException("Invalid shader lump"); } count = lump.length / sizeof(dshader_t); if (count < 1) { throw ScriptException("Map with no shaders"); } shaders.Resize(count); for (i = 0; i < count; i++, in++) { cshader_t out; Q_strncpyz(out.shader, in->shader, sizeof(out.shader)); out.contentFlags = LittleLong(in->contentFlags); out.surfaceFlags = LittleLong(in->surfaceFlags); shaders.AddObject(out); } } /* ================= NavigationBSP::IsValidContentsForPlayer ================= */ bool NavigationBSP::IsValidContentsForPlayer(int contents) { if (contents & CONTENTS_SOLID) { return true; } if (contents & CONTENTS_PLAYERCLIP) { return true; } if (contents & CONTENTS_FENCE) { return true; } return false; } /* ================= NavigationBSP::LoadPlanes ================= */ void NavigationBSP::LoadPlanes(const gameLump_c& lump, Container& planes) { int i, j; dplane_t *in; int count; int bits; in = (dplane_t *)lump.buffer; if (lump.length % sizeof(*in)) { throw ScriptException("Invalid plane lump"); } count = lump.length / sizeof(*in); if (count < 1) { throw ScriptException("Map with no planes"); } planes.Resize(count); for (i = 0; i < count; i++, in++) { cplane_t out; bits = 0; for (j = 0; j < 3; j++) { out.normal[j] = LittleFloat(in->normal[j]); if (out.normal[j] < 0) { bits |= 1 << j; } } out.dist = LittleFloat(in->dist); out.type = PlaneTypeForNormal(out.normal); out.signbits = bits; planes.AddObject(out); } } /* ================= NavigationBSP::LoadBrushSides ================= */ void NavigationBSP::LoadBrushSides( const gameLump_c& lump, const Container& shaders, const Container& planes, Container& sides ) { int i; dbrushside_t *in; int count; int num; in = (dbrushside_t *)lump.buffer; if (lump.length % sizeof(*in)) { throw ScriptException("Invalid plane lump"); } count = lump.length / sizeof(*in); sides.Resize(count); for (i = 0; i < count; i++, in++) { cbrushside_t out; num = LittleLong(in->planeNum); out.shaderNum = LittleLong(in->shaderNum); out.plane = &planes[num]; out.planenum = num; out.surfaceFlags = shaders.ObjectAt(out.shaderNum + 1).surfaceFlags; out.winding = NULL; sides.AddObject(out); } } /* ================= NavigationBSP::BoundBrush ================= */ void NavigationBSP::BoundBrush(cbrush_t *b) { b->bounds[0][0] = -b->sides[0].plane->dist; b->bounds[1][0] = b->sides[1].plane->dist; b->bounds[0][1] = -b->sides[2].plane->dist; b->bounds[1][1] = b->sides[3].plane->dist; b->bounds[0][2] = -b->sides[4].plane->dist; b->bounds[1][2] = b->sides[5].plane->dist; } /* ================= NavigationBSP::LoadBrushes ================= */ void NavigationBSP::LoadBrushes( const gameLump_c& lump, const Container& shaders, const Container& sides, Container& brushes ) { dbrush_t *in; int i, count; in = (dbrush_t *)lump.buffer; if (lump.length % sizeof(*in)) { throw ScriptException("Invalid brush lump"); } count = lump.length / sizeof(*in); brushes.Resize(count); for (i = 0; i < count; i++, in++) { cbrush_t out; out.sides = &sides.ObjectAt(LittleLong(in->firstSide) + 1); out.numsides = LittleLong(in->numSides); out.shaderNum = LittleLong(in->shaderNum); if (out.shaderNum < 0 || out.shaderNum >= shaders.NumObjects()) { throw ScriptException("bad shaderNum: %i", out.shaderNum); } out.contents = shaders[out.shaderNum].contentFlags; BoundBrush(&out); brushes.AddObject(out); } } /* ============ NavigationBSP::LoadLeafBrushes ============ */ void NavigationBSP::LoadLeafBrushes(const gameLump_c& lump, Container& leafbrushes) { int *in; int i, count; in = (int *)lump.buffer; if (lump.length % sizeof(*in)) { throw ScriptException("Invalid leaf brush lump"); } count = lump.length / sizeof(*in); for (i = 0; i < count; i++, in++) { leafbrushes.AddObject(LittleLong(*in)); } } /* ============ NavigationBSP::LoadSubmodels ============ */ void NavigationBSP::LoadSubmodels(const gameLump_c& lump, Container& submodels) { dmodel_t *in; int count; int i, j; in = (dmodel_t *)lump.buffer; if (lump.length % sizeof(*in)) { throw ScriptException("Invalid submodels lump"); } count = lump.length / sizeof(*in); if (count < 1) { Com_Error(ERR_DROP, "Map with no models"); } submodels.Resize(count); for (i = 0; i < count; i++, in++) { cmodel_t out; for (j = 0; j < 3; j++) { // spread the mins / maxs by a pixel out.mins[j] = LittleFloat(in->mins[j]) - 1; out.maxs[j] = LittleFloat(in->maxs[j]) + 1; } out.numBrushes = LittleLong(in->numBrushes); if (out.numBrushes) { out.firstBrush = LittleLong(in->firstBrush); } else { out.firstBrush = -1; } out.numSurfaces = LittleLong(in->numSurfaces); if (out.numSurfaces) { out.firstSurface = LittleLong(in->firstSurface); } else { out.firstSurface = -1; } submodels.AddObject(out); } } #define MAX_INDEXES 1024 /* ============ NavigationBSP::GenerateSideTriangles ============ */ void NavigationBSP::GenerateSideTriangles(navModel_t& model, const cbrush_t& brush, cbrushside_t& side) { int i, r, least, rotate, ni; int numIndexes; int a, b, c; const vec_t *v1, *v2; int indexes[MAX_INDEXES]; size_t baseVertex; navSurface_t *surface; if (!side.winding) { return; } if (!(brush.contents & (CONTENTS_PLAYERCLIP | CONTENTS_FENCE | CONTENTS_TRANSLUCENT))) { // Remove any nodraw surface that are not clips // These may be surfaces hidden where player are not supposed to stand on // like under the map. // // This fixes an issue in mohdm4 where the a path can be created // below the terrain... if (side.surfaceFlags & SURF_NODRAW) { // This is probably a surface a player shouldn't normally stand on return; } } if (side.surfaceFlags & SURF_SKY) { // Ignore sky surfaces return; } surface = &model.surfaces.ObjectAt(model.surfaces.AddObject({})); G_StripFaceSurface(*surface, side.winding); } /* ============ NavigationBSP::GenerateBrushTriangles ============ */ void NavigationBSP::GenerateBrushTriangles(navModel_t& model, const Container& planes, cbrush_t& brush) { size_t i; size_t numSkip = 0; if (!IsValidContentsForPlayer(brush.contents)) { return; } CreateBrushWindings(planes, brush); for (i = 0; i < brush.numsides; i++) { cbrushside_t& side = brush.sides[i]; GenerateSideTriangles(model, brush, side); } } /* ============ NavigationBSP::GenerateVerticesFromHull ============ */ void NavigationBSP::GenerateVerticesFromHull(bspMap_c& inBspMap, const Container& shaders) { Container planes; Container sides; Container brushes; Container submodels; const Container& worldSurfaces = navMap.GetWorldMap().surfaces; size_t i, j; LoadPlanes(inBspMap.LoadLump(LUMP_PLANES), planes); LoadBrushSides(inBspMap.LoadLump(LUMP_BRUSHSIDES), shaders, planes, sides); LoadBrushes(inBspMap.LoadLump(LUMP_BRUSHES), shaders, sides, brushes); LoadSubmodels(inBspMap.LoadLump(LUMP_MODELS), submodels); const cmodel_t& worldModel = submodels.ObjectAt(1); for (j = worldModel.firstBrush + 1; j <= worldModel.firstBrush + worldModel.numBrushes; j++) { cbrush_t& brush = brushes.ObjectAt(j); GenerateBrushTriangles(navMap.GetWorldMap(), planes, brush); } // // Get submodels // for (i = 2; i <= submodels.NumObjects(); i++) { const cmodel_t& submodel = submodels.ObjectAt(i); navModel_t& navModel = navMap.CreateModel(); if (submodel.firstBrush == -1) { navModel.surfaces.Resize(submodel.numSurfaces); for (j = submodel.firstSurface + 1; j <= submodel.firstSurface + submodel.numSurfaces; j++) { const navSurface_t& surface = worldSurfaces.ObjectAt(j); navModel.surfaces.AddObject(surface); } continue; } for (j = submodel.firstBrush + 1; j <= submodel.firstBrush + submodel.numBrushes; j++) { cbrush_t& brush = brushes.ObjectAt(j); GenerateBrushTriangles(navModel, planes, brush); } } } /* ============ NavigationBSP::RenderSurfaceGrid ============ */ void NavigationBSP::RenderSurfaceGrid(const surfaceGrid_t *grid, navSurface_t& outSurface) { int i; const Vector *vertices; const int *indexes; navSurface_t *surface; vertices = grid->getVertices(); indexes = grid->getIndices(); for (i = 0; i < grid->numVertices; i++) { outSurface.AddVertice(vertices[i]); } for (i = 0; i < grid->numIndexes; i++) { outSurface.AddIndex(indexes[i]); } } /* ============ NavigationBSP::ParseMesh ============ */ void NavigationBSP::ParseMesh( const dsurface_t *ds, const drawVert_t *verts, const Container& shaders, navSurface_t& outSurface ) { int i, j; int width, height, numPoints; Vector points[MAX_PATCH_SIZE * MAX_PATCH_SIZE]; surfaceGrid_t *grid; const cshader_t& shader = shaders.ObjectAt(LittleLong(ds->shaderNum) + 1); if (!IsValidContentsForPlayer(shader.contentFlags)) { return; } width = LittleLong(ds->patchWidth); height = LittleLong(ds->patchHeight); verts += LittleLong(ds->firstVert); numPoints = width * height; for (i = 0; i < numPoints; i++) { for (j = 0; j < 3; j++) { points[i][j] = LittleFloat(verts[i].xyz[j]); } } grid = G_SubdividePatchToGrid(width, height, points); // render the grid into vertices RenderSurfaceGrid(grid, outSurface); // destroy the grid G_FreeSurfaceGridMesh(grid); } /* ============ NavigationBSP::ParseTriSurf ============ */ void NavigationBSP::ParseTriSurf(const dsurface_t *ds, const drawVert_t *verts, const int *indexes) { int i, j; int numPoints, numIndexes; navSurface_t *surface; numPoints = LittleLong(ds->numVerts); numIndexes = LittleLong(ds->numIndexes); verts += LittleLong(ds->firstVert); indexes += LittleLong(ds->firstIndex); surface = &navMap.GetWorldMap().surfaces.ObjectAt(navMap.GetWorldMap().surfaces.AddObject({})); //navMap.GetWorldMap().vertices.Resize(navMap.GetWorldMap().vertices.NumObjects() + numPoints); for (i = 0; i < numPoints; i++) { Vector vec; for (j = 0; j < 3; j++) { vec[j] = LittleFloat(verts[i].xyz[j]); } surface->AddVertice(vec); } //navMap.GetWorldMap().indices.Resize(navMap.GetWorldMap().indices.NumObjects() + numIndexes); for (i = 0; i < numIndexes; i++) { surface->AddIndex(LittleLong(indexes[i])); } } /* ============ NavigationBSP::ParseFace ============ */ void NavigationBSP::ParseFace(const dsurface_t *ds, const drawVert_t *verts, const int *indexes) { int i, j; int numPoints, numIndexes; navSurface_t *surface; numPoints = LittleLong(ds->numVerts); numIndexes = LittleLong(ds->numIndexes); verts += LittleLong(ds->firstVert); indexes += LittleLong(ds->firstIndex); surface = &navMap.GetWorldMap().surfaces.ObjectAt(navMap.GetWorldMap().surfaces.AddObject({})); for (i = 0; i < numPoints; i++) { Vector vec; for (j = 0; j < 3; j++) { vec[j] = LittleFloat(verts[i].xyz[j]); } surface->AddVertice(vec); } for (i = 0; i < numIndexes; i++) { surface->AddIndex(LittleLong(indexes[i])); } } /* ============ NavigationBSP::ParseFlare ============ */ void NavigationBSP::ParseFlare(const dsurface_t *ds, const drawVert_t *verts) { // Nothing to do } /* ============ NavigationBSP::LoadSurfaces ============ */ void NavigationBSP::LoadSurfaces(bspMap_c& inBspMap, const Container& shaders) { const dsurface_t *in; const drawVert_t *dv; const int *indexes; navSurface_t *surface; int count; int i; int numVerts; gameLump_c surfs, verts, indexLump; // Gather vertices from meshes and surfaces surfs = inBspMap.LoadLump(LUMP_SURFACES); verts = inBspMap.LoadLump(LUMP_DRAWVERTS); indexLump = inBspMap.LoadLump(LUMP_DRAWINDEXES); if (surfs.length % sizeof(*in)) { throw ScriptException("LoadMap: funny lump size in %s", inBspMap.mapname); } count = surfs.length / sizeof(*in); dv = (const drawVert_t *)verts.buffer; if (verts.length % sizeof(*dv)) { throw ScriptException("LoadMap: funny lump size in %s", inBspMap.mapname); } indexes = (const int *)indexLump.buffer; if (indexLump.length % sizeof(*indexes)) { throw ScriptException("LoadMap: funny lump size in %s", inBspMap.mapname); } // // Calculate the number of vertices and indexes in = (dsurface_t *)surfs.buffer; navMap.GetWorldMap().surfaces.Resize(count); for (i = 0; i < count; i++, in++) { switch (LittleLong(in->surfaceType)) { case MST_PATCH: numVerts = in->patchWidth * in->patchHeight * 9; surface = &navMap.GetWorldMap().surfaces.ObjectAt(navMap.GetWorldMap().surfaces.AddObject({})); surface->vertices.Resize(numVerts); surface->indices.Resize(numVerts * 4); break; default: navMap.GetWorldMap().surfaces.AddObject({}); break; } } in = (dsurface_t *)surfs.buffer; for (i = 0; i < count; i++, in++) { switch (LittleLong(in->surfaceType)) { case MST_PATCH: ParseMesh(in, dv, shaders, navMap.GetWorldMap().surfaces.ObjectAt(i + 1)); break; case MST_TRIANGLE_SOUP: //ParseTriSurf(navMap, in, dv, indexes); break; case MST_PLANAR: //ParseFace(navMap, in, dv, indexes); break; case MST_FLARE: ParseFlare(in, dv); break; default: throw ScriptException("Bad surfaceType"); } } } /* ================ NavigationBSP::UnpackTerraPatch ================ */ void NavigationBSP::UnpackTerraPatch(const cTerraPatch_t *pPacked, cTerraPatchUnpacked_t *pUnpacked) { int i; if (pPacked->lmapScale <= 0) { throw ScriptException("invalid map: terrain has lmapScale <= 0"); } pUnpacked->x0 = ((int)pPacked->x << 6); pUnpacked->y0 = ((int)pPacked->y << 6); pUnpacked->z0 = pPacked->iBaseHeight; pUnpacked->iNorth = pPacked->iNorth; pUnpacked->iEast = pPacked->iEast; pUnpacked->iSouth = pPacked->iSouth; pUnpacked->iWest = pPacked->iWest; for (i = 0; i < MAX_TERRAIN_VARNODES; i++) { const varnode_t *packedVarTree; packedVarTree = &pPacked->varTree[0][i]; pUnpacked->varTree[0][i].fVariance = packedVarTree->flags & 0x7FF; pUnpacked->varTree[0][i].flags &= ~0xFF; pUnpacked->varTree[0][i].flags |= (packedVarTree->flags >> 12) & 0xFF; packedVarTree = &pPacked->varTree[1][i]; pUnpacked->varTree[1][i].fVariance = packedVarTree->flags & 0x7FF; pUnpacked->varTree[1][i].flags &= ~0xFF; pUnpacked->varTree[1][i].flags |= (packedVarTree->flags >> 12) & 0xFF; } for (i = 0; i < ARRAY_LEN(pUnpacked->heightmap); i++) { pUnpacked->heightmap[i] = pPacked->heightmap[i]; } pUnpacked->zmax = 0; pUnpacked->flags = pPacked->flags; for (i = 0; i < ARRAY_LEN(pUnpacked->heightmap); i++) { if (pUnpacked->zmax < pUnpacked->heightmap[i]) { pUnpacked->zmax = pUnpacked->heightmap[i]; } } pUnpacked->zmax += pUnpacked->zmax; } /* ==================== NavigationBSP::SwapTerraPatch Swaps the patch on big-endian ==================== */ void NavigationBSP::SwapTerraPatch(cTerraPatch_t *pPatch) { #ifdef Q3_BIG_ENDIAN int i; pPatch->iBaseHeight = LittleShort(pPatch->iBaseHeight); pPatch->iNorth = LittleShort(pPatch->iNorth); pPatch->iEast = LittleShort(pPatch->iEast); pPatch->iSouth = LittleShort(pPatch->iSouth); pPatch->iWest = LittleShort(pPatch->iWest); for (i = 0; i < MAX_TERRAIN_VARNODES; i++) { pPatch->varTree[0][i].flags = LittleUnsignedShort(pPatch->varTree[0][i].flags); pPatch->varTree[1][i].flags = LittleUnsignedShort(pPatch->varTree[1][i].flags); } #endif } /* ================ NavigationBSP::GenerateTerrainPatchMesh ================ */ void NavigationBSP::GenerateTerrainPatchMesh(const cTerraPatchUnpacked_t& patch) { terraInt vertNum; terraInt triNum; terraInt currentVertice = 0; navSurface_t *surface; surface = &navMap.GetWorldMap().surfaces.ObjectAt(navMap.GetWorldMap().surfaces.AddObject({})); for (vertNum = patch.drawinfo.iVertHead; vertNum; vertNum = g_pVert[vertNum].iNext) { terrainVert_t& vert = g_pVert[vertNum]; surface->AddVertice(vert.xyz); vert.iVertArray = currentVertice; currentVertice++; } for (triNum = patch.drawinfo.iTriHead; triNum; triNum = g_pTris[triNum].iNext) { const terraTri_t& tri = g_pTris[triNum]; if (tri.byConstChecks & 4) { surface->AddIndex(g_pVert[tri.iPt[0]].iVertArray); surface->AddIndex(g_pVert[tri.iPt[1]].iVertArray); surface->AddIndex(g_pVert[tri.iPt[2]].iVertArray); } } } /* ================ NavigationBSP::GenerateTerrainMesh ================ */ void NavigationBSP::GenerateTerrainMesh(cTerraPatchUnpacked_t *terraPatches, size_t numTerraPatches) { size_t i; size_t numVertices; size_t numIndices; terraInt triNum; if (!numTerraPatches) { return; } G_PreTessellateTerrain(terraPatches, numTerraPatches); G_DoTriSplitting(terraPatches, numTerraPatches); G_DoGeomorphs(terraPatches, numTerraPatches); // Calculate the number of required tris and vertices numVertices = 0; numIndices = 0; for (i = 0; i < numTerraPatches; i++) { const cTerraPatchUnpacked_t& patch = terraPatches[i]; numVertices += patch.drawinfo.nVerts; for (triNum = patch.drawinfo.iTriHead; triNum; triNum = g_pTris[triNum].iNext) { if (g_pTris[triNum].byConstChecks & 4) { numIndices += 3; } } } navMap.GetWorldMap().surfaces.Resize(navMap.GetWorldMap().surfaces.NumObjects() + numTerraPatches); for (i = 0; i < numTerraPatches; i++) { const cTerraPatchUnpacked_t& patch = terraPatches[i]; GenerateTerrainPatchMesh(patch); } G_TerrainFree(); } /* ================ NavigationBSP::LoadAndRenderTerrain ================ */ void NavigationBSP::LoadAndRenderTerrain(bspMap_c& inBspMap, const Container& shaders) { int i, j, k; cTerraPatch_t *in; cTerraPatchUnpacked_t *out; size_t numTerraPatches; cTerraPatchUnpacked_t *terraPatches; const gameLump_c lump = inBspMap.LoadLump(LUMP_TERRAIN); if (!lump.length) { return; } if (lump.length % sizeof(cTerraPatch_t)) { throw ScriptException("LoadTerrain: funny lump size in %s", inBspMap.mapname); } numTerraPatches = lump.length / sizeof(cTerraPatch_t); terraPatches = (cTerraPatchUnpacked_t *)gi.Malloc(numTerraPatches * sizeof(cTerraPatchUnpacked_t)); in = (cTerraPatch_t *)lump.buffer; out = terraPatches; #if 0 G_PrepareGenerateTerrainCollide(); for (i = 0; i < numTerraPatches; in++, out++, i++) { terrainCollide_t tc; navSurface_t *surface; SwapTerraPatch(in); G_GenerateTerrainCollide(in, &tc); if (!G_CreateTerPatchWindings(tc)) { continue; } surface = &navMap.GetWorldMap().surfaces.ObjectAt(navMap.GetWorldMap().surfaces.AddObject({})); for (j = 0; j < 8; j++) { for (k = 0; k < 8; k++) { const terrainCollideSquare_t& square = tc.squares[j][k]; if (square.w) { G_StripFaceSurface(*surface, square.w); } } } } #else for (i = 0; i < numTerraPatches; in++, out++, i++) { SwapTerraPatch(in); UnpackTerraPatch(in, out); } GenerateTerrainMesh(terraPatches, out - terraPatches); gi.Free(terraPatches); #endif } /* ================ NavigationBSP::CopyStaticModel ================ */ void NavigationBSP::CopyStaticModel(const cStaticModel_t *pSM, cStaticModelUnpacked_t *pUnpackedSM) { pUnpackedSM->angles[0] = LittleFloat(pSM->angles[0]); pUnpackedSM->angles[1] = LittleFloat(pSM->angles[1]); pUnpackedSM->angles[2] = LittleFloat(pSM->angles[2]); pUnpackedSM->origin[0] = LittleFloat(pSM->origin[0]); pUnpackedSM->origin[1] = LittleFloat(pSM->origin[1]); pUnpackedSM->origin[2] = LittleFloat(pSM->origin[2]); pUnpackedSM->scale = LittleFloat(pSM->scale); memcpy(pUnpackedSM->model, pSM->model, sizeof(pUnpackedSM->model)); } /* ================ NavigationBSP::LoadStaticModelDefs ================ */ void NavigationBSP::LoadStaticModelDefs(const gameLump_c& lump) { int i; const cStaticModel_t *in; cStaticModelUnpacked_t *out; size_t numStaticModels; cStaticModelUnpacked_t *staticModels; if (!lump.length) { return; } if (lump.length % sizeof(cStaticModel_t)) { throw ScriptException("G_LoadStaticModelDefs: funny lump size"); } numStaticModels = lump.length / sizeof(cStaticModel_t); staticModels = (cStaticModelUnpacked_t *)gi.Malloc(numStaticModels * sizeof(cStaticModelUnpacked_t)); in = (const cStaticModel_t *)lump.buffer; out = (cStaticModelUnpacked_t *)staticModels; for (i = 0; i < numStaticModels; in++, out++, i++) { CopyStaticModel(in, out); } } /* ============ NavigationBSP::ProcessBSPForNavigation ============ */ void NavigationBSP::ProcessBSPForNavigation(const char *mapname) { int length; int i; qhandle_t handle; Container shaders; // load it length = gi.FS_FOpenFile(mapname, &handle, qtrue, qtrue); if (length <= 0) { return; } bspMap_c bspMap(mapname, handle, length); gi.FS_Read(&bspMap.header, sizeof(dheader_t), bspMap.h); for (i = 0; i < sizeof(dheader_t) / 4; i++) { ((int *)&bspMap.header)[i] = LittleLong(((int *)&bspMap.header)[i]); } LoadShaders(bspMap.LoadLump(LUMP_SHADERS), shaders); // // Load patches // LoadSurfaces(bspMap, shaders); // // Create all vertices from brushes // GenerateVerticesFromHull(bspMap, shaders); // // Render the whole map terrain // LoadAndRenderTerrain(bspMap, shaders); }