mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-06-06 08:42:35 +00:00
BSP creator now loads seperate graphics and collision data.
This commit is contained in:
@@ -81,8 +81,10 @@ namespace
|
|||||||
class BSPLoader
|
class BSPLoader
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
const Input& m_input;
|
|
||||||
BSPData* m_bsp;
|
BSPData* m_bsp;
|
||||||
|
BSPWorld* m_curr_bsp_world;
|
||||||
|
bool m_is_world_gfx;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Accessor>> m_accessors;
|
std::vector<std::unique_ptr<Accessor>> m_accessors;
|
||||||
std::vector<std::unique_ptr<BufferView>> m_buffer_views;
|
std::vector<std::unique_ptr<BufferView>> m_buffer_views;
|
||||||
std::vector<std::unique_ptr<Buffer>> m_buffers;
|
std::vector<std::unique_ptr<Buffer>> m_buffers;
|
||||||
@@ -250,8 +252,8 @@ namespace
|
|||||||
|
|
||||||
surface.vertexCount = static_cast<uint16_t>(vertexCount);
|
surface.vertexCount = static_cast<uint16_t>(vertexCount);
|
||||||
surface.triCount = static_cast<uint16_t>(faceCount);
|
surface.triCount = static_cast<uint16_t>(faceCount);
|
||||||
surface.indexOfFirstIndex = static_cast<int>(m_bsp->gfxWorld.indices.size());
|
surface.indexOfFirstIndex = static_cast<int>(m_curr_bsp_world->indices.size());
|
||||||
surface.indexOfFirstVertex = static_cast<int>(m_bsp->gfxWorld.vertices.size());
|
surface.indexOfFirstVertex = static_cast<int>(m_curr_bsp_world->vertices.size());
|
||||||
|
|
||||||
for (auto faceIndex = 0u; faceIndex < faceCount; faceIndex++)
|
for (auto faceIndex = 0u; faceIndex < faceCount; faceIndex++)
|
||||||
{
|
{
|
||||||
@@ -265,13 +267,13 @@ namespace
|
|||||||
throw GltfLoadException("Index number exceeded the UINT16_MAX");
|
throw GltfLoadException("Index number exceeded the UINT16_MAX");
|
||||||
|
|
||||||
RhcToLhcIndices(indices);
|
RhcToLhcIndices(indices);
|
||||||
m_bsp->gfxWorld.indices.emplace_back(static_cast<uint16_t>(indices[0]));
|
m_curr_bsp_world->indices.emplace_back(static_cast<uint16_t>(indices[0]));
|
||||||
m_bsp->gfxWorld.indices.emplace_back(static_cast<uint16_t>(indices[1]));
|
m_curr_bsp_world->indices.emplace_back(static_cast<uint16_t>(indices[1]));
|
||||||
m_bsp->gfxWorld.indices.emplace_back(static_cast<uint16_t>(indices[2]));
|
m_curr_bsp_world->indices.emplace_back(static_cast<uint16_t>(indices[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto vertexOffset = static_cast<unsigned>(m_bsp->gfxWorld.vertices.size());
|
const auto vertexOffset = static_cast<unsigned>(m_curr_bsp_world->vertices.size());
|
||||||
m_bsp->gfxWorld.vertices.reserve(vertexOffset + vertexCount);
|
m_curr_bsp_world->vertices.reserve(vertexOffset + vertexCount);
|
||||||
for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++)
|
for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++)
|
||||||
{
|
{
|
||||||
BSPVertex vertex;
|
BSPVertex vertex;
|
||||||
@@ -296,22 +298,22 @@ namespace
|
|||||||
RhcToLhcCoordinates(vertex.pos.v);
|
RhcToLhcCoordinates(vertex.pos.v);
|
||||||
RhcToLhcCoordinates(vertex.normal.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
|
// generate tangent and binormal vectors
|
||||||
tangent_space::VertexData vertexData{
|
tangent_space::VertexData vertexData{
|
||||||
&m_bsp->gfxWorld.vertices[surface.indexOfFirstVertex].pos,
|
&m_curr_bsp_world->vertices[surface.indexOfFirstVertex].pos,
|
||||||
sizeof(BSPVertex),
|
sizeof(BSPVertex),
|
||||||
&m_bsp->gfxWorld.vertices[surface.indexOfFirstVertex].normal,
|
&m_curr_bsp_world->vertices[surface.indexOfFirstVertex].normal,
|
||||||
sizeof(BSPVertex),
|
sizeof(BSPVertex),
|
||||||
&m_bsp->gfxWorld.vertices[surface.indexOfFirstVertex].texCoord,
|
&m_curr_bsp_world->vertices[surface.indexOfFirstVertex].texCoord,
|
||||||
sizeof(BSPVertex),
|
sizeof(BSPVertex),
|
||||||
&m_bsp->gfxWorld.vertices[surface.indexOfFirstVertex].tangent,
|
&m_curr_bsp_world->vertices[surface.indexOfFirstVertex].tangent,
|
||||||
sizeof(BSPVertex),
|
sizeof(BSPVertex),
|
||||||
&m_bsp->gfxWorld.vertices[surface.indexOfFirstVertex].binormal,
|
&m_curr_bsp_world->vertices[surface.indexOfFirstVertex].binormal,
|
||||||
sizeof(BSPVertex),
|
sizeof(BSPVertex),
|
||||||
&m_bsp->gfxWorld.indices[surface.indexOfFirstIndex],
|
&m_curr_bsp_world->indices[surface.indexOfFirstIndex],
|
||||||
};
|
};
|
||||||
tangent_space::CalculateTangentSpace(vertexData, faceCount, vertexCount);
|
tangent_space::CalculateTangentSpace(vertexData, faceCount, vertexCount);
|
||||||
|
|
||||||
@@ -322,7 +324,7 @@ namespace
|
|||||||
{
|
{
|
||||||
Eigen::Matrix4f nodeMatrix = createNodeMatrix(node);
|
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;
|
int lightIndex = node.extensions->KHR_lights_punctual->light;
|
||||||
|
|
||||||
@@ -375,11 +377,11 @@ namespace
|
|||||||
if (primitive.material)
|
if (primitive.material)
|
||||||
surface.materialIndex = *primitive.material;
|
surface.materialIndex = *primitive.material;
|
||||||
else
|
else
|
||||||
surface.materialIndex = m_bsp->gfxWorld.materials.size() - 1; // last material is used for colour only meshes
|
surface.materialIndex = m_curr_bsp_world->materials.size() - 1; // last material is used for colour only meshes
|
||||||
vec4_t vertexColour = m_bsp->gfxWorld.materials.at(surface.materialIndex).materialColour;
|
vec4_t vertexColour = m_curr_bsp_world->materials.at(surface.materialIndex).materialColour;
|
||||||
CreateVertices(accessorsForVertex, node, nodeMatrix, surface, vertexColour);
|
CreateVertices(accessorsForVertex, node, nodeMatrix, surface, vertexColour);
|
||||||
|
|
||||||
m_bsp->gfxWorld.surfaces.emplace_back(surface);
|
m_curr_bsp_world->surfaces.emplace_back(surface);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -427,7 +429,7 @@ namespace
|
|||||||
{
|
{
|
||||||
if (jRoot.materials)
|
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)
|
for (auto& jsMaterial : *jRoot.materials)
|
||||||
{
|
{
|
||||||
BSPMaterial material;
|
BSPMaterial material;
|
||||||
@@ -469,7 +471,7 @@ namespace
|
|||||||
material.materialColour.w = 1.0f;
|
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.y = 1.0f;
|
||||||
colorMaterial.materialColour.z = 1.0f;
|
colorMaterial.materialColour.z = 1.0f;
|
||||||
colorMaterial.materialColour.w = 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)
|
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)
|
if (!jRoot.buffers)
|
||||||
return;
|
return;
|
||||||
@@ -598,7 +600,7 @@ namespace
|
|||||||
{
|
{
|
||||||
const void* embeddedBufferPtr = nullptr;
|
const void* embeddedBufferPtr = nullptr;
|
||||||
size_t embeddedBufferSize = 0u;
|
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");
|
throw GltfLoadException("Buffer tried to access embedded data when there is none");
|
||||||
|
|
||||||
m_buffers.emplace_back(std::make_unique<EmbeddedBuffer>(embeddedBufferPtr, embeddedBufferSize));
|
m_buffers.emplace_back(std::make_unique<EmbeddedBuffer>(embeddedBufferPtr, embeddedBufferSize));
|
||||||
@@ -674,12 +676,12 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool addGLTFDataToBSP(bool isGfxWorld)
|
bool addGLTFDataToBSP(Input& gltfInput, bool isGfxWorld)
|
||||||
{
|
{
|
||||||
JsonRoot jRoot;
|
JsonRoot jRoot;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
jRoot = m_input.GetJson().get<JsonRoot>();
|
jRoot = gltfInput.GetJson().get<JsonRoot>();
|
||||||
}
|
}
|
||||||
catch (const nlohmann::json::exception& e)
|
catch (const nlohmann::json::exception& e)
|
||||||
{
|
{
|
||||||
@@ -689,11 +691,22 @@ namespace
|
|||||||
|
|
||||||
try
|
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);
|
CreateBufferViews(jRoot);
|
||||||
CreateAccessors(jRoot);
|
CreateAccessors(jRoot);
|
||||||
|
|
||||||
LoadLights(jRoot);
|
if (isGfxWorld) // lights aren't needed in collision data
|
||||||
|
LoadLights(jRoot);
|
||||||
|
|
||||||
LoadMaterials(jRoot);
|
LoadMaterials(jRoot);
|
||||||
TraverseNodes(jRoot); // requires materials and lights
|
TraverseNodes(jRoot); // requires materials and lights
|
||||||
}
|
}
|
||||||
@@ -706,9 +719,12 @@ namespace
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
BSPLoader(const Input& input, BSPData* bsp)
|
BSPLoader(BSPData* bsp)
|
||||||
: m_input(input),
|
: m_bsp(bsp)
|
||||||
m_bsp(bsp) {};
|
{
|
||||||
|
m_curr_bsp_world = nullptr;
|
||||||
|
m_is_world_gfx = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -716,7 +732,10 @@ namespace BSP
|
|||||||
{
|
{
|
||||||
std::unique_ptr<BSPData> createBSPData(std::string& mapName, ISearchPath& searchPath)
|
std::unique_ptr<BSPData> createBSPData(std::string& mapName, ISearchPath& searchPath)
|
||||||
{
|
{
|
||||||
|
bool seperateColFile = true;
|
||||||
bool isGfxFileGltf = true;
|
bool isGfxFileGltf = true;
|
||||||
|
bool isColFileGltf = true;
|
||||||
|
|
||||||
std::string gfxFilePath = BSPUtil::getFileNameForBSPAsset("map_gfx.gltf");
|
std::string gfxFilePath = BSPUtil::getFileNameForBSPAsset("map_gfx.gltf");
|
||||||
auto gfxFile = searchPath.Open(gfxFilePath);
|
auto gfxFile = searchPath.Open(gfxFilePath);
|
||||||
if (!gfxFile.IsOpen())
|
if (!gfxFile.IsOpen())
|
||||||
@@ -731,19 +750,31 @@ namespace BSP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<BSPData> bsp = std::make_unique<BSPData>();
|
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<BSPData> bsp = std::make_unique<BSPData>();
|
||||||
bsp->name = mapName;
|
bsp->name = mapName;
|
||||||
bsp->bspName = "maps/mp/" + mapName + ".d3dbsp";
|
bsp->bspName = "maps/mp/" + mapName + ".d3dbsp";
|
||||||
|
|
||||||
|
BSPLoader loader(bsp.get());
|
||||||
if (isGfxFileGltf)
|
if (isGfxFileGltf)
|
||||||
{
|
{
|
||||||
gltf::TextInput input;
|
gltf::TextInput input;
|
||||||
if (!input.ReadGltfData(*gfxFile.m_stream))
|
if (!input.ReadGltfData(*gfxFile.m_stream))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
if (!loader.addGLTFDataToBSP(input, true))
|
||||||
BSPLoader loader(input, bsp.get());
|
|
||||||
if (!loader.addGLTFDataToBSP(true))
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -751,291 +782,32 @@ namespace BSP
|
|||||||
gltf::BinInput input;
|
gltf::BinInput input;
|
||||||
if (!input.ReadGltfData(*gfxFile.m_stream))
|
if (!input.ReadGltfData(*gfxFile.m_stream))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
if (!loader.addGLTFDataToBSP(input, true))
|
||||||
BSPLoader loader(input, bsp.get());
|
|
||||||
if (!loader.addGLTFDataToBSP(true))
|
|
||||||
return nullptr;
|
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;
|
return bsp;
|
||||||
}
|
}
|
||||||
} // namespace BSP
|
} // namespace BSP
|
||||||
|
|
||||||
/*
|
|
||||||
std::unique_ptr<BSPData> 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<char> gfxMapData(new char[static_cast<size_t>(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<size_t>(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<char> colMapData(new char[static_cast<size_t>(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<size_t>(colFile.m_length), &optsCol, &errorCol);
|
|
||||||
if (!colScene)
|
|
||||||
{
|
|
||||||
con::error("Failed to load map collision fbx file: {}", errorCol.description.data);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<BSPData> bsp = std::make_unique<BSPData>();
|
|
||||||
|
|
||||||
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<BSPSurface>& surfaceVec, std::vector<BSPVertex>& vertexVec, std::vector<uint16_t>& 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<int>(partTriangleNum);
|
|
||||||
surface.indexOfFirstVertex = static_cast<int>(vertexVec.size());
|
|
||||||
surface.indexOfFirstIndex = static_cast<int>(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<BSPVertex> tempVertices;
|
|
||||||
std::vector<uint32_t> 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<float>(transformedPos.x);
|
|
||||||
blenderCoords.y = static_cast<float>(transformedPos.y);
|
|
||||||
blenderCoords.z = static_cast<float>(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<float>(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<float>(diffuse.x * factor);
|
|
||||||
vertex.color.y = static_cast<float>(diffuse.y * factor);
|
|
||||||
vertex.color.z = static_cast<float>(diffuse.z * factor);
|
|
||||||
vertex.color.w = static_cast<float>(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<float>(uv.x);
|
|
||||||
vertex.texCoord.y = static_cast<float>(1.0f - uv.y);
|
|
||||||
|
|
||||||
ufbx_vec3 normal = ufbx_get_vertex_vec3(&mesh->vertex_normal, index);
|
|
||||||
vertex.normal.x = static_cast<float>(normal.x);
|
|
||||||
vertex.normal.y = static_cast<float>(normal.y);
|
|
||||||
vertex.normal.z = static_cast<float>(normal.z);
|
|
||||||
|
|
||||||
if (mesh->vertex_tangent.exists)
|
|
||||||
{
|
|
||||||
ufbx_vec3 tangent = ufbx_get_vertex_vec3(&mesh->vertex_tangent, index);
|
|
||||||
vertex.tangent.x = static_cast<float>(tangent.x);
|
|
||||||
vertex.tangent.y = static_cast<float>(tangent.y);
|
|
||||||
vertex.tangent.z = static_cast<float>(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<uint32_t> 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<uint16_t>(outIndices[idx + 2]));
|
|
||||||
indexVec.emplace_back(static_cast<uint16_t>(outIndices[idx + 1]));
|
|
||||||
indexVec.emplace_back(static_cast<uint16_t>(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<int>(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.");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|||||||
Reference in New Issue
Block a user