diff --git a/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp b/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp index eb6e5c97..f35e5809 100644 --- a/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp +++ b/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp @@ -81,8 +81,10 @@ namespace class BSPLoader { private: - const Input& m_input; BSPData* m_bsp; + BSPWorld* m_curr_bsp_world; + bool m_is_world_gfx; + std::vector> m_accessors; std::vector> m_buffer_views; std::vector> m_buffers; @@ -250,8 +252,8 @@ namespace surface.vertexCount = static_cast(vertexCount); surface.triCount = static_cast(faceCount); - surface.indexOfFirstIndex = static_cast(m_bsp->gfxWorld.indices.size()); - surface.indexOfFirstVertex = static_cast(m_bsp->gfxWorld.vertices.size()); + surface.indexOfFirstIndex = static_cast(m_curr_bsp_world->indices.size()); + surface.indexOfFirstVertex = static_cast(m_curr_bsp_world->vertices.size()); for (auto faceIndex = 0u; faceIndex < faceCount; faceIndex++) { @@ -265,13 +267,13 @@ namespace throw GltfLoadException("Index number exceeded the UINT16_MAX"); RhcToLhcIndices(indices); - m_bsp->gfxWorld.indices.emplace_back(static_cast(indices[0])); - m_bsp->gfxWorld.indices.emplace_back(static_cast(indices[1])); - m_bsp->gfxWorld.indices.emplace_back(static_cast(indices[2])); + m_curr_bsp_world->indices.emplace_back(static_cast(indices[0])); + m_curr_bsp_world->indices.emplace_back(static_cast(indices[1])); + m_curr_bsp_world->indices.emplace_back(static_cast(indices[2])); } - const auto vertexOffset = static_cast(m_bsp->gfxWorld.vertices.size()); - m_bsp->gfxWorld.vertices.reserve(vertexOffset + vertexCount); + const auto vertexOffset = static_cast(m_curr_bsp_world->vertices.size()); + m_curr_bsp_world->vertices.reserve(vertexOffset + vertexCount); for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++) { BSPVertex vertex; @@ -296,22 +298,22 @@ namespace RhcToLhcCoordinates(vertex.pos.v); RhcToLhcCoordinates(vertex.normal.v); - m_bsp->gfxWorld.vertices.emplace_back(vertex); + m_curr_bsp_world->vertices.emplace_back(vertex); } // generate tangent and binormal vectors tangent_space::VertexData vertexData{ - &m_bsp->gfxWorld.vertices[surface.indexOfFirstVertex].pos, + &m_curr_bsp_world->vertices[surface.indexOfFirstVertex].pos, sizeof(BSPVertex), - &m_bsp->gfxWorld.vertices[surface.indexOfFirstVertex].normal, + &m_curr_bsp_world->vertices[surface.indexOfFirstVertex].normal, sizeof(BSPVertex), - &m_bsp->gfxWorld.vertices[surface.indexOfFirstVertex].texCoord, + &m_curr_bsp_world->vertices[surface.indexOfFirstVertex].texCoord, sizeof(BSPVertex), - &m_bsp->gfxWorld.vertices[surface.indexOfFirstVertex].tangent, + &m_curr_bsp_world->vertices[surface.indexOfFirstVertex].tangent, sizeof(BSPVertex), - &m_bsp->gfxWorld.vertices[surface.indexOfFirstVertex].binormal, + &m_curr_bsp_world->vertices[surface.indexOfFirstVertex].binormal, sizeof(BSPVertex), - &m_bsp->gfxWorld.indices[surface.indexOfFirstIndex], + &m_curr_bsp_world->indices[surface.indexOfFirstIndex], }; tangent_space::CalculateTangentSpace(vertexData, faceCount, vertexCount); @@ -322,7 +324,7 @@ namespace { Eigen::Matrix4f nodeMatrix = createNodeMatrix(node); - if (node.extensions && node.extensions->KHR_lights_punctual) + if (m_is_world_gfx && node.extensions && node.extensions->KHR_lights_punctual) { int lightIndex = node.extensions->KHR_lights_punctual->light; @@ -375,11 +377,11 @@ namespace if (primitive.material) surface.materialIndex = *primitive.material; else - surface.materialIndex = m_bsp->gfxWorld.materials.size() - 1; // last material is used for colour only meshes - vec4_t vertexColour = m_bsp->gfxWorld.materials.at(surface.materialIndex).materialColour; + surface.materialIndex = m_curr_bsp_world->materials.size() - 1; // last material is used for colour only meshes + vec4_t vertexColour = m_curr_bsp_world->materials.at(surface.materialIndex).materialColour; CreateVertices(accessorsForVertex, node, nodeMatrix, surface, vertexColour); - m_bsp->gfxWorld.surfaces.emplace_back(surface); + m_curr_bsp_world->surfaces.emplace_back(surface); return true; } } @@ -427,7 +429,7 @@ namespace { if (jRoot.materials) { - m_bsp->gfxWorld.materials.reserve((*jRoot.materials).size()); + m_curr_bsp_world->materials.reserve((*jRoot.materials).size()); for (auto& jsMaterial : *jRoot.materials) { BSPMaterial material; @@ -469,7 +471,7 @@ namespace material.materialColour.w = 1.0f; } - m_bsp->gfxWorld.materials.emplace_back(material); + m_curr_bsp_world->materials.emplace_back(material); } } @@ -481,7 +483,7 @@ namespace colorMaterial.materialColour.y = 1.0f; colorMaterial.materialColour.z = 1.0f; colorMaterial.materialColour.w = 1.0f; - m_bsp->gfxWorld.materials.emplace_back(colorMaterial); + m_curr_bsp_world->materials.emplace_back(colorMaterial); } void LoadLights(const JsonRoot& jRoot) @@ -586,7 +588,7 @@ namespace } } - void CreateBuffers(const JsonRoot& jRoot) + void CreateBuffers(const JsonRoot& jRoot, Input& gltfInput) { if (!jRoot.buffers) return; @@ -598,7 +600,7 @@ namespace { const void* embeddedBufferPtr = nullptr; size_t embeddedBufferSize = 0u; - if (!m_input.GetEmbeddedBuffer(embeddedBufferPtr, embeddedBufferSize) || embeddedBufferSize == 0u) + if (!gltfInput.GetEmbeddedBuffer(embeddedBufferPtr, embeddedBufferSize) || embeddedBufferSize == 0u) throw GltfLoadException("Buffer tried to access embedded data when there is none"); m_buffers.emplace_back(std::make_unique(embeddedBufferPtr, embeddedBufferSize)); @@ -674,12 +676,12 @@ namespace } public: - bool addGLTFDataToBSP(bool isGfxWorld) + bool addGLTFDataToBSP(Input& gltfInput, bool isGfxWorld) { JsonRoot jRoot; try { - jRoot = m_input.GetJson().get(); + jRoot = gltfInput.GetJson().get(); } catch (const nlohmann::json::exception& e) { @@ -689,11 +691,22 @@ namespace try { - CreateBuffers(jRoot); + m_is_world_gfx = isGfxWorld; + if (isGfxWorld) + m_curr_bsp_world = &m_bsp->gfxWorld; + else + m_curr_bsp_world = &m_bsp->colWorld; + m_accessors.clear(); + m_buffer_views.clear(); + m_buffers.clear(); + + CreateBuffers(jRoot, gltfInput); CreateBufferViews(jRoot); CreateAccessors(jRoot); - LoadLights(jRoot); + if (isGfxWorld) // lights aren't needed in collision data + LoadLights(jRoot); + LoadMaterials(jRoot); TraverseNodes(jRoot); // requires materials and lights } @@ -706,9 +719,12 @@ namespace return true; } - BSPLoader(const Input& input, BSPData* bsp) - : m_input(input), - m_bsp(bsp) {}; + BSPLoader(BSPData* bsp) + : m_bsp(bsp) + { + m_curr_bsp_world = nullptr; + m_is_world_gfx = false; + }; }; } // namespace @@ -716,7 +732,10 @@ namespace BSP { std::unique_ptr createBSPData(std::string& mapName, ISearchPath& searchPath) { + bool seperateColFile = true; bool isGfxFileGltf = true; + bool isColFileGltf = true; + std::string gfxFilePath = BSPUtil::getFileNameForBSPAsset("map_gfx.gltf"); auto gfxFile = searchPath.Open(gfxFilePath); if (!gfxFile.IsOpen()) @@ -731,19 +750,31 @@ namespace BSP } } - std::unique_ptr bsp = std::make_unique(); + std::string colFilePath = BSPUtil::getFileNameForBSPAsset("map_col.gltf"); + auto colFile = searchPath.Open(colFilePath); + if (!colFile.IsOpen()) + { + isColFileGltf = false; + colFilePath = BSPUtil::getFileNameForBSPAsset("map_col.glb"); + colFile = searchPath.Open(colFilePath); + if (!colFile.IsOpen()) + { + con::info("BSP Creator: generating colision data from GLTF graphics data."); + seperateColFile = false; + } + } + std::unique_ptr bsp = std::make_unique(); bsp->name = mapName; bsp->bspName = "maps/mp/" + mapName + ".d3dbsp"; + BSPLoader loader(bsp.get()); if (isGfxFileGltf) { gltf::TextInput input; if (!input.ReadGltfData(*gfxFile.m_stream)) return nullptr; - - BSPLoader loader(input, bsp.get()); - if (!loader.addGLTFDataToBSP(true)) + if (!loader.addGLTFDataToBSP(input, true)) return nullptr; } else @@ -751,291 +782,32 @@ namespace BSP gltf::BinInput input; if (!input.ReadGltfData(*gfxFile.m_stream)) return nullptr; - - BSPLoader loader(input, bsp.get()); - if (!loader.addGLTFDataToBSP(true)) + if (!loader.addGLTFDataToBSP(input, true)) return nullptr; } - bsp->colWorld = bsp->gfxWorld; + if (seperateColFile) + { + if (isColFileGltf) + { + gltf::TextInput input; + if (!input.ReadGltfData(*colFile.m_stream)) + return nullptr; + if (!loader.addGLTFDataToBSP(input, false)) + return nullptr; + } + else + { + gltf::BinInput input; + if (!input.ReadGltfData(*colFile.m_stream)) + return nullptr; + if (!loader.addGLTFDataToBSP(input, false)) + return nullptr; + } + } + else + bsp->colWorld = bsp->gfxWorld; return bsp; } } // namespace BSP - -/* -std::unique_ptr createBSPData(std::string& mapName, ISearchPath& searchPath) - { - std::string gfxFbxFileName = "map_gfx.fbx"; - std::string gfxFbxPath = BSPUtil::getFileNameForBSPAsset(gfxFbxFileName); - auto gfxFile = searchPath.Open(gfxFbxPath); - if (!gfxFile.IsOpen()) - { - con::error("Failed to open map gfx fbx file: {}", gfxFbxPath); - return nullptr; - } - - std::unique_ptr gfxMapData(new char[static_cast(gfxFile.m_length)]); - gfxFile.m_stream->read(gfxMapData.get(), gfxFile.m_length); - if (gfxFile.m_stream->gcount() != gfxFile.m_length) - { - con::error("Read error of gfx fbx file: {}", gfxFbxPath); - return nullptr; - } - - ufbx_error errorGfx; - ufbx_load_opts optsGfx{}; - optsGfx.target_axes = ufbx_axes_right_handed_y_up; - optsGfx.generate_missing_normals = true; - optsGfx.allow_missing_vertex_position = false; - ufbx_scene* gfxScene = ufbx_load_memory(gfxMapData.get(), static_cast(gfxFile.m_length), &optsGfx, &errorGfx); - if (!gfxScene) - { - con::error("Failed to load map gfx fbx file: {}", errorGfx.description.data); - return nullptr; - } - - ufbx_scene* colScene; - std::string colFbxFileName = "map_col.fbx"; - std::string colFbxPath = BSPUtil::getFileNameForBSPAsset(colFbxFileName); - auto colFile = searchPath.Open(colFbxPath); - if (!colFile.IsOpen()) - { - con::warn("Failed to open map collison fbx file: {}. map gfx will be used for collision instead.", colFbxPath); - colScene = gfxScene; - } - else - { - std::unique_ptr colMapData(new char[static_cast(colFile.m_length)]); - colFile.m_stream->seekg(0); - colFile.m_stream->read(colMapData.get(), colFile.m_length); - if (colFile.m_stream->gcount() != colFile.m_length) - { - con::error("Read error of collision fbx file: {}", colFbxPath); - return nullptr; - } - - ufbx_error errorCol; - ufbx_load_opts optsCol{}; - optsCol.target_axes = ufbx_axes_right_handed_y_up; - optsCol.generate_missing_normals = true; - optsCol.allow_missing_vertex_position = false; - colScene = ufbx_load_memory(colMapData.get(), static_cast(colFile.m_length), &optsCol, &errorCol); - if (!colScene) - { - con::error("Failed to load map collision fbx file: {}", errorCol.description.data); - return nullptr; - } - } - - std::unique_ptr bsp = std::make_unique(); - - bsp->name = mapName; - bsp->bspName = "maps/mp/" + mapName + ".d3dbsp"; - - loadWorldData(gfxScene, bsp.get(), true); - loadWorldData(colScene, bsp.get(), false); - - ufbx_free_scene(gfxScene); - if (gfxScene != colScene) - ufbx_free_scene(colScene); - - return bsp; - } - - - - - - - - - - - - using namespace BSP; - - void addFBXMeshToWorld( - ufbx_node* node, std::vector& surfaceVec, std::vector& vertexVec, std::vector& indexVec, bool& hasTangentSpace) - { - ufbx_mesh* mesh = node->mesh; - - assert(node->attrib_type == UFBX_ELEMENT_MESH); - - if (mesh->instances.count != 1) - con::warn("mesh {} has {} instances, only the 1st instace will be used.", node->name.data, mesh->instances.count); - - if (mesh->num_triangles == 0) - { - con::warn("ignoring mesh {}: triangle count is 0.", node->name.data); - return; - } - - if (mesh->num_indices % 3 != 0) - { - con::warn("ignoring mesh {}: it is not triangulated.", node->name.data); - return; - } - - for (size_t k = 0; k < mesh->num_indices; k++) - { - if (mesh->vertex_indices[k] > UINT16_MAX) - { - con::warn("ignoring mesh {}, it has more than {} indices.", node->name.data, UINT16_MAX); - return; - } - } - - if (mesh->vertex_tangent.exists == false) - hasTangentSpace = false; - - // Fix the target_unit_meters ufbx opt not working - // UFBX stores the transform data in units that are 100x larger than what blender uses, so this converts them back - ufbx_transform origTransform = node->local_transform; - origTransform.translation.x /= 100.0f; - origTransform.translation.y /= 100.0f; - origTransform.translation.z /= 100.0f; - origTransform.scale.x /= 100.0f; - origTransform.scale.y /= 100.0f; - origTransform.scale.z /= 100.0f; - ufbx_matrix meshMatrix = ufbx_transform_to_matrix(&origTransform); - - for (ufbx_mesh_part& meshPart : mesh->material_parts) - { - if (meshPart.num_faces == 0) - continue; - - BSPSurface surface; - size_t partTriangleNum = meshPart.num_triangles; - surface.triCount = static_cast(partTriangleNum); - surface.indexOfFirstVertex = static_cast(vertexVec.size()); - surface.indexOfFirstIndex = static_cast(indexVec.size()); - - if (mesh->materials.count == 0) - { - surface.material.materialType = MATERIAL_TYPE_EMPTY; - surface.material.materialName = ""; - } - else - { - surface.material.materialType = MATERIAL_TYPE_TEXTURE; - surface.material.materialName = mesh->materials.data[meshPart.index]->name.data; - } - - std::vector tempVertices; - std::vector tempIndices; - tempIndices.resize(mesh->max_face_triangles * 3); - for (uint32_t faceIndex : meshPart.face_indices) - { - ufbx_face* face = &mesh->faces.data[faceIndex]; - - // Triangulate the face into the indices vector - uint32_t triangluatedTriCount = ufbx_triangulate_face(tempIndices.data(), tempIndices.size(), mesh, *face); - - for (uint32_t idxOfIndex = 0; idxOfIndex < triangluatedTriCount * 3; idxOfIndex++) - { - BSPVertex vertex; - uint32_t index = tempIndices[idxOfIndex]; - - ufbx_vec3 transformedPos = ufbx_transform_position(&meshMatrix, ufbx_get_vertex_vec3(&mesh->vertex_position, index)); - vec3_t blenderCoords; - blenderCoords.x = static_cast(transformedPos.x); - blenderCoords.y = static_cast(transformedPos.y); - blenderCoords.z = static_cast(transformedPos.z); - vertex.pos = BSPUtil::convertToBO2Coords(blenderCoords); - - if (surface.material.materialType == MATERIAL_TYPE_TEXTURE || surface.material.materialType == MATERIAL_TYPE_EMPTY) - { - vertex.color.x = 1.0f; - vertex.color.y = 1.0f; - vertex.color.z = 1.0f; - vertex.color.w = 1.0f; - } - else // surface->material.materialType == MATERIAL_TYPE_COLOUR - { - float factor = static_cast(mesh->materials.data[meshPart.index]->fbx.diffuse_factor.value_real); - ufbx_vec4 diffuse = mesh->materials.data[meshPart.index]->fbx.diffuse_color.value_vec4; - vertex.color.x = static_cast(diffuse.x * factor); - vertex.color.y = static_cast(diffuse.y * factor); - vertex.color.z = static_cast(diffuse.z * factor); - vertex.color.w = static_cast(diffuse.w * factor); - } - - // 1.0f - uv.y reason: https://gamedev.stackexchange.com/questions/92886/fbx-uv-coordinates-is-strange - ufbx_vec2 uv = ufbx_get_vertex_vec2(&mesh->vertex_uv, index); - vertex.texCoord.x = static_cast(uv.x); - vertex.texCoord.y = static_cast(1.0f - uv.y); - - ufbx_vec3 normal = ufbx_get_vertex_vec3(&mesh->vertex_normal, index); - vertex.normal.x = static_cast(normal.x); - vertex.normal.y = static_cast(normal.y); - vertex.normal.z = static_cast(normal.z); - - if (mesh->vertex_tangent.exists) - { - ufbx_vec3 tangent = ufbx_get_vertex_vec3(&mesh->vertex_tangent, index); - vertex.tangent.x = static_cast(tangent.x); - vertex.tangent.y = static_cast(tangent.y); - vertex.tangent.z = static_cast(tangent.z); - } - else - { - vertex.tangent.x = 0.0f; - vertex.tangent.y = 0.0f; - vertex.tangent.z = 0.0f; - } - - tempVertices.emplace_back(vertex); - } - } - - // Generate the index buffer. - // ufbx_generate_indices will deduplicate vertices, modifying the arrays passed in streams, - // indices are written to outIndices and the number of unique vertices is returned. - ufbx_vertex_stream streams[1] = { - {tempVertices.data(), tempVertices.size(), sizeof(BSPVertex)}, - }; - std::vector outIndices; - outIndices.resize(partTriangleNum * 3); - size_t numGeneratedVertices = ufbx_generate_indices(streams, 1, outIndices.data(), outIndices.size(), nullptr, nullptr); - assert(numGeneratedVertices != 0); - - // trim non-unique vertexes and add to the world vertex vector - tempVertices.resize(numGeneratedVertices); - vertexVec.insert(vertexVec.end(), tempVertices.begin(), tempVertices.end()); - - // T6 uses unsigned shorts as their index type so we have to loop and convert them from an unsigned int - for (size_t idx = 0; idx < outIndices.size(); idx += 3) - { - // BO2's index ordering is opposite to the FBX, so its converted here - indexVec.emplace_back(static_cast(outIndices[idx + 2])); - indexVec.emplace_back(static_cast(outIndices[idx + 1])); - indexVec.emplace_back(static_cast(outIndices[idx + 0])); - } - - surfaceVec.emplace_back(surface); - } - } - - void loadWorldData(ufbx_scene* scene, BSPData* bsp, bool isGfxData) - { - bool hasTangentSpace = true; - for (ufbx_node* node : scene->nodes) - { - if (node->attrib_type == UFBX_ELEMENT_MESH) - { - if (isGfxData) - addFBXMeshToWorld(node, bsp->gfxWorld.surfaces, bsp->gfxWorld.vertices, bsp->gfxWorld.indices, hasTangentSpace); - else - addFBXMeshToWorld(node, bsp->colWorld.surfaces, bsp->colWorld.vertices, bsp->colWorld.indices, hasTangentSpace); - } - else - { - con::debug("ignoring node type {}: {}", static_cast(node->attrib_type), node->name.data); - } - } - - if (hasTangentSpace == false) - con::warn("warning: one or more meshes have no tangent space. Be sure to select the tangent space box when exporting the FBX."); - } -*/