diff --git a/src/ObjLoading/Game/T6/BSP/BSP.h b/src/ObjLoading/Game/T6/BSP/BSP.h index d67d4348..227c35a1 100644 --- a/src/ObjLoading/Game/T6/BSP/BSP.h +++ b/src/ObjLoading/Game/T6/BSP/BSP.h @@ -36,7 +36,7 @@ namespace BSP struct BSPSurface { - BSPMaterial material; + size_t materialIndex; uint16_t vertexCount; uint16_t triCount; int indexOfFirstVertex; @@ -48,6 +48,7 @@ namespace BSP std::vector surfaces; std::vector vertices; std::vector indices; + std::vector materials; }; struct BSPData diff --git a/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp b/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp index 46f48da3..54aa417b 100644 --- a/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp +++ b/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp @@ -83,6 +83,7 @@ namespace private: const Input& m_input; BSPData* m_bsp; + size_t m_color_mat_idx; std::vector> m_accessors; std::vector> m_buffer_views; std::vector> m_buffers; @@ -316,27 +317,15 @@ namespace void loadSurfaceMaterialData(const JsonRoot& jRoot, const JsonMeshPrimitives& primitive, BSPSurface& surface) { - BSPMaterialType matType; if (!primitive.material) - if (!primitive.attributes.COLOR_0) - { - // matType = MATERIAL_TYPE_EMPTY; - throw GltfLoadException("Primitive requires material or colour data."); - } - else - matType = MATERIAL_TYPE_COLOUR; - else - matType = MATERIAL_TYPE_TEXTURE; - surface.material.materialType = matType; - - if (matType == MATERIAL_TYPE_TEXTURE) { - if (!jRoot.materials || *primitive.material >= jRoot.materials->size()) - throw GltfLoadException("Invalid material index"); - surface.material.materialName = jRoot.materials.value()[primitive.material.value()].name.value(); + if (!primitive.attributes.COLOR_0) + throw GltfLoadException("Primitive requires material or colour data."); + + surface.materialIndex = m_color_mat_idx; } else - surface.material.materialName = ""; + surface.materialIndex = *primitive.material; } bool CreateSurfacesFromNode(const JsonRoot& jRoot, const gltf::JsonNode& node) @@ -414,6 +403,36 @@ namespace return rootNodes; } + void LoadMaterials(const JsonRoot& jRoot) + { + if (!jRoot.materials) + return; + m_bsp->gfxWorld.materials.reserve((*jRoot.materials).size()); + for (auto& jsMaterial : *jRoot.materials) + { + BSPMaterial material; + + if (jsMaterial.name && (*jsMaterial.name).length() != 0) + { + material.materialType = MATERIAL_TYPE_TEXTURE; + material.materialName = *jsMaterial.name; + } + else + { + material.materialType = MATERIAL_TYPE_EMPTY; + material.materialName = ""; + } + + m_bsp->gfxWorld.materials.emplace_back(material); + } + + m_color_mat_idx = m_bsp->gfxWorld.materials.size(); + BSPMaterial colorMaterial; + colorMaterial.materialType = MATERIAL_TYPE_COLOUR; + colorMaterial.materialName = ""; + m_bsp->gfxWorld.materials.emplace_back(colorMaterial); + } + void TraverseNodes(const JsonRoot& jRoot) { // Make sure there are any nodes to traverse @@ -551,6 +570,7 @@ namespace CreateBufferViews(jRoot); CreateAccessors(jRoot); + LoadMaterials(jRoot); TraverseNodes(jRoot); } catch (const GltfLoadException& e) diff --git a/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.cpp b/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.cpp index de120a2d..22d41530 100644 --- a/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.cpp +++ b/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.cpp @@ -220,6 +220,8 @@ namespace BSP if (remainder > 0) parentCount++; + // the material index of the AABB tree is only checked for the parent node, so + size_t parentAABBArrayIndex = AABBTreeVec.size(); AABBTreeVec.resize(AABBTreeVec.size() + parentCount); size_t unaddedObjectCount = leafObjectCount; @@ -232,7 +234,7 @@ namespace BSP else unaddedObjectCount -= BSPGameConstants::MAX_AABB_TREE_CHILDREN; - // add the parent AABB + // calculate parent AABB mins and maxs vec3_t parentMins; vec3_t parentMaxs; for (size_t objectIdx = 0; objectIdx < childObjectCount; objectIdx++) @@ -259,7 +261,7 @@ namespace BSP CollisionAabbTree parentAABB; parentAABB.origin = BSPUtil::calcMiddleOfAABB(parentMins, parentMaxs); parentAABB.halfSize = BSPUtil::calcHalfSizeOfAABB(parentMins, parentMaxs); - parentAABB.materialIndex = 0; // always use the first material + parentAABB.materialIndex = 0; parentAABB.childCount = static_cast(childObjectCount); parentAABB.u.firstChildIndex = static_cast(childObjectStartIndex); AABBTreeVec.at(parentAABBArrayIndex + parentIdx) = parentAABB; @@ -505,8 +507,10 @@ namespace BSP // partitions are "containers" for vertices. BSP tree leafs contain a list of these partitions to determine the collision within a leaf. std::vector partitionVec; std::vector uniqueIndicesVec; - for (BSPSurface& surface : bsp->colWorld.surfaces) + for (size_t surfIdx = 0; surfIdx < bsp->colWorld.surfaces.size(); surfIdx++) { + BSPSurface& surface = bsp->colWorld.surfaces[surfIdx]; + // 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 @@ -528,6 +532,8 @@ namespace BSP uniqueIndicesVec.emplace_back(tri[2]); partitionVec.emplace_back(partition); + + partitionToSurfaceMap.emplace_back(surfIdx); } } clipMap->partitionCount = static_cast(partitionVec.size()); @@ -607,6 +613,29 @@ namespace BSP return true; } + bool ClipMapLinker::loadMaterials(clipMap_t* clipMap, BSPData* bsp) + { + // Clipmap materials define the properties of a material (bullet penetration, no collision, water, etc) + + if (bsp->colWorld.materials.size() > UINT16_MAX) + { + con::error("Collision map exceeds 0xFFFF materials"); + return false; + } + + clipMap->info.numMaterials = static_cast(bsp->colWorld.materials.size()); + clipMap->info.materials = m_memory.Alloc(clipMap->info.numMaterials); + for (size_t matIdx = 0; matIdx < bsp->colWorld.materials.size(); matIdx++) + { + ClipMaterial* clipMat = &clipMap->info.materials[matIdx]; + BSPMaterial bspMat = bsp->colWorld.materials.at(matIdx); + + clipMat->name = m_memory.Dup(bspMat.materialName.c_str()); + clipMat->contentFlags = BSPEditableConstants::MATERIAL_CONTENT_FLAGS; + clipMat->surfaceFlags = BSPEditableConstants::MATERIAL_SURFACE_FLAGS; + } + } + clipMap_t* ClipMapLinker::linkClipMap(BSPData* bsp) { clipMap_t* clipMap = m_memory.Alloc(); @@ -631,13 +660,7 @@ namespace BSP loadXModelCollision(clipMap); - // Clipmap materials define the properties of a material (bullet penetration, no collision, water, etc) - // Right now there is no way to define properties per material so only one material is used - clipMap->info.numMaterials = 1; - clipMap->info.materials = m_memory.Alloc(clipMap->info.numMaterials); - clipMap->info.materials[0].name = m_memory.Dup(BSPLinkingConstants::MISSING_IMAGE_NAME); - clipMap->info.materials[0].contentFlags = BSPEditableConstants::MATERIAL_CONTENT_FLAGS; - clipMap->info.materials[0].surfaceFlags = BSPEditableConstants::MATERIAL_SURFACE_FLAGS; + loadMaterials(clipMap, bsp); if (!loadWorldCollision(clipMap, bsp)) return nullptr; diff --git a/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.h b/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.h index 459eac42..5597b7e4 100644 --- a/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.h +++ b/src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.h @@ -31,10 +31,13 @@ namespace BSP std::vector leafVec; std::vector AABBTreeVec; size_t highestLeafObjectCount = 0; + std::vector partitionToSurfaceMap; + std::vector surfaceToMaterialMap; void addAABBTreeFromLeaf(clipMap_t* clipMap, BSPTree* tree, size_t* out_parentCount, size_t* out_parentStartIndex); int16_t loadBSPNode(clipMap_t* clipMap, BSPTree* tree); bool loadBSPTree(clipMap_t* clipMap, BSPData* bsp); bool loadPartitions(clipMap_t* clipMap, BSPData* bsp); bool loadWorldCollision(clipMap_t* clipMap, BSPData* bsp); + bool loadMaterials(clipMap_t* clipMap, BSPData* bsp); }; } // namespace BSP diff --git a/src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.cpp b/src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.cpp index 510d84d3..e04a115c 100644 --- a/src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.cpp +++ b/src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.cpp @@ -73,22 +73,21 @@ namespace BSP gfxSurface->tris.vertexDataOffset0 = bspSurface.indexOfFirstVertex * sizeof(GfxPackedWorldVertex); gfxSurface->tris.vertexDataOffset1 = 0; // vd1 is unused - std::string surfMaterialName; - if (bspSurface.material.materialType == MATERIAL_TYPE_TEXTURE) - surfMaterialName = bspSurface.material.materialName; - else // MATERIAL_TYPE_COLOUR || MATERIAL_TYPE_EMPTY - surfMaterialName = BSPLinkingConstants::COLOR_ONLY_IMAGE_NAME; + BSPMaterial bspMaterial = bsp->colWorld.materials.at(bspSurface.materialIndex); - auto surfMaterialAsset = m_context.LoadDependency(surfMaterialName); + std::string materialName; + if (bspMaterial.materialType == MATERIAL_TYPE_EMPTY) + materialName = BSPLinkingConstants::MISSING_IMAGE_NAME; + else if (bspMaterial.materialType == MATERIAL_TYPE_COLOUR) + materialName = BSPLinkingConstants::COLOR_ONLY_IMAGE_NAME; + else // MATERIAL_TYPE_TEXTURE + materialName = bspMaterial.materialName; + + auto surfMaterialAsset = m_context.LoadDependency(materialName); if (surfMaterialAsset == nullptr) { - std::string missingImageName = BSPLinkingConstants::MISSING_IMAGE_NAME; - surfMaterialAsset = m_context.LoadDependency(missingImageName); - if (surfMaterialAsset == nullptr) - { - con::error("unable to load the missing image texture {}!", missingImageName); - return false; - } + surfMaterialAsset = m_context.LoadDependency(BSPLinkingConstants::MISSING_IMAGE_NAME); + assert(surfMaterialAsset != nullptr); } gfxSurface->material = surfMaterialAsset->Asset();