diff --git a/src/ObjLoading/Game/T6/BSP/BSP.h b/src/ObjLoading/Game/T6/BSP/BSP.h index 95e04897..f2811743 100644 --- a/src/ObjLoading/Game/T6/BSP/BSP.h +++ b/src/ObjLoading/Game/T6/BSP/BSP.h @@ -62,7 +62,7 @@ namespace BSP // These values are hardcoded ingame and will break the map if they are changed namespace BSPGameConstants { - constexpr int MAX_COLLISION_VERTS = UINT16_MAX; + constexpr unsigned int MAX_COLLISION_VERTS = UINT16_MAX; enum BSPDefaultLights { diff --git a/src/ObjLoading/Game/T6/BSP/BSPCalculation.cpp b/src/ObjLoading/Game/T6/BSP/BSPCalculation.cpp index 60ee89cd..b8a3a22b 100644 --- a/src/ObjLoading/Game/T6/BSP/BSPCalculation.cpp +++ b/src/ObjLoading/Game/T6/BSP/BSPCalculation.cpp @@ -20,7 +20,7 @@ namespace BSP objectList.emplace_back(std::move(object)); } - BSPObject* BSPLeaf::getObject(int index) + BSPObject* BSPLeaf::getObject(size_t index) { return objectList.at(index).get(); } diff --git a/src/ObjLoading/Game/T6/BSP/BSPCalculation.h b/src/ObjLoading/Game/T6/BSP/BSPCalculation.h index 61b8de07..9988f519 100644 --- a/src/ObjLoading/Game/T6/BSP/BSPCalculation.h +++ b/src/ObjLoading/Game/T6/BSP/BSPCalculation.h @@ -35,7 +35,7 @@ namespace BSP std::vector> objectList; void addObject(std::shared_ptr object); - BSPObject* getObject(int index); + BSPObject* getObject(size_t index); size_t getObjectCount(); }; diff --git a/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp b/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp index 63a41224..8aad76d0 100644 --- a/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp +++ b/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp @@ -193,7 +193,8 @@ namespace BSP { std::unique_ptr createBSPData(std::string& mapName, ISearchPath& searchPath) { - std::string gfxFbxPath = BSPUtil::getFileNameForBSPAsset("map_gfx.fbx"); + std::string gfxFbxFileName = "map_gfx.fbx"; + std::string gfxFbxPath = BSPUtil::getFileNameForBSPAsset(gfxFbxFileName); auto gfxFile = searchPath.Open(gfxFbxPath); if (!gfxFile.IsOpen()) { @@ -222,7 +223,8 @@ namespace BSP } ufbx_scene* colScene; - std::string colFbxPath = BSPUtil::getFileNameForBSPAsset("map_col.fbx"); + std::string colFbxFileName = "map_col.fbx"; + std::string colFbxPath = BSPUtil::getFileNameForBSPAsset(colFbxFileName); auto colFile = searchPath.Open(colFbxPath); if (!colFile.IsOpen()) { diff --git a/src/ObjLoading/Game/T6/BSP/BSPUtil.cpp b/src/ObjLoading/Game/T6/BSP/BSPUtil.cpp index 878f318d..0a282067 100644 --- a/src/ObjLoading/Game/T6/BSP/BSPUtil.cpp +++ b/src/ObjLoading/Game/T6/BSP/BSPUtil.cpp @@ -5,12 +5,12 @@ #include "BSPUtil.h" -std::string BSPUtil::getFileNameForBSPAsset(std::string assetName) +std::string BSPUtil::getFileNameForBSPAsset(std::string& assetName) { return std::format("BSP/{}", assetName); } -vec3_t BSPUtil::convertToBO2Coords(vec3_t coordinate) +vec3_t BSPUtil::convertToBO2Coords(vec3_t& coordinate) { vec3_t result; result.x = coordinate.x; @@ -19,7 +19,7 @@ vec3_t BSPUtil::convertToBO2Coords(vec3_t coordinate) return result; } -vec3_t BSPUtil::convertFromBO2Coords(vec3_t coordinate) +vec3_t BSPUtil::convertFromBO2Coords(vec3_t& coordinate) { vec3_t result; result.x = coordinate.x; @@ -28,54 +28,63 @@ vec3_t BSPUtil::convertFromBO2Coords(vec3_t coordinate) return result; } -void BSPUtil::updateAABB(vec3_t* newAABBMins, vec3_t* newAABBMaxs, vec3_t* AABBMins, vec3_t* AABBMaxs) +void BSPUtil::updateAABB(vec3_t& newAABBMins, vec3_t& newAABBMaxs, vec3_t& AABBMins, vec3_t& AABBMaxs) { - if (AABBMins->x > newAABBMins->x) - AABBMins->x = newAABBMins->x; + if (AABBMins.x > newAABBMins.x) + AABBMins.x = newAABBMins.x; - if (newAABBMaxs->x > AABBMaxs->x) - AABBMaxs->x = newAABBMaxs->x; + if (newAABBMaxs.x > AABBMaxs.x) + AABBMaxs.x = newAABBMaxs.x; - if (AABBMins->y > newAABBMins->y) - AABBMins->y = newAABBMins->y; + if (AABBMins.y > newAABBMins.y) + AABBMins.y = newAABBMins.y; - if (newAABBMaxs->y > AABBMaxs->y) - AABBMaxs->y = newAABBMaxs->y; + if (newAABBMaxs.y > AABBMaxs.y) + AABBMaxs.y = newAABBMaxs.y; - if (AABBMins->z > newAABBMins->z) - AABBMins->z = newAABBMins->z; + if (AABBMins.z > newAABBMins.z) + AABBMins.z = newAABBMins.z; - if (newAABBMaxs->z > AABBMaxs->z) - AABBMaxs->z = newAABBMaxs->z; + if (newAABBMaxs.z > AABBMaxs.z) + AABBMaxs.z = newAABBMaxs.z; } -void BSPUtil::updateAABBWithpoint(vec3_t* point, vec3_t* AABBMins, vec3_t* AABBMaxs) +void BSPUtil::updateAABBWithPoint(vec3_t& point, vec3_t& AABBMins, vec3_t& AABBMaxs) { - if (AABBMins->x > point->x) - AABBMins->x = point->x; + if (AABBMins.x > point.x) + AABBMins.x = point.x; - if (point->x > AABBMaxs->x) - AABBMaxs->x = point->x; + if (point.x > AABBMaxs.x) + AABBMaxs.x = point.x; - if (AABBMins->y > point->y) - AABBMins->y = point->y; + if (AABBMins.y > point.y) + AABBMins.y = point.y; - if (point->y > AABBMaxs->y) - AABBMaxs->y = point->y; + if (point.y > AABBMaxs.y) + AABBMaxs.y = point.y; - if (AABBMins->z > point->z) - AABBMins->z = point->z; + if (AABBMins.z > point.z) + AABBMins.z = point.z; - if (point->z > AABBMaxs->z) - AABBMaxs->z = point->z; + if (point.z > AABBMaxs.z) + AABBMaxs.z = point.z; } -vec3_t BSPUtil::calcMiddleOfBounds(vec3_t* mins, vec3_t* maxs) +vec3_t BSPUtil::calcMiddleOfAABB(vec3_t& mins, vec3_t& maxs) { vec3_t result; - result.x = (mins->x + maxs->x) * 0.5f; - result.y = (mins->y + maxs->y) * 0.5f; - result.z = (mins->z + maxs->z) * 0.5f; + result.x = (mins.x + maxs.x) * 0.5f; + result.y = (mins.y + maxs.y) * 0.5f; + result.z = (mins.z + maxs.z) * 0.5f; + return result; +} + +vec3_t BSPUtil::calcHalfSizeOfAABB(vec3_t& mins, vec3_t& maxs) +{ + vec3_t result; + result.x = (maxs.x - mins.x) * 0.5f; + result.y = (maxs.y - mins.y) * 0.5f; + result.z = (maxs.z - mins.z) * 0.5f; return result; } @@ -84,7 +93,7 @@ int BSPUtil::allignBy128(int size) return ((size + 127) & 0xFFFFFF80); } -float BSPUtil::distBetweenPoints(vec3_t p1, vec3_t p2) +float BSPUtil::distBetweenPoints(vec3_t& p1, vec3_t& p2) { float x = p2.x - p1.x; float y = p2.y - p1.y; @@ -130,7 +139,7 @@ void BSPUtil::matrixTranspose3x3(const vec3_t* in, vec3_t* out) out[2].z = in[2].z; } -vec3_t BSPUtil::convertStringToVec3(std::string str) +vec3_t BSPUtil::convertStringToVec3(std::string& str) { std::string v1Str = str; @@ -153,7 +162,7 @@ vec3_t BSPUtil::convertStringToVec3(std::string str) return result; } -std::string BSPUtil::convertVec3ToString(vec3_t vec) +std::string BSPUtil::convertVec3ToString(vec3_t& vec) { std::string result = std::format("{} {} {}", vec.x, vec.y, vec.z); return result; diff --git a/src/ObjLoading/Game/T6/BSP/BSPUtil.h b/src/ObjLoading/Game/T6/BSP/BSPUtil.h index 30307bcf..474d51c1 100644 --- a/src/ObjLoading/Game/T6/BSP/BSPUtil.h +++ b/src/ObjLoading/Game/T6/BSP/BSPUtil.h @@ -6,16 +6,17 @@ class BSPUtil { public: - static std::string getFileNameForBSPAsset(std::string assetName); - static vec3_t convertToBO2Coords(vec3_t OGL_coordinate); - static vec3_t convertFromBO2Coords(vec3_t bo2_coordinate); - static void updateAABB(vec3_t* newAABBMins, vec3_t* newAABBMaxs, vec3_t* AABBMins, vec3_t* AABBMaxs); - static void updateAABBWithpoint(vec3_t* point, vec3_t* AABBMins, vec3_t* AABBMaxs); - static vec3_t calcMiddleOfBounds(vec3_t* mins, vec3_t* maxs); + static std::string getFileNameForBSPAsset(std::string& assetName); + static vec3_t convertToBO2Coords(vec3_t& OGL_coordinate); + static vec3_t convertFromBO2Coords(vec3_t& bo2_coordinate); + static void updateAABB(vec3_t& newAABBMins, vec3_t& newAABBMaxs, vec3_t& AABBMins, vec3_t& AABBMaxs); + static void updateAABBWithPoint(vec3_t& point, vec3_t& AABBMins, vec3_t& AABBMaxs); + static vec3_t calcMiddleOfAABB(vec3_t& mins, vec3_t& maxs); + static vec3_t calcHalfSizeOfAABB(vec3_t& mins, vec3_t& maxs); static int allignBy128(int size); - static float distBetweenPoints(vec3_t p1, vec3_t p2); + static float distBetweenPoints(vec3_t& p1, vec3_t& p2); static void convertAnglesToAxis(vec3_t* angles, vec3_t* axis); static void matrixTranspose3x3(const vec3_t* in, vec3_t* out); - static vec3_t convertStringToVec3(std::string str); - static std::string convertVec3ToString(vec3_t vec); + static vec3_t convertStringToVec3(std::string& str); + static std::string convertVec3ToString(vec3_t& vec); }; diff --git a/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.cpp b/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.cpp index e8617928..aee90d7c 100644 --- a/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.cpp +++ b/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.cpp @@ -1,12 +1,6 @@ #include "ClipMapLinker.h" #include "../BSPUtil.h" - -namespace -{ - -} - namespace BSP { ClipMapLinker::ClipMapLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context) @@ -122,7 +116,7 @@ namespace BSP // The game allocates 32 empty ropes clipMap->max_ropes = 32; // max 300 - clipMap->ropes = m_memory.Alloc (clipMap->max_ropes); + clipMap->ropes = m_memory.Alloc(clipMap->max_ropes); } void ClipMapLinker::loadSubModelCollision(clipMap_t* clipMap, BSPData* bsp) @@ -214,374 +208,334 @@ namespace BSP */ } - void aabbCalcOriginAndHalfSize(vec3_t* mins, vec3_t* maxs, vec3_t* out_origin, vec3_t* out_halfSize) - { - // Origin is the midpoint: (min + max) / 2 - vec3_t temp; - temp.x = mins->x + maxs->x; - temp.y = mins->y + maxs->y; - temp.z = mins->z + maxs->z; - out_origin->x = temp.x * 0.5f; - out_origin->y = temp.y * 0.5f; - out_origin->z = temp.z * 0.5f; - - // Half-size is half the difference: (max - min) / 2 - temp.x = maxs->x - mins->x; - temp.y = maxs->y - mins->y; - temp.z = maxs->z - mins->z; - out_halfSize->x = temp.x * 0.5f; - out_halfSize->y = temp.y * 0.5f; - out_halfSize->z = temp.z * 0.5f; - } - - void traverseBSPTreeForCounts(BSPTree* node, size_t* numPlanes, size_t* numNodes, size_t* numLeafs, size_t* numAABBTrees, size_t* maxObjsPerLeaf) - { - if (node->isLeaf) - { - (*numLeafs)++; - // there won't be an AABB tree when objectList is empty - if (node->leaf->getObjectCount() > 0) - { - *numAABBTrees += node->leaf->getObjectCount() + 1; - - if (node->leaf->getObjectCount() > *maxObjsPerLeaf) - *maxObjsPerLeaf = node->leaf->getObjectCount(); - } - } - else - { - (*numPlanes)++; - (*numNodes)++; - traverseBSPTreeForCounts(node->node->front.get(), numPlanes, numNodes, numLeafs, numAABBTrees, maxObjsPerLeaf); - traverseBSPTreeForCounts(node->node->back.get(), numPlanes, numNodes, numLeafs, numAABBTrees, maxObjsPerLeaf); - } - } - - vec3_t normalX = { 1.0f, 0.0f, 0.0f }; - vec3_t normalY = { 0.0f, 1.0f, 0.0f }; - vec3_t normalZ = { 0.0f, 0.0f, 1.0f }; - - int currPlaneCount = 0; - int currNodeCount = 0; - int currLeafCount = 0; - int currAABBCount = 0; - - int addAABBTreeFromLeaf(BSPTree* node, clipMap_t* clipMap) + int ClipMapLinker::addAABBTreeFromLeaf(BSPTree* node, clipMap_t* clipMap) { assert(node->isLeaf); - int objectCount = node->leaf->getObjectCount(); - int firstAABBIndex = currAABBCount; - currAABBCount += objectCount + 1; + size_t objectCount = node->leaf->getObjectCount(); + size_t parentAABBIndex = AABBTreeVec.size(); - // calculate root AABB node mins and maxs - // cannot convert mins and maxs coord to BO2 directly as this will result in incorrect mins and maxs - // so we have to recompute every min and max, not hard just tedious - int firstPartitionIndex = node->leaf->getObject(0)->partitionIndex; - auto firstPartition = &clipMap->partitions[firstPartitionIndex]; - uint16_t* firstTri = clipMap->triIndices[firstPartition->firstTri]; - vec3_t* firstVert = &clipMap->verts[firstTri[0]]; - vec3_t aabbMins; - vec3_t aabbMaxs; - aabbMins.x = firstVert->x; - aabbMins.y = firstVert->y; - aabbMins.z = firstVert->z; - aabbMaxs.x = firstVert->x; - aabbMaxs.y = firstVert->y; - aabbMaxs.z = firstVert->z; - for (int i = 0; i < objectCount; i++) + assert(objectCount > 0); + + // add the parent AABB node + vec3_t parentMins; + vec3_t parentMaxs; + for (size_t objectIdx = 0; objectIdx < objectCount; objectIdx++) { - int currPartitionIndex = node->leaf->getObject(i)->partitionIndex; - auto currPartition = &clipMap->partitions[currPartitionIndex]; - - for (int k = 0; k < currPartition->triCount; k++) + int partitionIndex = node->leaf->getObject(objectIdx)->partitionIndex; + CollisionPartition* partition = &clipMap->partitions[partitionIndex]; + for (int uindIdx = 0; uindIdx < partition->nuinds; uindIdx++) { - uint16_t* tri = clipMap->triIndices[currPartition->firstTri + k]; - for (int l = 0; l < 3; l++) + uint16_t uind = clipMap->info.uinds[partition->fuind + uindIdx]; + vec3_t vert = clipMap->verts[uind]; + + // initalise the parent AABB with the first vertex + if (objectIdx == 0 && uindIdx == 0) { - uint16_t vertIndex = tri[l]; - vec3_t vertCoord = clipMap->verts[vertIndex]; - BSPUtil::updateAABBWithpoint(&vertCoord, &aabbMins, &aabbMaxs); + parentMins = vert; + parentMaxs = vert; } + + BSPUtil::updateAABBWithPoint(vert, parentMins, parentMaxs); } } - CollisionAabbTree* rootAABB = &clipMap->aabbTrees[firstAABBIndex]; - aabbCalcOriginAndHalfSize(&aabbMins, &aabbMaxs, &rootAABB->origin, &rootAABB->halfSize); - rootAABB->materialIndex = 0; - rootAABB->childCount = objectCount; - rootAABB->u.firstChildIndex = firstAABBIndex + 1; - // populate child AABB nodes - for (int i = 0; i < objectCount; i++) + CollisionAabbTree parentAABB; + parentAABB.origin = BSPUtil::calcMiddleOfAABB(parentMins, parentMaxs); + parentAABB.halfSize = BSPUtil::calcHalfSizeOfAABB(parentMins, parentMaxs); + parentAABB.materialIndex = 0; // always use the first material + parentAABB.childCount = static_cast(objectCount); + parentAABB.u.firstChildIndex = static_cast(parentAABBIndex + 1); + AABBTreeVec.emplace_back(parentAABB); + + // add child AABB nodes + for (size_t objectIdx = 0; objectIdx < objectCount; objectIdx++) { - CollisionAabbTree* currAabbTree = &clipMap->aabbTrees[rootAABB->u.firstChildIndex + i]; - int currPartitionIndex = node->leaf->getObject(i)->partitionIndex; - - currAabbTree->materialIndex = 0; - currAabbTree->childCount = 0; - currAabbTree->u.partitionIndex = currPartitionIndex; - - // calculate partition origin and half size - CollisionPartition* aabbPartition = &clipMap->partitions[currPartitionIndex]; - uint16_t firstUind = clipMap->info.uinds[aabbPartition->fuind]; - vec3_t* firstVertex = &clipMap->verts[firstUind]; - vec3_t mins; - vec3_t maxs; - mins.x = firstVertex->x; - mins.y = firstVertex->y; - mins.z = firstVertex->z; - maxs.x = firstVertex->x; - maxs.y = firstVertex->y; - maxs.z = firstVertex->z; - for (int i = 1; i < aabbPartition->nuinds; i++) + int partitionIndex = node->leaf->getObject(objectIdx)->partitionIndex; + CollisionPartition* partition = &clipMap->partitions[partitionIndex]; + vec3_t childMins; + vec3_t childMaxs; + for (int uindIdx = 0; uindIdx < partition->nuinds; uindIdx++) { - uint16_t currUind = clipMap->info.uinds[aabbPartition->fuind + i]; - vec3_t* currVertex = &clipMap->verts[currUind]; + uint16_t uind = clipMap->info.uinds[partition->fuind + uindIdx]; + vec3_t vert = clipMap->verts[uind]; - BSPUtil::updateAABBWithpoint(currVertex, &mins, &maxs); + // initalise the child AABB with the first vertex + if (uindIdx == 0) + { + childMins = vert; + childMaxs = vert; + } + + BSPUtil::updateAABBWithPoint(vert, childMins, childMaxs); } - aabbCalcOriginAndHalfSize(&mins, &maxs, &currAabbTree->origin, &currAabbTree->halfSize); + CollisionAabbTree childAABBTree; + childAABBTree.materialIndex = 0; // always use the first material + childAABBTree.childCount = 0; + childAABBTree.u.partitionIndex = partitionIndex; + childAABBTree.origin = BSPUtil::calcMiddleOfAABB(childMins, childMaxs); + childAABBTree.halfSize = BSPUtil::calcHalfSizeOfAABB(childMins, childMaxs); + AABBTreeVec.emplace_back(childAABBTree); } - return firstAABBIndex; + return static_cast(parentAABBIndex); } - // returns the index corresponding to the BSPTree* node parsed - int16_t populateBSPTree_r(clipMap_t* clipMap, BSPTree* node) - { - if (node->isLeaf) - { - int currLeafIndex = currLeafCount; - currLeafCount++; - cLeaf_s* currLeaf = &clipMap->leafs[currLeafIndex]; + constexpr vec3_t normalX = { 1.0f, 0.0f, 0.0f }; + constexpr vec3_t normalY = { 0.0f, 1.0f, 0.0f }; + constexpr vec3_t normalZ = { 0.0f, 0.0f, 1.0f }; - currLeaf->cluster = 0; - currLeaf->brushContents = 0; // no brushes used so contents is 0 - currLeaf->terrainContents = BSPEditableConstants::LEAF_TERRAIN_CONTENTS; // clipMap->cmodels[0].leaf.terrainContents takes prescedence + // returns the index of the node/leaf parsed by the function + // Nodes are indexed by their index in the node array + // Leafs are indexed by (-1 - ) + // See https://developer.valvesoftware.com/wiki/BSP_(Source) + int16_t ClipMapLinker::loadBSPNode(clipMap_t* clipMap, BSPTree* tree) + { + if (tree->isLeaf) + { + cLeaf_s leaf; + + leaf.cluster = 0; // always use cluster 0 + leaf.brushContents = 0; // no brushes used so contents is 0 + leaf.terrainContents = BSPEditableConstants::LEAF_TERRAIN_CONTENTS; // unused when leafBrushNode == 0 - currLeaf->mins.x = 0.0f; - currLeaf->mins.y = 0.0f; - currLeaf->mins.z = 0.0f; - currLeaf->maxs.x = 0.0f; - currLeaf->maxs.y = 0.0f; - currLeaf->maxs.z = 0.0f; - currLeaf->leafBrushNode = 0; + leaf.mins.x = 0.0f; + leaf.mins.y = 0.0f; + leaf.mins.z = 0.0f; + leaf.maxs.x = 0.0f; + leaf.maxs.y = 0.0f; + leaf.maxs.z = 0.0f; + leaf.leafBrushNode = 0; - if (node->leaf->getObjectCount() > 0) + if (tree->leaf->getObjectCount() > 0) { - currLeaf->firstCollAabbIndex = addAABBTreeFromLeaf(node, clipMap); - currLeaf->collAabbCount = 1; + leaf.firstCollAabbIndex = addAABBTreeFromLeaf(tree, clipMap); + leaf.collAabbCount = 1; // 1 as it is the parent of all object AABBs } else { - currLeaf->firstCollAabbIndex = 0; - currLeaf->collAabbCount = 0; + leaf.firstCollAabbIndex = 0; + leaf.collAabbCount = 0; } - return -1 - currLeafIndex; + uint16_t leafIndex = static_cast(leafVec.size()); + leafVec.emplace_back(leaf); + + return -1 - leafIndex; } else { - cplane_s* currPlane = &clipMap->info.planes[currPlaneCount]; - currPlaneCount++; + cplane_s plane; - if (node->node->axis == AXIS_X) + if (tree->node->axis == AXIS_X) { - // X is unchanged when going from OGL x -> BO2 x - currPlane->normal = normalX; - - // converting OGL -> BO2 X coords doesn't change the x coords at all, so - // the dist stays the same - currPlane->dist = (float)node->node->distance; + plane.normal = normalX; + plane.dist = tree->node->distance; } else { - // converting OGL -> BO2 Z coords negates the z coords and sets it to the y coord. - // convert the z normal to the y normal, but don't negate it. Negative normals don't do - // what is expected when the game uses them - assert(node->node->axis == AXIS_Z); - currPlane->normal = normalY; - - // converting OGL -> BO2 Z coords negates the z coords and sets it to the y coord. - // just negate it here as it is just the distance from the orgin along the axis - currPlane->dist = (float)(-node->node->distance); + // converting Z coords to BO2 negates the z coords and sets it to the y coord. + assert(tree->node->axis == AXIS_Z); + plane.normal = normalY; + plane.dist = -tree->node->distance; } - bool foundType = false; - if (currPlane->normal.x == 1.0f) - { - assert(!foundType); - foundType = true; - currPlane->type = 0; - } - else if (currPlane->normal.y == 1.0f) - { - assert(!foundType); - foundType = true; - currPlane->type = 1; - } - else if (currPlane->normal.z == 1.0f) - { - assert(!foundType); - foundType = true; - currPlane->type = 2; - } + if (plane.normal.x == 1.0f) + plane.type = 0; + else if (plane.normal.y == 1.0f) + plane.type = 1; else - assert(foundType); - - currPlane->signbits = 0; - if (currPlane->normal.x < 0.0f) - currPlane->signbits |= 1; - if (currPlane->normal.y < 0.0f) - currPlane->signbits |= 2; - if (currPlane->normal.z < 0.0f) - currPlane->signbits |= 4; - - currPlane->pad[0] = 0; - currPlane->pad[1] = 0; - - int currNodeIndex = currNodeCount; - currNodeCount++; - cNode_t* currNode = &clipMap->nodes[currNodeIndex]; - - currNode->plane = currPlane; - // Reason for the front and back flip (due to the hacky nature of making the mins and maxs work (see createClipMap)): - // after converting between OGL and BO2 coords and when and updating the normal from Z -> Y, - // the normal vector flips and objects behind the plane are now in front, and vise versa - // so the back node now represents the front, and the front node represents the back. - // Do the OGL -> Bo2 coord change on paper and it will make sense - if (currPlane->type == 1) { - currNode->children[1] = populateBSPTree_r(clipMap, node->node->front.get()); - currNode->children[0] = populateBSPTree_r(clipMap, node->node->back.get()); + assert(plane.normal.z == 1.0f); + plane.type = 2; + } + + plane.signbits = 0; + if (plane.normal.x < 0.0f) + plane.signbits |= 1; + if (plane.normal.y < 0.0f) + plane.signbits |= 2; + if (plane.normal.z < 0.0f) + plane.signbits |= 4; + + plane.pad[0] = 0; + plane.pad[1] = 0; + + planeVec.emplace_back(plane); + + // The recursion of adding the children means the node needs to be added before the chilren are loaded + size_t nodeIndex = nodeVec.size(); + nodeVec.emplace_back(); + + cNode_t node; + node.plane = nullptr; // initalised after the BSP tree has been loaded + if (plane.type == 1) + { + // Due to the plane normal going from Z to Y, objects behind the plane are now in front and objects in front are now behind + node.children[1] = loadBSPNode(clipMap, tree->node->front.get()); + node.children[0] = loadBSPNode(clipMap, tree->node->back.get()); } else { - currNode->children[0] = populateBSPTree_r(clipMap, node->node->front.get()); - currNode->children[1] = populateBSPTree_r(clipMap, node->node->back.get()); + node.children[0] = loadBSPNode(clipMap, tree->node->front.get()); + node.children[1] = loadBSPNode(clipMap, tree->node->back.get()); } - return currNodeIndex; + nodeVec.at(nodeIndex) = node; + + return static_cast(nodeIndex); } } - void ClipMapLinker::populateBSPTree(clipMap_t* clipMap, BSPTree* tree) + void ClipMapLinker::loadBSPTree(clipMap_t* clipMap, BSPData* bsp) { - size_t numPlanes = 0; - size_t numNodes = 0; - size_t numLeafs = 0; - size_t numAABBTrees = 0; - size_t maxObjsPerLeaf = 0; + // HACK: + // the BSP tree creation does not work when BO2's coordinate system is used for mins and maxs. + // Workaround is to convert every BO2 coordinate before it is added into the BSP tree. - traverseBSPTreeForCounts(tree, &numPlanes, &numNodes, &numLeafs, &numAABBTrees, &maxObjsPerLeaf); - - printf("Max Objects per leaf: %i\n", maxObjsPerLeaf); - - clipMap->info.planeCount = numPlanes; - clipMap->info.planes = m_memory.Alloc(clipMap->info.planeCount); - clipMap->numNodes = numNodes; - clipMap->nodes = m_memory.Alloc(clipMap->numNodes); - // aabb trees: each leaf will have their own AABB tree of the objects within it, and the root aabb node will be the parent of every other aabb node. - // therefore, each aabb tree will be of size (numObjects + 1) as the tree needs a root aabb node to reference it's children. - clipMap->aabbTreeCount = numAABBTrees; - clipMap->aabbTrees = m_memory.Alloc(clipMap->aabbTreeCount); - - currPlaneCount = 0; - currNodeCount = 0; - currAABBCount = 0; - - // first leaf is always empty - clipMap->numLeafs = numLeafs + 1; - clipMap->leafs = m_memory.Alloc(clipMap->numLeafs); - memset(&clipMap->leafs[0], 0, sizeof(cLeaf_s)); - currLeafCount = 1; - - populateBSPTree_r(clipMap, tree); - - assert(clipMap->info.planeCount == currPlaneCount); - assert(clipMap->numNodes == currNodeCount); - assert(clipMap->numLeafs == currLeafCount); - assert(clipMap->aabbTreeCount == currAABBCount); - } - - bool ClipMapLinker::createPartitions(clipMap_t* clipMap, BSPData* bsp) - { - int collisionVertexCount = bsp->colWorld.vertices.size(); - std::vector collisionVertVec; - for (int i = 0; i < collisionVertexCount; i++) + vec3_t worldMins; + vec3_t worldMaxs; + for (unsigned int vertIdx = 0; vertIdx < clipMap->vertCount; vertIdx++) { - collisionVertVec.push_back(BSPUtil::convertToBO2Coords(bsp->colWorld.vertices[i].pos)); - //collisionVertVec.push_back(bsp->colWorld.vertices[i].pos); + vec3_t vertex = BSPUtil::convertFromBO2Coords(clipMap->verts[vertIdx]); + // initalise AABB with the first vertex + if (vertIdx == 0) + { + worldMins = vertex; + worldMaxs = vertex; + } + BSPUtil::updateAABBWithPoint(vertex, worldMins, worldMaxs); } - clipMap->vertCount = collisionVertexCount; - clipMap->verts = m_memory.Alloc(collisionVertexCount); - memcpy(clipMap->verts, &collisionVertVec[0], sizeof(vec3_t) * collisionVertexCount); + std::unique_ptr tree = std::make_unique(worldMins.x, worldMins.y, worldMins.z, worldMaxs.x, worldMaxs.y, worldMaxs.z, 0); + assert(!tree->isLeaf); + for (int partitionIdx = 0; partitionIdx < clipMap->partitionCount; partitionIdx++) + { + vec3_t partitionMins; + vec3_t partitionMaxs; + CollisionPartition* partition = &clipMap->partitions[partitionIdx]; + for (int uindIdx = 0; uindIdx < partition->nuinds; uindIdx++) + { + uint16_t uind = clipMap->info.uinds[partition->fuind + uindIdx]; + vec3_t vert = BSPUtil::convertFromBO2Coords(clipMap->verts[uind]); + // initalise the AABB with the first vertex + if (uindIdx == 0) + { + partitionMins = vert; + partitionMaxs = vert; + } + BSPUtil::updateAABBWithPoint(vert, partitionMins, partitionMaxs); + } + std::shared_ptr currObject = std::make_shared(partitionMins.x, partitionMins.y, partitionMins.z, partitionMaxs.x, partitionMaxs.y, partitionMaxs.z, partitionIdx); + tree->addObjectToTree(std::move(currObject)); + } + + // load planes, nodes, leafs, and AABB trees + loadBSPNode(clipMap, tree.get()); + + clipMap->info.planeCount = static_cast(planeVec.size()); + clipMap->info.planes = m_memory.Alloc(planeVec.size()); + memcpy(clipMap->info.planes, planeVec.data(), sizeof(cplane_s) * planeVec.size()); + + clipMap->numNodes = static_cast(nodeVec.size()); + clipMap->nodes = m_memory.Alloc(nodeVec.size()); + memcpy(clipMap->nodes, nodeVec.data(), sizeof(cNode_t) * nodeVec.size()); + + clipMap->numLeafs = static_cast(leafVec.size()); + clipMap->leafs = m_memory.Alloc(leafVec.size()); + memcpy(clipMap->leafs, leafVec.data(), sizeof(cLeaf_s) * leafVec.size()); + + clipMap->aabbTreeCount = static_cast(AABBTreeVec.size()); + clipMap->aabbTrees = m_memory.Alloc(AABBTreeVec.size()); + memcpy(clipMap->aabbTrees, AABBTreeVec.data(), sizeof(CollisionAabbTree) * AABBTreeVec.size()); + + // The plane of each node have the same index + for (size_t nodeIdx = 0; nodeIdx < nodeVec.size(); nodeIdx++) + clipMap->nodes[nodeIdx].plane = &clipMap->info.planes[nodeIdx]; + } + + bool ClipMapLinker::loadPartitions(clipMap_t* clipMap, BSPData* bsp) + { // due to tris using uint16_t as the type for indexing the vert array, - // any vertex count over the uint16_t max means the vertices above it can't be indexed - if (collisionVertexCount > BSPGameConstants::MAX_COLLISION_VERTS) + // any vertex count over the uint16_t max means the vertices above the uint16_t max can't be indexed + if (static_cast(bsp->colWorld.vertices.size()) > BSPGameConstants::MAX_COLLISION_VERTS) { - printf("ERROR: collision vertex count %i exceeds the maximum number: %i!\n", collisionVertexCount, BSPGameConstants::MAX_COLLISION_VERTS); + printf("ERROR: collision vertex count %i exceeds the maximum number: %i!\n", clipMap->vertCount, BSPGameConstants::MAX_COLLISION_VERTS); return false; } + clipMap->vertCount = static_cast(bsp->colWorld.vertices.size()); + clipMap->verts = m_memory.Alloc(clipMap->vertCount); + for (unsigned int vertIdx = 0; vertIdx < clipMap->vertCount; vertIdx++) + clipMap->verts[vertIdx] = BSPUtil::convertToBO2Coords(bsp->colWorld.vertices[vertIdx].pos); + + // The clipmap index buffer has a unique index for each vertex in the world, compared to the gfxworld's + // index buffer having a unique index for each vertex on a surface. This code converts gfxworld indices to clipmap indices. std::vector triIndexVec; - for (size_t i = 0; i < bsp->colWorld.surfaces.size(); i++) + for (BSPSurface& surface : bsp->colWorld.surfaces) { - BSPSurface* currSurface = &bsp->colWorld.surfaces[i]; - int triCount = currSurface->triCount; - - for (int k = 0; k < triCount * 3; k += 3) + int indexOfFirstIndex = surface.indexOfFirstIndex; + int indexOfFirstVertex = surface.indexOfFirstVertex; + for (int indexIdx = 0; indexIdx < surface.triCount * 3; indexIdx += 3) { - int firstIndex_Index = currSurface->indexOfFirstIndex; - int firstVertexIndex = currSurface->indexOfFirstVertex; - - // gfx index bufer starts at 0 for each new mesh, while the clipmap index buffer indexes the entire - // clipmap verts buffer, so this code updates the indexes to follow that. - int triIndex0 = bsp->colWorld.indices[firstIndex_Index + (k + 0)] + firstVertexIndex; - int triIndex1 = bsp->colWorld.indices[firstIndex_Index + (k + 1)] + firstVertexIndex; - int triIndex2 = bsp->colWorld.indices[firstIndex_Index + (k + 2)] + firstVertexIndex; + int firstTriIndex = indexOfFirstIndex + indexIdx; + int triIndex0 = bsp->colWorld.indices[firstTriIndex + 0] + indexOfFirstVertex; + int triIndex1 = bsp->colWorld.indices[firstTriIndex + 1] + indexOfFirstVertex; + int triIndex2 = bsp->colWorld.indices[firstTriIndex + 2] + indexOfFirstVertex; // triangle index ordering is opposite to blenders, so its converted here - triIndexVec.push_back(triIndex2); - triIndexVec.push_back(triIndex1); - triIndexVec.push_back(triIndex0); + triIndexVec.emplace_back(triIndex2); + triIndexVec.emplace_back(triIndex1); + triIndexVec.emplace_back(triIndex0); } } - assert(triIndexVec.size() % 3 == 0); - - // the reinterpret_cast is used as triIndices is just a pointer to an array of indicies, - // and static_cast can't safely do the conversion - clipMap->triCount = triIndexVec.size() / 3; + // the reinterpret_cast is used as triIndices is just a pointer to an array of indicies, and static_cast can't safely do the conversion + clipMap->triCount = static_cast(triIndexVec.size() / 3); clipMap->triIndices = reinterpret_cast(m_memory.Alloc(triIndexVec.size())); memcpy(clipMap->triIndices, &triIndexVec[0], sizeof(uint16_t) * triIndexVec.size()); - // partitions are made for each triangle, not one for each surface. - // one for each surface causes physics bugs, as the entire bounding box is considered solid instead of the surface itself (for some reason). - // so a partition is made for each triangle which removes the physics bugs but likely makes the game run slower + // partitions are "containers" for vertices. BSP tree leafs contain a list of these partitions to determine the collision within a leaf. std::vector partitionVec; - for (size_t i = 0; i < bsp->colWorld.surfaces.size(); i++) + std::vector uniqueIndicesVec; + for (BSPSurface& surface : bsp->colWorld.surfaces) { - int triCount = bsp->colWorld.surfaces[i].triCount; - int firstTriIndex = bsp->colWorld.surfaces[i].indexOfFirstIndex / 3; - for (int k = 0; k < triCount; k++) + // partitions are made for each triangle, not one for each surface. + // one for each surface causes physics bugs, as the entire bounding box is considered solid instead of the surface itself (for some reason). + // so a partition is made for each triangle which removes the physics bugs but likely makes the game run slower + int indexOfFirstTri = surface.indexOfFirstIndex / 3; + int indexOfFirstVertex = surface.indexOfFirstVertex; + for (int triIdx = 0; triIdx < surface.triCount; triIdx++) { - CollisionPartition newPartition; - newPartition.nuinds = 0; // initialised later - newPartition.fuind = 0; // initialised later - newPartition.triCount = 1; - newPartition.firstTri = firstTriIndex; - firstTriIndex += 1; + CollisionPartition partition; + partition.triCount = 1; + partition.firstTri = indexOfFirstTri + triIdx; - partitionVec.push_back(newPartition); + partition.nuinds = 3; + partition.fuind = static_cast(uniqueIndicesVec.size()); + + // All tri indices are unique since there is only one tri per partition + uint16_t* tri = clipMap->triIndices[partition.firstTri]; + uniqueIndicesVec.emplace_back(tri[0]); + uniqueIndicesVec.emplace_back(tri[1]); + uniqueIndicesVec.emplace_back(tri[2]); + + partitionVec.emplace_back(partition); } } - clipMap->partitionCount = partitionVec.size(); + clipMap->partitionCount = static_cast(partitionVec.size()); clipMap->partitions = m_memory.Alloc(clipMap->partitionCount); - memcpy(clipMap->partitions, &partitionVec[0], sizeof(CollisionPartition) * clipMap->partitionCount); + memcpy(clipMap->partitions, partitionVec.data(), sizeof(CollisionPartition) * partitionVec.size()); + clipMap->info.nuinds = static_cast(uniqueIndicesVec.size()); + clipMap->info.uinds = m_memory.Alloc(uniqueIndicesVec.size()); + memcpy(clipMap->info.uinds, uniqueIndicesVec.data(), sizeof(uint16_t) * uniqueIndicesVec.size()); + + return true; + + /* + // Proper unique index creation code kept for future use int totalUindCount = 0; std::vector uindVec; for (int i = 0; i < clipMap->partitionCount; i++) @@ -606,7 +560,7 @@ namespace BSP } if (isVertexIndexUnique) - uniqueVertVec.push_back(vertIndex); + uniqueVertVec.emplace_back(vertIndex); } } @@ -618,11 +572,10 @@ namespace BSP clipMap->info.nuinds = totalUindCount; clipMap->info.uinds = m_memory.Alloc(totalUindCount); memcpy(clipMap->info.uinds, &uindVec[0], sizeof(uint16_t) * totalUindCount); - - return true; + */ } - bool ClipMapLinker::loadBrushCollision(clipMap_t* clipMap, BSPData* bsp) + bool ClipMapLinker::loadWorldCollision(clipMap_t* clipMap, BSPData* bsp) { // No support for brushes, only tris right now clipMap->info.numBrushSides = 0; @@ -638,70 +591,11 @@ namespace BSP clipMap->info.brushBounds = nullptr; clipMap->info.brushContents = nullptr; - // clipmap BSP creation must go last as it depends on unids, tris and verts already being populated - // HACK: - // the BSP tree creation does not work when BO2's coordinate system is used for mins and maxs. - // Workaround is to convert every BO2 coordinate to OGL's before it is added into the BSP tree, - // and then convert them back when it is being parsed into the clipmap. Requires some hacky - // logic, check populateBSPTree_r and addAABBTreeFromLeaf - - if (!createPartitions(clipMap, bsp)) + // load verts, tris, uinds and partitions + if (!loadPartitions(clipMap, bsp)) return false; - vec3_t* firstVert = &clipMap->verts[0]; - vec3_t clipMins; - vec3_t clipMaxs; - clipMins.x = firstVert->x; - clipMins.y = firstVert->y; - clipMins.z = firstVert->z; - clipMaxs.x = firstVert->x; - clipMaxs.y = firstVert->y; - clipMaxs.z = firstVert->z; - clipMins = BSPUtil::convertFromBO2Coords(clipMins); - clipMaxs = BSPUtil::convertFromBO2Coords(clipMaxs); - for (unsigned int i = 1; i < clipMap->vertCount; i++) - { - vec3_t vertCoord = BSPUtil::convertFromBO2Coords(clipMap->verts[i]); - BSPUtil::updateAABBWithpoint(&vertCoord, &clipMins, &clipMaxs); - } - - BSPTree* tree = new BSPTree(clipMins.x, clipMins.y, clipMins.z, clipMaxs.x, clipMaxs.y, clipMaxs.z, 0); - - assert(!tree->isLeaf); - - for (int i = 0; i < clipMap->partitionCount; i++) - { - auto currPartition = &clipMap->partitions[i]; - - uint16_t* firstTri = clipMap->triIndices[currPartition->firstTri]; - vec3_t* firstVert = &clipMap->verts[firstTri[0]]; - vec3_t mins; - vec3_t maxs; - mins.x = firstVert->x; - mins.y = firstVert->y; - mins.z = firstVert->z; - maxs.x = firstVert->x; - maxs.y = firstVert->y; - maxs.z = firstVert->z; - mins = BSPUtil::convertFromBO2Coords(mins); - maxs = BSPUtil::convertFromBO2Coords(maxs); - for (int k = 0; k < currPartition->triCount; k++) - { - uint16_t* tri = clipMap->triIndices[currPartition->firstTri + k]; - for (int l = 0; l < 3; l++) - { - uint16_t vertIndex = tri[l]; - vec3_t vertCoord = BSPUtil::convertFromBO2Coords(clipMap->verts[vertIndex]); - BSPUtil::updateAABBWithpoint(&vertCoord, &mins, &maxs); - } - } - - std::shared_ptr currObject = std::make_shared(mins.x, mins.y, mins.z, maxs.x, maxs.y, maxs.z, i); - - tree->addObjectToTree(std::move(currObject)); - } - - populateBSPTree(clipMap, tree); + loadBSPTree(clipMap, bsp); return true; } @@ -746,7 +640,7 @@ namespace BSP clipMap->triEdgeIsWalkable = new char[walkableEdgeSize]; memset(clipMap->triEdgeIsWalkable, 1, walkableEdgeSize * sizeof(char)); - if (!loadBrushCollision(clipMap, bsp)) + if (!loadWorldCollision(clipMap, bsp)) return AssetCreationResult::Failure(); m_context.AddAsset(clipMap->name, clipMap); diff --git a/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.h b/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.h index 9a08ebd7..ec2a4766 100644 --- a/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.h +++ b/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.h @@ -26,8 +26,16 @@ namespace BSP void loadSubModelCollision(clipMap_t* clipMap, BSPData* bsp); void loadXModelCollision(clipMap_t* clipMap); - bool loadBrushCollision(clipMap_t* clipMap, BSPData* bsp); + std::vector planeVec; + std::vector nodeVec; + std::vector leafVec; + std::vector AABBTreeVec; + int addAABBTreeFromLeaf(BSPTree* node, clipMap_t* clipMap); + int16_t loadBSPNode(clipMap_t* clipMap, BSPTree* tree); + + void loadBSPTree(clipMap_t* clipMap, BSPData* bsp); + bool loadWorldCollision(clipMap_t* clipMap, BSPData* bsp); void populateBSPTree(clipMap_t* clipMap, BSPTree* tree); - bool createPartitions(clipMap_t* clipMap, BSPData* bsp); + bool loadPartitions(clipMap_t* clipMap, BSPData* bsp); }; } \ No newline at end of file diff --git a/src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.cpp b/src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.cpp index 570ecb53..d16a8eee 100644 --- a/src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.cpp +++ b/src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.cpp @@ -130,7 +130,7 @@ namespace BSP for (int k = 0; k < currSurface->tris.triCount * 3; k++) { uint16_t vertIndex = gfxWorld->draw.indices[currSurface->tris.baseIndex + k]; - BSPUtil::updateAABBWithpoint(&firstVert[vertIndex].xyz, &currSurface->bounds[0], &currSurface->bounds[1]); + BSPUtil::updateAABBWithPoint(firstVert[vertIndex].xyz, currSurface->bounds[0], currSurface->bounds[1]); } // unused values @@ -547,7 +547,7 @@ namespace BSP for (int i = 0; i < gfxWorld->surfaceCount; i++) { - BSPUtil::updateAABB(&gfxWorld->dpvs.surfaces[i].bounds[0], &gfxWorld->dpvs.surfaces[i].bounds[1], &gfxWorld->mins, &gfxWorld->maxs); + BSPUtil::updateAABB(gfxWorld->dpvs.surfaces[i].bounds[0], gfxWorld->dpvs.surfaces[i].bounds[1], gfxWorld->mins, gfxWorld->maxs); } } diff --git a/src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.cpp b/src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.cpp index 6849a3ed..00459a56 100644 --- a/src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.cpp +++ b/src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.cpp @@ -79,7 +79,8 @@ namespace BSP try { json entJs; - std::string entityFilePath = BSPUtil::getFileNameForBSPAsset("entities.json"); + std::string entityFileName = "entities.json"; + std::string entityFilePath = BSPUtil::getFileNameForBSPAsset(entityFileName); const auto entFile = m_search_path.Open(entityFilePath); if (!entFile.IsOpen()) { @@ -95,7 +96,8 @@ namespace BSP return AssetCreationResult::Failure(); json spawnJs; - std::string spawnFilePath = BSPUtil::getFileNameForBSPAsset("spawns.json"); + std::string spawnFileName = "spawns.json"; + std::string spawnFilePath = BSPUtil::getFileNameForBSPAsset(spawnFileName); const auto spawnFile = m_search_path.Open(spawnFilePath); if (!spawnFile.IsOpen()) { diff --git a/src/ObjLoading/Game/T6/BSP/LoaderBSP_T6.cpp b/src/ObjLoading/Game/T6/BSP/LoaderBSP_T6.cpp index 74bac61c..630897a0 100644 --- a/src/ObjLoading/Game/T6/BSP/LoaderBSP_T6.cpp +++ b/src/ObjLoading/Game/T6/BSP/LoaderBSP_T6.cpp @@ -21,7 +21,8 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { // custom maps must have a map_gfx file - auto mapGfxFile = m_search_path.Open(BSPUtil::getFileNameForBSPAsset("map_gfx.fbx")); + std::string mapGfxFileName = "map_gfx.fbx"; + auto mapGfxFile = m_search_path.Open(BSPUtil::getFileNameForBSPAsset(mapGfxFileName)); if (!mapGfxFile.IsOpen()) return AssetCreationResult::NoAction();