Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/BodyModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
#include <gfx/mdl/res/rio_ModelCacher.h>

class Model;
#ifndef NO_GLTF
class GLTFExportCallback;
#endif

struct BodyBone
{
Expand Down Expand Up @@ -248,6 +251,9 @@ class BodyModel
static rio::Vector3f calcBodyScale(f32 build, f32 height);
void draw(rio::Matrix34f& model_mtx, rio::BaseMtx34f& view_mtx,
rio::BaseMtx44f& proj_mtx);
#ifndef NO_GLTF
void exportToGLTF(GLTFExportCallback& exporter, const rio::Matrix34f& model_mtx);
#endif
private:
rio::mdl::Model* getBodyModel_();
void initializeSkeleton_();
Expand Down
21 changes: 21 additions & 0 deletions include/GLTFExportCallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <cstdint> // For fixed-width integer types
#include <iostream> // For std::ostream

#include <math/rio_Matrix.h>

// Include TinyGLTF
#include "for_gltf/tiny_gltf.h"

Expand Down Expand Up @@ -69,6 +71,17 @@ class GLTFExportCallback
return ExportModelInternal("", outStream);
}

void SetRootTransform(const rio::Matrix34f& mtx)
{
mRootTransform = mtx;
mUseRootTransform = true;
}

void ResetRootTransform()
{
mUseRootTransform = false;
}

// Data structures to collect the mesh data
struct MeshData
{
Expand All @@ -88,6 +101,11 @@ class GLTFExportCallback
FFLCullMode cullMode; ///< Culling mode
};

/**
* @brief Appends mesh data gathered outside the FFL callbacks.
*/
void AddMeshData(MeshData&& meshData);

private:
// Static callback functions matching FFLShaderCallback's function pointer types

Expand Down Expand Up @@ -349,4 +367,7 @@ class GLTFExportCallback

// Texture management
std::unordered_map<rio::Texture2D*, int> mTextureMap; ///< Maps Texture2D pointers to GLTF texture indices

rio::Matrix34f mRootTransform;
bool mUseRootTransform = false;
};
153 changes: 153 additions & 0 deletions src/BodyModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
#include <Types.h>
#include <Model.h>

#ifndef NO_GLTF
#include <GLTFExportCallback.h>
#include <gpu/rio_Drawer.h>
#include <gfx/mdl/res/rio_MeshData.h>

#include <algorithm>
#include <cmath>
#include <limits>
#include <utility>
#endif

#include <misc/rio_MemUtil.h>

//BodyModel::BodyModel(rio::mdl::Model* pBodyModel, BodyType type)
Expand Down Expand Up @@ -473,3 +484,145 @@ rio::Vector3f BodyModel::calcBodyScale(f32 build, f32 height)

return bodyScale;
}

#ifndef NO_GLTF
namespace
{
rio::Vector3f MultiplyPoint(const rio::Matrix34f& mtx, const rio::Vector3f& vec)
{
return {
mtx.m[0][0] * vec.x + mtx.m[0][1] * vec.y + mtx.m[0][2] * vec.z + mtx.m[0][3],
mtx.m[1][0] * vec.x + mtx.m[1][1] * vec.y + mtx.m[1][2] * vec.z + mtx.m[1][3],
mtx.m[2][0] * vec.x + mtx.m[2][1] * vec.y + mtx.m[2][2] * vec.z + mtx.m[2][3]
};
}

rio::Vector3f MultiplyVector(const rio::Matrix34f& mtx, const rio::Vector3f& vec)
{
return {
mtx.m[0][0] * vec.x + mtx.m[0][1] * vec.y + mtx.m[0][2] * vec.z,
mtx.m[1][0] * vec.x + mtx.m[1][1] * vec.y + mtx.m[1][2] * vec.z,
mtx.m[2][0] * vec.x + mtx.m[2][1] * vec.y + mtx.m[2][2] * vec.z
};
}
}

void BodyModel::exportToGLTF(GLTFExportCallback& exporter, const rio::Matrix34f& model_mtx)
{
if (mpBodyModel == nullptr)
return;

const rio::mdl::Model* bodyModel = getBodyModel_();
if (bodyModel == nullptr)
return;

// Build the base matrix used for rendering the body in the realtime path.
rio::Matrix34f modelMtxBody = rio::Matrix34f::ident;
if (mUseSkeleton)
modelMtxBody.applyScaleLocal(mScale);
else
modelMtxBody.applyScaleLocal(mBodyScale);
modelMtxBody.setMul(model_mtx, modelMtxBody);

FFLiCharInfo* pCharInfo = mpModel->getCharInfo();
const FFLColor bodyColor = FFLGetFavoriteColor(pCharInfo->favoriteColor);

const rio::mdl::Mesh* meshes = bodyModel->meshes();
for (u32 meshIndex = 0; meshIndex < bodyModel->numMeshes(); ++meshIndex)
{
const bool isPantsMesh = ((meshIndex % 2) == 1);
if (isPantsMesh && mPantsColor == PANTS_COLOR_NO_DRAW_PANTS)
continue;

const rio::mdl::Mesh& mesh = meshes[meshIndex];
const rio::mdl::res::Mesh& resMesh = mesh.resMesh();

const auto& vtxBuf = resMesh.vertexBuffer();
const auto& idxBuf = resMesh.indexBuffer();
if (vtxBuf.count() == 0 || idxBuf.count() == 0)
continue;

GLTFExportCallback::MeshData meshData;
meshData.modulateMode = FFL_MODULATE_MODE_CONSTANT;
meshData.modulateType = static_cast<FFLModulateType>(isPantsMesh ? CUSTOM_MATERIAL_PARAM_PANTS : CUSTOM_MATERIAL_PARAM_BODY);
meshData.texture = nullptr;
meshData.cullMode = FFL_CULL_MODE_BACK;
meshData.primitiveType = rio::Drawer::TRIANGLES;

FFLColor modulateColor = bodyColor;
if (isPantsMesh && mPantsColor != PANTS_COLOR_SAME_AS_BODY && mPantsColor < PANTS_COLOR_COUNT)
modulateColor = cPantsColors[mPantsColor];
meshData.colorR = { modulateColor.r, modulateColor.g, modulateColor.b, modulateColor.a };

meshData.indices.reserve(idxBuf.count());
const u32* indexPtr = idxBuf.ptr();
for (u32 i = 0; i < idxBuf.count(); ++i)
{
const u32 index = indexPtr[i];
RIO_ASSERT(index <= std::numeric_limits<uint16_t>::max());
meshData.indices.push_back(static_cast<uint16_t>(index));
}

const rio::mdl::res::Vertex* vertexPtr = vtxBuf.ptr();
const u32 vertexCount = vtxBuf.count();
meshData.positions.resize(vertexCount * 3);
meshData.normals.resize(vertexCount * 3);
if (!mUseSkeleton)
meshData.texcoords.resize(vertexCount * 2);

for (u32 v = 0; v < vertexCount; ++v)
{
const rio::mdl::res::Vertex& vertex = vertexPtr[v];

const rio::Vector3f position { vertex.pos.x, vertex.pos.y, vertex.pos.z };
const rio::Vector3f normal { vertex.normal.x, vertex.normal.y, vertex.normal.z };

rio::Vector3f transformedPosition;
rio::Vector3f transformedNormal;

if (mUseSkeleton && mpBodyModel->mBoneCount > 0)
{
s32 boneIndex = static_cast<s32>(std::round(vertex.tex_coord.x));
boneIndex = std::clamp(boneIndex, 0, mpBodyModel->mBoneCount - 1);

rio::Matrix34f combined;
combined.setMul(modelMtxBody, mSkeletonMatrix[boneIndex]);

transformedPosition = MultiplyPoint(combined, position);
transformedNormal = MultiplyVector(combined, normal);
}
else
{
transformedPosition = MultiplyPoint(modelMtxBody, position);
transformedNormal = MultiplyVector(modelMtxBody, normal);

if (!meshData.texcoords.empty())
{
meshData.texcoords[v * 2 + 0] = vertex.tex_coord.x;
meshData.texcoords[v * 2 + 1] = vertex.tex_coord.y;
}
}

const f32 length = std::sqrt(transformedNormal.x * transformedNormal.x +
transformedNormal.y * transformedNormal.y +
transformedNormal.z * transformedNormal.z);
if (length > 0.0f)
{
transformedNormal.x /= length;
transformedNormal.y /= length;
transformedNormal.z /= length;
}

meshData.positions[v * 3 + 0] = transformedPosition.x;
meshData.positions[v * 3 + 1] = transformedPosition.y;
meshData.positions[v * 3 + 2] = transformedPosition.z;

meshData.normals[v * 3 + 0] = transformedNormal.x;
meshData.normals[v * 3 + 1] = transformedNormal.y;
meshData.normals[v * 3 + 2] = transformedNormal.z;
}

exporter.AddMeshData(std::move(meshData));
}
}
#endif // NO_GLTF
79 changes: 77 additions & 2 deletions src/GLTFExportCallback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <GLTFExportCallback.h>
#include <nn/ffl/FFLDrawParam.h>
#include <IShader.h>
#include <cstring>
#include <limits>
#include <cstdio>
Expand All @@ -11,6 +12,7 @@
#include <algorithm>
#include <sstream>
#include <iomanip>
#include <utility>

#include <misc/rio_MemUtil.h>

Expand All @@ -32,6 +34,24 @@ void strUTF16ToUTF8(char* dst, const u16* src, s32 n); // DataUtils.cpp

namespace
{
rio::Vector3f MultiplyPoint(const rio::Matrix34f& mtx, const rio::Vector3f& vec)
{
return {
mtx.m[0][0] * vec.x + mtx.m[0][1] * vec.y + mtx.m[0][2] * vec.z + mtx.m[0][3],
mtx.m[1][0] * vec.x + mtx.m[1][1] * vec.y + mtx.m[1][2] * vec.z + mtx.m[1][3],
mtx.m[2][0] * vec.x + mtx.m[2][1] * vec.y + mtx.m[2][2] * vec.z + mtx.m[2][3]
};
}

rio::Vector3f MultiplyVector(const rio::Matrix34f& mtx, const rio::Vector3f& vec)
{
return {
mtx.m[0][0] * vec.x + mtx.m[0][1] * vec.y + mtx.m[0][2] * vec.z,
mtx.m[1][0] * vec.x + mtx.m[1][1] * vec.y + mtx.m[1][2] * vec.z,
mtx.m[2][0] * vec.x + mtx.m[2][1] * vec.y + mtx.m[2][2] * vec.z
};
}

// converts char* buffer to hex representation
std::string charToHex(char* input, int len)
{
Expand Down Expand Up @@ -122,6 +142,13 @@ namespace
GLTFExportCallback::GLTFExportCallback()
{
mpCharModel = nullptr;
mRootTransform = rio::Matrix34f::ident;
mUseRootTransform = false;
}

void GLTFExportCallback::AddMeshData(MeshData&& meshData)
{
mMeshes.push_back(std::move(meshData));
}

// Static function implementations
Expand Down Expand Up @@ -149,6 +176,22 @@ static const char* cMeshNames[FFL_MODULATE_TYPE_SHAPE_MAX] = {
"XluGlass"
};

static std::string GetMeshNameForModulateType(FFLModulateType type)
{
if (type < FFL_MODULATE_TYPE_SHAPE_MAX)
return cMeshNames[type];

switch (type)
{
case static_cast<FFLModulateType>(CUSTOM_MATERIAL_PARAM_BODY):
return "Body";
case static_cast<FFLModulateType>(CUSTOM_MATERIAL_PARAM_PANTS):
return "Pants";
default:
return "Custom_" + std::to_string(static_cast<int>(type));
}
}

void GLTFExportCallback::DrawFunc(void* pObj, const FFLDrawParam* drawParam)
{
GLTFExportCallback* self = static_cast<GLTFExportCallback*>(pObj);
Expand Down Expand Up @@ -524,6 +567,39 @@ void GLTFExportCallback::Draw(const FFLDrawParam& drawParam)

meshData.primitiveType = static_cast<rio::Drawer::PrimitiveMode>(drawParam.primitiveParam.primitiveType);

if (mUseRootTransform)
{
for (u32 i = 0; i < vertexCount; ++i)
{
rio::Vector3f position {
meshData.positions[i * 3 + 0],
meshData.positions[i * 3 + 1],
meshData.positions[i * 3 + 2]
};
position = MultiplyPoint(mRootTransform, position);
meshData.positions[i * 3 + 0] = position.x;
meshData.positions[i * 3 + 1] = position.y;
meshData.positions[i * 3 + 2] = position.z;

rio::Vector3f normal {
meshData.normals[i * 3 + 0],
meshData.normals[i * 3 + 1],
meshData.normals[i * 3 + 2]
};
normal = MultiplyVector(mRootTransform, normal);
const f32 length = std::sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z);
if (length > 0.0f)
{
normal.x /= length;
normal.y /= length;
normal.z /= length;
}
meshData.normals[i * 3 + 0] = normal.x;
meshData.normals[i * 3 + 1] = normal.y;
meshData.normals[i * 3 + 2] = normal.z;
}
}

// Store the meshData
mMeshes.push_back(meshData);
}
Expand Down Expand Up @@ -561,9 +637,8 @@ bool GLTFExportCallback::ExportModelInternal(const std::string& filename, std::o
// Set primitive mode based on the mesh's primitive type
primitive.mode = MapPrimitiveMode(meshData.primitiveType);

RIO_ASSERT(meshData.modulateType < FFL_MODULATE_TYPE_SHAPE_MAX); // do not include mask tex
// Set name of mesh depending on modulate type
gltfMesh.name = cMeshNames[meshData.modulateType];
gltfMesh.name = GetMeshNameForModulateType(meshData.modulateType);

// Add modulate parameters to this primitive's extras for custom usage
AddPrimitiveExtras(meshData, primitive);
Expand Down
Loading