跳到主要内容

资源:FBX格式

fbx

Arche 使用 FBX SDK 加载 FBX 格式文件,加载后的 FBX 文件主要用于 CPU 蒙皮动画系统使用。因此,在 SkinnedMeshRenderer 上,直接可以加载对应的资源:

apps/animation_app.cpp
characterRenderer->addSkinnedMesh("../assets/Models/Doggy/Doggy.fbx",
"../assets/Models/Doggy/doggy_skeleton.ozz");

在具体实现中,该函数会调用 loader 命名空间的函数:

bool loadScene(const char* _filename, const animation::Skeleton& skeleton, vector<Mesh>& _meshes);

其中 Mesh 并不是前几篇文章介绍过的网格资源,而是专门为了转译 FBX 中网格格式的中间数据:

// Defines a mesh with skinning information (joint indices and weights).
// The mesh is subdivided into parts that group vertices according to their
// number of influencing joints. Triangle indices are shared across mesh parts.
struct Mesh {
// Defines a portion of the mesh. A mesh is subdivided in sets of vertices
// with the same number of joint influences.
struct Part {
int vertex_count() const { return static_cast<int>(positions.size()) / 3; }

int influences_count() const {
const int _vertex_count = vertex_count();
if (_vertex_count == 0) {
return 0;
}
return static_cast<int>(joint_indices.size()) / _vertex_count;
}

typedef vector<float> Positions;
Positions positions;
enum { kPositionsCpnts = 3 }; // x, y, z components

typedef vector<float> Normals;
Normals normals;
enum { kNormalsCpnts = 3 }; // x, y, z components

typedef vector<float> Tangents;
Tangents tangents;
enum { kTangentsCpnts = 4 }; // x, y, z, right or left handed.

typedef vector<float> UVs;
UVs uvs; // u, v components
enum { kUVsCpnts = 2 };

typedef vector<uint8_t> Colors;
Colors colors;
enum { kColorsCpnts = 4 }; // r, g, b, a components

typedef vector<uint16_t> JointIndices;
JointIndices joint_indices; // Stride equals influences_count

typedef vector<float> JointWeights;
JointWeights joint_weights; // Stride equals influences_count - 1
};
typedef vector<Part> Parts;
Parts parts;
};

因此,这使得 SkinnedMeshRendererMeshRenderer 的渲染函数有很大不同,前者可以拥有多个蒙皮网格,但这些蒙皮网格并不是最终用于渲染的数据,而是要经过 ozz::geometry::SkinningJob 转换的数据:

ozz::geometry::SkinningJob skinning_job;
skinning_job.vertex_count = static_cast<int>(part_vertex_count);
const int part_influences_count = part.influences_count();

// Clamps joints influence count according to the option.
skinning_job.influences_count = part_influences_count;

// Setup skinning matrices, that came from the animation stage before being
// multiplied by inverse model-space bind-pose.
skinning_job.joint_matrices = _skinning_matrices;

// Setup joint's indices.
skinning_job.joint_indices = make_span(part.joint_indices);
skinning_job.joint_indices_stride = sizeof(uint16_t) * part_influences_count;

// Setup joint's weights.
if (part_influences_count > 1) {
skinning_job.joint_weights = make_span(part.joint_weights);
skinning_job.joint_weights_stride =
sizeof(float) * (part_influences_count - 1);
}

// Setup input positions, coming from the loaded mesh.
skinning_job.in_positions = make_span(part.positions);
skinning_job.in_positions_stride = sizeof(float) * ozz::loader::Mesh::Part::kPositionsCpnts;

ozz::geometry::SkinningJob 输出的数据才被传入到 GPU 用于渲染。