mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-19 07:42:54 +00:00
631 lines
25 KiB
C++
631 lines
25 KiB
C++
#include "GltfWriter.h"
|
|
|
|
#include "GitVersion.h"
|
|
#include "XModel/Gltf/GltfConstants.h"
|
|
#include "XModel/Gltf/JsonGltf.h"
|
|
|
|
#pragma warning(push, 0)
|
|
#include <Eigen>
|
|
#pragma warning(pop)
|
|
|
|
#include <format>
|
|
|
|
using namespace gltf;
|
|
using namespace nlohmann;
|
|
|
|
namespace
|
|
{
|
|
constexpr auto GLTF_GENERATOR = "OpenAssetTools " GIT_VERSION;
|
|
|
|
struct GltfVertex
|
|
{
|
|
float coordinates[3];
|
|
float normal[3];
|
|
float uv[2];
|
|
};
|
|
|
|
class GltfWriterImpl final : public gltf::Writer
|
|
{
|
|
public:
|
|
GltfWriterImpl(const Output* output, std::string gameName, std::string zoneName)
|
|
: m_output(output),
|
|
m_game_name(std::move(gameName)),
|
|
m_zone_name(std::move(zoneName))
|
|
{
|
|
}
|
|
|
|
void Write(const XModelCommon& xmodel) override
|
|
{
|
|
JsonRoot gltf;
|
|
std::vector<uint8_t> bufferData;
|
|
|
|
CreateJsonAsset(gltf.asset);
|
|
CreateSkeletonNodes(gltf, xmodel);
|
|
CreateMeshNode(gltf, xmodel);
|
|
CreateRootNode(gltf, xmodel);
|
|
CreateMaterials(gltf, xmodel);
|
|
CreateBufferViews(gltf, xmodel);
|
|
CreateAccessors(gltf, xmodel);
|
|
CreateSkin(gltf, xmodel);
|
|
CreateMesh(gltf, xmodel);
|
|
CreateScene(gltf, xmodel);
|
|
FillBufferData(gltf, xmodel, bufferData);
|
|
CreateBuffer(gltf, xmodel, bufferData);
|
|
|
|
const json jRoot = gltf;
|
|
m_output->EmitJson(jRoot);
|
|
|
|
if (!bufferData.empty())
|
|
m_output->EmitBuffer(bufferData.data(), bufferData.size());
|
|
|
|
m_output->Finalize();
|
|
}
|
|
|
|
private:
|
|
static void CreateJsonAsset(JsonAsset& asset)
|
|
{
|
|
asset.version = GLTF_VERSION_STRING;
|
|
asset.generator = GLTF_GENERATOR;
|
|
}
|
|
|
|
void CreateMeshNode(JsonRoot& gltf, const XModelCommon& xmodel)
|
|
{
|
|
JsonNode meshNode;
|
|
|
|
if (!xmodel.m_name.empty())
|
|
meshNode.name = xmodel.m_name;
|
|
|
|
// We only have one mesh
|
|
meshNode.mesh = 0u;
|
|
|
|
// Only add skin if the model has bones
|
|
if (!xmodel.m_bones.empty())
|
|
{
|
|
// We only have one skin
|
|
meshNode.skin = 0u;
|
|
}
|
|
|
|
if (!gltf.nodes.has_value())
|
|
gltf.nodes.emplace();
|
|
|
|
m_mesh_node = gltf.nodes->size();
|
|
gltf.nodes->emplace_back(std::move(meshNode));
|
|
}
|
|
|
|
void CreateRootNode(JsonRoot& gltf, const XModelCommon& xmodel)
|
|
{
|
|
JsonNode rootNode;
|
|
|
|
if (!xmodel.m_name.empty())
|
|
rootNode.name = std::format("{}_skel", xmodel.m_name);
|
|
|
|
if (!gltf.nodes.has_value())
|
|
gltf.nodes.emplace();
|
|
|
|
rootNode.children.emplace();
|
|
rootNode.children->push_back(m_mesh_node);
|
|
|
|
if (!xmodel.m_bones.empty())
|
|
rootNode.children->push_back(m_first_bone_node);
|
|
|
|
m_root_node = gltf.nodes->size();
|
|
gltf.nodes->emplace_back(std::move(rootNode));
|
|
}
|
|
|
|
void CreateMesh(JsonRoot& gltf, const XModelCommon& xmodel)
|
|
{
|
|
if (!gltf.meshes.has_value())
|
|
gltf.meshes.emplace();
|
|
|
|
JsonMesh mesh;
|
|
|
|
const auto hasBoneWeightData = !xmodel.m_vertex_bone_weights.empty();
|
|
|
|
auto objectIndex = 0u;
|
|
for (const auto& object : xmodel.m_objects)
|
|
{
|
|
JsonMeshPrimitives primitives;
|
|
|
|
if (object.materialIndex >= 0)
|
|
primitives.material = static_cast<unsigned>(object.materialIndex);
|
|
|
|
primitives.attributes.POSITION = m_position_accessor;
|
|
primitives.attributes.NORMAL = m_normal_accessor;
|
|
primitives.attributes.TEXCOORD_0 = m_uv_accessor;
|
|
|
|
if (hasBoneWeightData)
|
|
{
|
|
primitives.attributes.JOINTS_0 = m_joints_accessor;
|
|
primitives.attributes.WEIGHTS_0 = m_weights_accessor;
|
|
}
|
|
|
|
primitives.mode = JsonMeshPrimitivesMode::TRIANGLES;
|
|
primitives.indices = m_first_index_accessor + objectIndex;
|
|
|
|
mesh.primitives.emplace_back(primitives);
|
|
objectIndex++;
|
|
}
|
|
|
|
gltf.meshes->emplace_back(std::move(mesh));
|
|
}
|
|
|
|
static void CreateMaterials(JsonRoot& gltf, const XModelCommon& xmodel)
|
|
{
|
|
if (!gltf.materials.has_value())
|
|
gltf.materials.emplace();
|
|
|
|
for (const auto& modelMaterial : xmodel.m_materials)
|
|
{
|
|
JsonMaterial material;
|
|
|
|
material.name = modelMaterial.name;
|
|
|
|
if (!modelMaterial.colorMapName.empty())
|
|
{
|
|
material.pbrMetallicRoughness.emplace();
|
|
material.pbrMetallicRoughness->baseColorTexture.emplace();
|
|
|
|
material.pbrMetallicRoughness->baseColorTexture->index = CreateTexture(gltf, modelMaterial.colorMapName);
|
|
material.pbrMetallicRoughness->metallicFactor = 0.0f;
|
|
}
|
|
|
|
if (!modelMaterial.normalMapName.empty())
|
|
material.normalTexture.emplace().index = CreateTexture(gltf, modelMaterial.normalMapName);
|
|
|
|
material.doubleSided = true;
|
|
gltf.materials->emplace_back(material);
|
|
}
|
|
}
|
|
|
|
static unsigned CreateTexture(JsonRoot& gltf, const std::string& textureName)
|
|
{
|
|
if (!gltf.textures.has_value())
|
|
gltf.textures.emplace();
|
|
if (!gltf.images.has_value())
|
|
gltf.images.emplace();
|
|
|
|
auto uri = std::format("../images/{}.dds", textureName);
|
|
|
|
auto existingTexIndex = 0u;
|
|
for (const auto& existingTex : gltf.textures.value())
|
|
{
|
|
const auto& existingImage = gltf.images.value()[existingTex.source];
|
|
|
|
if (existingImage.uri == uri)
|
|
return existingTexIndex;
|
|
|
|
existingTexIndex++;
|
|
}
|
|
|
|
JsonImage image;
|
|
image.uri = std::move(uri);
|
|
const auto imageIndex = gltf.images->size();
|
|
gltf.images->emplace_back(std::move(image));
|
|
|
|
JsonTexture texture;
|
|
texture.source = imageIndex;
|
|
const auto textureIndex = gltf.textures->size();
|
|
gltf.textures->emplace_back(texture);
|
|
|
|
return textureIndex;
|
|
}
|
|
|
|
void CreateSkeletonNodes(JsonRoot& gltf, const XModelCommon& xmodel)
|
|
{
|
|
if (xmodel.m_bones.empty())
|
|
return;
|
|
|
|
if (!gltf.nodes.has_value())
|
|
gltf.nodes.emplace();
|
|
|
|
const auto boneCount = xmodel.m_bones.size();
|
|
m_first_bone_node = gltf.nodes->size();
|
|
for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++)
|
|
{
|
|
JsonNode boneNode;
|
|
const auto& bone = xmodel.m_bones[boneIndex];
|
|
|
|
Eigen::Vector3f translation(bone.globalOffset[0], bone.globalOffset[2], -bone.globalOffset[1]);
|
|
Eigen::Quaternionf rotation(bone.globalRotation.w, bone.globalRotation.x, bone.globalRotation.z, -bone.globalRotation.y);
|
|
if (bone.parentIndex >= 0)
|
|
{
|
|
const auto& parentBone = xmodel.m_bones[bone.parentIndex];
|
|
const auto inverseParentRotation =
|
|
Eigen::Quaternionf(parentBone.globalRotation.w, parentBone.globalRotation.x, parentBone.globalRotation.z, -parentBone.globalRotation.y)
|
|
.normalized()
|
|
.inverse()
|
|
.normalized();
|
|
|
|
translation -= Eigen::Vector3f(parentBone.globalOffset[0], parentBone.globalOffset[2], -parentBone.globalOffset[1]);
|
|
translation = inverseParentRotation * translation;
|
|
rotation = inverseParentRotation * rotation;
|
|
}
|
|
rotation.normalize();
|
|
|
|
boneNode.name = bone.name;
|
|
boneNode.translation = std::to_array({translation.x(), translation.y(), translation.z()});
|
|
boneNode.rotation = std::to_array({rotation.x(), rotation.y(), rotation.z(), rotation.w()});
|
|
|
|
std::vector<unsigned> children;
|
|
for (auto maybeChildIndex = 0u; maybeChildIndex < boneCount; maybeChildIndex++)
|
|
{
|
|
if (xmodel.m_bones[maybeChildIndex].parentIndex == static_cast<int>(boneIndex))
|
|
children.emplace_back(maybeChildIndex + m_first_bone_node);
|
|
}
|
|
if (!children.empty())
|
|
boneNode.children = std::move(children);
|
|
|
|
gltf.nodes->emplace_back(std::move(boneNode));
|
|
}
|
|
}
|
|
|
|
void CreateSkin(JsonRoot& gltf, const XModelCommon& xmodel) const
|
|
{
|
|
if (xmodel.m_bones.empty())
|
|
return;
|
|
|
|
if (!gltf.skins.has_value())
|
|
gltf.skins.emplace();
|
|
|
|
JsonSkin skin;
|
|
|
|
const auto boneCount = xmodel.m_bones.size();
|
|
|
|
skin.joints.reserve(boneCount);
|
|
for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++)
|
|
skin.joints.emplace_back(boneIndex + m_first_bone_node);
|
|
|
|
skin.inverseBindMatrices = m_inverse_bind_matrices_accessor;
|
|
skin.skeleton = m_first_bone_node;
|
|
|
|
gltf.skins->emplace_back(std::move(skin));
|
|
}
|
|
|
|
void CreateScene(JsonRoot& gltf, const XModelCommon& xmodel) const
|
|
{
|
|
JsonScene scene;
|
|
|
|
// Only add skin if the model has bones
|
|
scene.nodes.emplace_back(m_root_node);
|
|
|
|
if (!gltf.scenes.has_value())
|
|
gltf.scenes.emplace();
|
|
|
|
gltf.scenes->emplace_back(std::move(scene));
|
|
gltf.scene = 0u;
|
|
}
|
|
|
|
void CreateBufferViews(JsonRoot& gltf, const XModelCommon& xmodel)
|
|
{
|
|
if (!gltf.bufferViews.has_value())
|
|
gltf.bufferViews.emplace();
|
|
|
|
unsigned bufferOffset = 0u;
|
|
|
|
JsonBufferView vertexBufferView;
|
|
vertexBufferView.buffer = 0u;
|
|
vertexBufferView.byteOffset = bufferOffset;
|
|
vertexBufferView.byteStride = sizeof(GltfVertex);
|
|
vertexBufferView.byteLength = sizeof(GltfVertex) * xmodel.m_vertices.size();
|
|
vertexBufferView.target = JsonBufferViewTarget::ARRAY_BUFFER;
|
|
bufferOffset += vertexBufferView.byteLength;
|
|
|
|
m_vertex_buffer_view = gltf.bufferViews->size();
|
|
gltf.bufferViews->emplace_back(vertexBufferView);
|
|
|
|
if (!xmodel.m_vertex_bone_weights.empty())
|
|
{
|
|
JsonBufferView jointsBufferView;
|
|
jointsBufferView.buffer = 0u;
|
|
jointsBufferView.byteOffset = bufferOffset;
|
|
jointsBufferView.byteLength = sizeof(uint8_t) * xmodel.m_vertices.size() * 4u;
|
|
jointsBufferView.target = JsonBufferViewTarget::ARRAY_BUFFER;
|
|
bufferOffset += jointsBufferView.byteLength;
|
|
|
|
m_joints_buffer_view = gltf.bufferViews->size();
|
|
gltf.bufferViews->emplace_back(jointsBufferView);
|
|
|
|
JsonBufferView weightsBufferView;
|
|
weightsBufferView.buffer = 0u;
|
|
weightsBufferView.byteOffset = bufferOffset;
|
|
weightsBufferView.byteLength = sizeof(float) * xmodel.m_vertices.size() * 4u;
|
|
weightsBufferView.target = JsonBufferViewTarget::ARRAY_BUFFER;
|
|
bufferOffset += weightsBufferView.byteLength;
|
|
|
|
m_weights_buffer_view = gltf.bufferViews->size();
|
|
gltf.bufferViews->emplace_back(weightsBufferView);
|
|
|
|
JsonBufferView inverseBindMatricesBufferView;
|
|
inverseBindMatricesBufferView.buffer = 0u;
|
|
inverseBindMatricesBufferView.byteOffset = bufferOffset;
|
|
inverseBindMatricesBufferView.byteLength = sizeof(float) * xmodel.m_bones.size() * 16u;
|
|
bufferOffset += inverseBindMatricesBufferView.byteLength;
|
|
|
|
m_inverse_bind_matrices_buffer_view = gltf.bufferViews->size();
|
|
gltf.bufferViews->emplace_back(inverseBindMatricesBufferView);
|
|
}
|
|
|
|
m_first_index_buffer_view = gltf.bufferViews->size();
|
|
for (const auto& object : xmodel.m_objects)
|
|
{
|
|
JsonBufferView indicesBufferView;
|
|
indicesBufferView.buffer = 0u;
|
|
indicesBufferView.byteOffset = bufferOffset;
|
|
indicesBufferView.byteLength = sizeof(unsigned short) * object.m_faces.size() * 3u;
|
|
indicesBufferView.target = JsonBufferViewTarget::ELEMENT_ARRAY_BUFFER;
|
|
bufferOffset += indicesBufferView.byteLength;
|
|
|
|
gltf.bufferViews->emplace_back(indicesBufferView);
|
|
}
|
|
}
|
|
|
|
void CreateAccessors(JsonRoot& gltf, const XModelCommon& xmodel)
|
|
{
|
|
if (!gltf.accessors.has_value())
|
|
gltf.accessors.emplace();
|
|
|
|
JsonAccessor positionAccessor;
|
|
positionAccessor.bufferView = m_vertex_buffer_view;
|
|
positionAccessor.byteOffset = offsetof(GltfVertex, coordinates);
|
|
positionAccessor.componentType = JsonAccessorComponentType::FLOAT;
|
|
positionAccessor.count = xmodel.m_vertices.size();
|
|
positionAccessor.type = JsonAccessorType::VEC3;
|
|
m_position_accessor = gltf.accessors->size();
|
|
gltf.accessors->emplace_back(positionAccessor);
|
|
|
|
JsonAccessor normalAccessor;
|
|
normalAccessor.bufferView = m_vertex_buffer_view;
|
|
normalAccessor.byteOffset = offsetof(GltfVertex, normal);
|
|
normalAccessor.componentType = JsonAccessorComponentType::FLOAT;
|
|
normalAccessor.count = xmodel.m_vertices.size();
|
|
normalAccessor.type = JsonAccessorType::VEC3;
|
|
m_normal_accessor = gltf.accessors->size();
|
|
gltf.accessors->emplace_back(normalAccessor);
|
|
|
|
JsonAccessor uvAccessor;
|
|
uvAccessor.bufferView = m_vertex_buffer_view;
|
|
uvAccessor.byteOffset = offsetof(GltfVertex, uv);
|
|
uvAccessor.componentType = JsonAccessorComponentType::FLOAT;
|
|
uvAccessor.count = xmodel.m_vertices.size();
|
|
uvAccessor.type = JsonAccessorType::VEC2;
|
|
m_uv_accessor = gltf.accessors->size();
|
|
gltf.accessors->emplace_back(uvAccessor);
|
|
|
|
if (!xmodel.m_vertex_bone_weights.empty())
|
|
{
|
|
JsonAccessor jointsAccessor;
|
|
jointsAccessor.bufferView = m_joints_buffer_view;
|
|
jointsAccessor.componentType = JsonAccessorComponentType::UNSIGNED_BYTE;
|
|
jointsAccessor.count = xmodel.m_vertices.size();
|
|
jointsAccessor.type = JsonAccessorType::VEC4;
|
|
m_joints_accessor = gltf.accessors->size();
|
|
gltf.accessors->emplace_back(jointsAccessor);
|
|
|
|
JsonAccessor weightsAccessor;
|
|
weightsAccessor.bufferView = m_weights_buffer_view;
|
|
weightsAccessor.componentType = JsonAccessorComponentType::FLOAT;
|
|
weightsAccessor.count = xmodel.m_vertices.size();
|
|
weightsAccessor.type = JsonAccessorType::VEC4;
|
|
m_weights_accessor = gltf.accessors->size();
|
|
gltf.accessors->emplace_back(weightsAccessor);
|
|
|
|
JsonAccessor inverseBindMatricesAccessor;
|
|
inverseBindMatricesAccessor.bufferView = m_inverse_bind_matrices_buffer_view;
|
|
inverseBindMatricesAccessor.componentType = JsonAccessorComponentType::FLOAT;
|
|
inverseBindMatricesAccessor.count = xmodel.m_bones.size();
|
|
inverseBindMatricesAccessor.type = JsonAccessorType::MAT4;
|
|
m_inverse_bind_matrices_accessor = gltf.accessors->size();
|
|
gltf.accessors->emplace_back(inverseBindMatricesAccessor);
|
|
}
|
|
|
|
m_first_index_accessor = gltf.accessors->size();
|
|
for (auto i = 0u; i < xmodel.m_objects.size(); i++)
|
|
{
|
|
const auto& object = xmodel.m_objects[i];
|
|
|
|
JsonAccessor indicesAccessor;
|
|
indicesAccessor.bufferView = m_first_index_buffer_view + i;
|
|
indicesAccessor.componentType = JsonAccessorComponentType::UNSIGNED_SHORT;
|
|
indicesAccessor.count = object.m_faces.size() * 3u;
|
|
indicesAccessor.type = JsonAccessorType::SCALAR;
|
|
|
|
gltf.accessors->emplace_back(indicesAccessor);
|
|
}
|
|
}
|
|
|
|
void FillBufferData(JsonRoot& gltf, const XModelCommon& xmodel, std::vector<uint8_t>& bufferData) const
|
|
{
|
|
const auto expectedBufferSize = GetExpectedBufferSize(xmodel);
|
|
bufferData.resize(expectedBufferSize);
|
|
|
|
auto currentBufferOffset = 0u;
|
|
|
|
float minPosition[3]{
|
|
std::numeric_limits<float>::max(),
|
|
std::numeric_limits<float>::max(),
|
|
std::numeric_limits<float>::max(),
|
|
};
|
|
float maxPosition[3]{
|
|
-std::numeric_limits<float>::max(),
|
|
-std::numeric_limits<float>::max(),
|
|
-std::numeric_limits<float>::max(),
|
|
};
|
|
|
|
for (const auto& commonVertex : xmodel.m_vertices)
|
|
{
|
|
auto* vertex = reinterpret_cast<GltfVertex*>(&bufferData[currentBufferOffset]);
|
|
|
|
vertex->coordinates[0] = commonVertex.coordinates[0];
|
|
vertex->coordinates[1] = commonVertex.coordinates[2];
|
|
vertex->coordinates[2] = -commonVertex.coordinates[1];
|
|
|
|
if (minPosition[0] > vertex->coordinates[0])
|
|
minPosition[0] = vertex->coordinates[0];
|
|
if (minPosition[1] > vertex->coordinates[1])
|
|
minPosition[1] = vertex->coordinates[1];
|
|
if (minPosition[2] > vertex->coordinates[2])
|
|
minPosition[2] = vertex->coordinates[2];
|
|
if (maxPosition[0] < vertex->coordinates[0])
|
|
maxPosition[0] = vertex->coordinates[0];
|
|
if (maxPosition[1] < vertex->coordinates[1])
|
|
maxPosition[1] = vertex->coordinates[1];
|
|
if (maxPosition[2] < vertex->coordinates[2])
|
|
maxPosition[2] = vertex->coordinates[2];
|
|
|
|
vertex->normal[0] = commonVertex.normal[0];
|
|
vertex->normal[1] = commonVertex.normal[2];
|
|
vertex->normal[2] = -commonVertex.normal[1];
|
|
|
|
vertex->uv[0] = commonVertex.uv[0];
|
|
vertex->uv[1] = commonVertex.uv[1];
|
|
|
|
currentBufferOffset += sizeof(GltfVertex);
|
|
}
|
|
|
|
if (gltf.accessors.has_value())
|
|
{
|
|
gltf.accessors.value()[m_position_accessor].min = std::vector({minPosition[0], minPosition[1], minPosition[2]});
|
|
gltf.accessors.value()[m_position_accessor].max = std::vector({maxPosition[0], maxPosition[1], maxPosition[2]});
|
|
}
|
|
|
|
if (!xmodel.m_vertex_bone_weights.empty())
|
|
{
|
|
assert(xmodel.m_vertex_bone_weights.size() == xmodel.m_vertices.size());
|
|
|
|
auto* joints = reinterpret_cast<uint8_t*>(&bufferData[currentBufferOffset]);
|
|
auto* weights = reinterpret_cast<float*>(&bufferData[currentBufferOffset + sizeof(uint8_t) * 4u * xmodel.m_vertex_bone_weights.size()]);
|
|
for (const auto& commonVertexWeights : xmodel.m_vertex_bone_weights)
|
|
{
|
|
assert(commonVertexWeights.weights != nullptr);
|
|
assert(commonVertexWeights.weightCount <= 4u);
|
|
|
|
const auto commonVertexWeightCount = std::min(commonVertexWeights.weightCount, 4u);
|
|
for (auto i = 0u; i < commonVertexWeightCount; i++)
|
|
{
|
|
joints[i] = static_cast<unsigned char>(commonVertexWeights.weights[i].boneIndex);
|
|
weights[i] = commonVertexWeights.weights[i].weight;
|
|
}
|
|
|
|
for (auto i = commonVertexWeightCount; i < 4u; i++)
|
|
{
|
|
joints[i] = 0u;
|
|
weights[i] = 0.0f;
|
|
}
|
|
|
|
joints += 4u;
|
|
weights += 4u;
|
|
}
|
|
|
|
currentBufferOffset += (sizeof(uint8_t) + sizeof(float)) * 4u * xmodel.m_vertex_bone_weights.size();
|
|
|
|
auto* inverseBindMatrixData = reinterpret_cast<float*>(&bufferData[currentBufferOffset]);
|
|
for (const auto& bone : xmodel.m_bones)
|
|
{
|
|
const auto translation = Eigen::Translation3f(bone.globalOffset[0], bone.globalOffset[2], -bone.globalOffset[1]);
|
|
const auto rotation = Eigen::Quaternionf(bone.globalRotation.w, bone.globalRotation.x, bone.globalRotation.z, -bone.globalRotation.y);
|
|
|
|
const auto inverseBindMatrix = (translation * rotation).matrix().inverse();
|
|
|
|
// GLTF matrix is column major
|
|
inverseBindMatrixData[0] = inverseBindMatrix(0, 0);
|
|
inverseBindMatrixData[1] = inverseBindMatrix(1, 0);
|
|
inverseBindMatrixData[2] = inverseBindMatrix(2, 0);
|
|
inverseBindMatrixData[3] = inverseBindMatrix(3, 0);
|
|
inverseBindMatrixData[4] = inverseBindMatrix(0, 1);
|
|
inverseBindMatrixData[5] = inverseBindMatrix(1, 1);
|
|
inverseBindMatrixData[6] = inverseBindMatrix(2, 1);
|
|
inverseBindMatrixData[7] = inverseBindMatrix(3, 1);
|
|
inverseBindMatrixData[8] = inverseBindMatrix(0, 2);
|
|
inverseBindMatrixData[9] = inverseBindMatrix(1, 2);
|
|
inverseBindMatrixData[10] = inverseBindMatrix(2, 2);
|
|
inverseBindMatrixData[11] = inverseBindMatrix(3, 2);
|
|
inverseBindMatrixData[12] = inverseBindMatrix(0, 3);
|
|
inverseBindMatrixData[13] = inverseBindMatrix(1, 3);
|
|
inverseBindMatrixData[14] = inverseBindMatrix(2, 3);
|
|
inverseBindMatrixData[15] = inverseBindMatrix(3, 3);
|
|
|
|
inverseBindMatrixData += 16u;
|
|
}
|
|
currentBufferOffset += sizeof(float) * 16u * xmodel.m_bones.size();
|
|
}
|
|
|
|
for (const auto& object : xmodel.m_objects)
|
|
{
|
|
for (const auto& face : object.m_faces)
|
|
{
|
|
auto* faceIndices = reinterpret_cast<unsigned short*>(&bufferData[currentBufferOffset]);
|
|
faceIndices[0] = static_cast<unsigned short>(face.vertexIndex[2]);
|
|
faceIndices[1] = static_cast<unsigned short>(face.vertexIndex[1]);
|
|
faceIndices[2] = static_cast<unsigned short>(face.vertexIndex[0]);
|
|
|
|
currentBufferOffset += sizeof(unsigned short) * 3u;
|
|
}
|
|
}
|
|
|
|
assert(expectedBufferSize == currentBufferOffset);
|
|
}
|
|
|
|
static size_t GetExpectedBufferSize(const XModelCommon& xmodel)
|
|
{
|
|
auto result = 0u;
|
|
|
|
result += xmodel.m_vertices.size() * sizeof(GltfVertex);
|
|
|
|
if (!xmodel.m_vertex_bone_weights.empty())
|
|
{
|
|
// Joints and weights
|
|
result += xmodel.m_vertices.size() * 4u * (sizeof(uint8_t) + sizeof(float));
|
|
|
|
// Inverse bind matrices
|
|
result += xmodel.m_bones.size() * 16u * sizeof(float);
|
|
}
|
|
|
|
for (const auto& object : xmodel.m_objects)
|
|
{
|
|
result += object.m_faces.size() * sizeof(unsigned short) * 3u;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void CreateBuffer(JsonRoot& gltf, const XModelCommon& xmodel, const std::vector<uint8_t>& bufferData) const
|
|
{
|
|
if (!gltf.buffers.has_value())
|
|
gltf.buffers.emplace();
|
|
|
|
JsonBuffer jsonBuffer;
|
|
jsonBuffer.byteLength = bufferData.size();
|
|
|
|
if (!bufferData.empty())
|
|
jsonBuffer.uri = m_output->CreateBufferUri(bufferData.data(), bufferData.size());
|
|
|
|
gltf.buffers->emplace_back(std::move(jsonBuffer));
|
|
}
|
|
|
|
unsigned m_mesh_node = 0u;
|
|
unsigned m_root_node = 0u;
|
|
unsigned m_first_bone_node = 0u;
|
|
unsigned m_position_accessor = 0u;
|
|
unsigned m_normal_accessor = 0u;
|
|
unsigned m_uv_accessor = 0u;
|
|
unsigned m_joints_accessor = 0u;
|
|
unsigned m_weights_accessor = 0u;
|
|
unsigned m_inverse_bind_matrices_accessor = 0u;
|
|
unsigned m_vertex_buffer_view = 0u;
|
|
unsigned m_joints_buffer_view = 0u;
|
|
unsigned m_weights_buffer_view = 0u;
|
|
unsigned m_inverse_bind_matrices_buffer_view = 0u;
|
|
unsigned m_first_index_buffer_view = 0u;
|
|
unsigned m_first_index_accessor = 0u;
|
|
|
|
const Output* m_output;
|
|
std::string m_game_name;
|
|
std::string m_zone_name;
|
|
};
|
|
} // namespace
|
|
|
|
std::unique_ptr<Writer> Writer::CreateWriter(const Output* output, std::string gameName, std::string zoneName)
|
|
{
|
|
return std::make_unique<GltfWriterImpl>(output, std::move(gameName), std::move(zoneName));
|
|
}
|