Init commit
This commit is contained in:
@@ -0,0 +1,456 @@
|
||||
// ReSharper disable CppDeclaratorNeverUsed
|
||||
// ReSharper disable CppDFAUnreachableFunctionCall
|
||||
|
||||
#include "../include/zassimp/importer.h"
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/postprocess.h>
|
||||
#include <Windows.h>
|
||||
#include <iostream>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
std::string GetAiMatrixPrint(const aiMatrix4x4 &am) {
|
||||
std::stringstream ss;
|
||||
ss << am.a1 << " " << am.a2 << " " << am.a3 << " " << am.a4 << '\n' <<
|
||||
am.b1 << " " << am.b2 << " " << am.b3 << " " << am.b4 << '\n' <<
|
||||
am.c1 << " " << am.c2 << " " << am.c3 << " " << am.c4 << '\n' <<
|
||||
am.d1 << " " << am.d2 << " " << am.d3 << " " << am.d4 << std::endl;
|
||||
return ss.str();
|
||||
}
|
||||
void PrintAiBoneInfo(const aiBone *bone) {
|
||||
std::cout << " Print Bone Info" << std::endl;
|
||||
std::cout << "Name: " << bone->mName.C_Str() <<
|
||||
|
||||
// mArmature 是 “bone armature node”,用于 skeleton conversion,
|
||||
// 并且必须启用 aiProcess_PopulateArmatureData 才会填充;
|
||||
// 默认构造时它就是 nullptr
|
||||
// "\nArmatureName: " << bone->mArmature->mName.C_Str() <<
|
||||
|
||||
// 这个东西是 DCC 场景图/transform hierarchy 的一部分。
|
||||
// "\nNode->Name: " << bone->mNode->mName.C_Str() <<
|
||||
|
||||
"\nNumWeights: " << bone->mNumWeights <<
|
||||
"\nWeights[0].VertexID: " << bone->mWeights->mVertexId <<
|
||||
"\nWeights[0].Weight: " << bone->mWeights->mWeight <<
|
||||
std::endl;
|
||||
}
|
||||
void PrintAiMeshInfo(const aiMesh *am) {
|
||||
std::cout << "======== Print AiMesh Info ========" << std::endl;
|
||||
std::cout << "Name: " << am->mName.C_Str() <<
|
||||
"\nHasBones: " << std::boolalpha << am->HasBones() <<
|
||||
"\nNumBones: " << am->mNumBones <<
|
||||
"\nHasFaces: " << std::boolalpha << am->HasFaces() <<
|
||||
"\nNumFaces: " << am->mNumFaces <<
|
||||
"\nMinAABB:" << am->mAABB.mMin.x << am->mAABB.mMin.y << am->mAABB.mMin.z <<
|
||||
"\nMaxAABB: " << am->mAABB.mMax.x << am->mAABB.mMax.y << am->mAABB.mMax.z <<
|
||||
"\nHasNormals: " << std::boolalpha << am->HasNormals() <<
|
||||
"\nHasPositions: " << std::boolalpha << am->HasPositions() <<
|
||||
"\nHasVertexColors[0]: " << std::boolalpha << am->HasVertexColors(0) <<
|
||||
"\nHasVertexColors[1]: " << std::boolalpha << am->HasVertexColors(1) <<
|
||||
"\nGetNumColorChannels: " << am->GetNumColorChannels() <<
|
||||
"\nMaterialIndex: " << am->mMaterialIndex <<
|
||||
"\nNumAnimMeshes: " << am->mNumAnimMeshes <<
|
||||
std::endl;
|
||||
for (size_t i = 0; i < am->mNumBones; ++i) {
|
||||
PrintAiBoneInfo(am->mBones[i]);
|
||||
}
|
||||
}
|
||||
int curNodeLevel = 0;
|
||||
void RecursePrintAiNodeInfo(const aiNode *node) {
|
||||
curNodeLevel++;
|
||||
std::cout << "++++++++ recurse print node LEVEL=" << curNodeLevel << " ++++++++" <<
|
||||
"\nname: " << node->mName.C_Str() <<
|
||||
"\nTsfMatrix:\n" << GetAiMatrixPrint(node->mTransformation) <<
|
||||
"NumMeshes: " << node->mNumMeshes <<
|
||||
"\nMeshIds: ";
|
||||
for (unsigned int meshid: std::span(node->mMeshes, node->mNumMeshes)) {
|
||||
std::cout << meshid << ' ';
|
||||
}
|
||||
std::cout << "\nSubNodeCount: " << node->mNumChildren << std::endl;
|
||||
for (auto *subnode: std::span(node->mChildren, node->mNumChildren)) {
|
||||
RecursePrintAiNodeInfo(subnode);
|
||||
}
|
||||
|
||||
|
||||
curNodeLevel--;
|
||||
}
|
||||
|
||||
|
||||
constexpr unsigned int GetFlag_Simple() {
|
||||
return aiProcess_Triangulate | aiProcess_FlipUVs;
|
||||
}
|
||||
|
||||
constexpr unsigned int GetFlag_Smooth() {
|
||||
return aiProcess_Triangulate |
|
||||
aiProcess_JoinIdenticalVertices |
|
||||
aiProcess_GenSmoothNormals |
|
||||
aiProcess_CalcTangentSpace |
|
||||
aiProcess_ImproveCacheLocality |
|
||||
aiProcess_OptimizeMeshes |
|
||||
aiProcess_SortByPType |
|
||||
aiProcess_FindInvalidData |
|
||||
aiProcess_FindDegenerates |
|
||||
aiProcess_MakeLeftHanded |
|
||||
aiProcess_FlipUVs |
|
||||
aiProcess_FlipWindingOrder |
|
||||
aiProcess_ConvertToLeftHanded;
|
||||
|
||||
}
|
||||
|
||||
constexpr unsigned int GetFlag_NoSmoothNormal() {
|
||||
return aiProcess_Triangulate |
|
||||
aiProcess_JoinIdenticalVertices |
|
||||
aiProcess_CalcTangentSpace |
|
||||
aiProcess_ImproveCacheLocality |
|
||||
aiProcess_OptimizeMeshes |
|
||||
aiProcess_SortByPType |
|
||||
aiProcess_FindInvalidData |
|
||||
aiProcess_FindDegenerates |
|
||||
aiProcess_MakeLeftHanded |
|
||||
aiProcess_FlipUVs |
|
||||
aiProcess_FlipWindingOrder |
|
||||
aiProcess_ConvertToLeftHanded;
|
||||
}
|
||||
|
||||
// DirectX::XMFLOAT4X4 AiToFloat4x4(const aiMatrix4x4 &ai) {
|
||||
// return {
|
||||
// ai.a1, ai.a2, ai.a3, ai.a4,
|
||||
// ai.b1, ai.b2, ai.b3, ai.b4,
|
||||
// ai.c1, ai.c2, ai.c3, ai.c4,
|
||||
// ai.d1, ai.d2, ai.d3, ai.d4
|
||||
// };
|
||||
// }
|
||||
DirectX::XMFLOAT4X4 AiToRowMajorFloat4x4(const aiMatrix4x4 &m) {
|
||||
return {
|
||||
m.a1, m.b1, m.c1, m.d1,
|
||||
m.a2, m.b2, m.c2, m.d2,
|
||||
m.a3, m.b3, m.c3, m.d3,
|
||||
m.a4, m.b4, m.c4, m.d4
|
||||
};
|
||||
}
|
||||
|
||||
DirectX::XMFLOAT4X4 InverseFloat4x4(const DirectX::XMFLOAT4X4 &origin) {
|
||||
using namespace DirectX;
|
||||
|
||||
XMMATRIX m = XMLoadFloat4x4(&origin);
|
||||
m = XMMatrixInverse(nullptr, m);
|
||||
|
||||
XMFLOAT4X4 result{};
|
||||
XMStoreFloat4x4(&result, m);
|
||||
return result;
|
||||
}
|
||||
|
||||
class Processor {
|
||||
public:
|
||||
explicit Processor(std::filesystem::path path,
|
||||
const aiScene *scene) : modelPath(std::move(path)),
|
||||
m_scene(scene) {}
|
||||
|
||||
|
||||
Z::Model *ProcessAiScene() {
|
||||
m_model = new Z::Model{};
|
||||
// m_model->name = m_scene->mName.C_Str();
|
||||
// m_model->name = modelPath.string();
|
||||
std::u8string u8 = modelPath.u8string();
|
||||
m_model->name = {reinterpret_cast<const char *>(u8.c_str()), u8.size()};
|
||||
|
||||
m_model->path = modelPath;
|
||||
|
||||
// 创建MeshData,顺序映射 aiScene->mMeshes 数组
|
||||
m_model->ar_meshDatas.reserve(m_scene->mNumMeshes);
|
||||
int _i = 0;
|
||||
for (const aiMesh *am: std::span(m_scene->mMeshes, m_scene->mNumMeshes)) {
|
||||
m_model->ar_meshDatas.push_back(CreateMeshData(am, _i));
|
||||
_i++;
|
||||
}
|
||||
|
||||
// 处理NodeTree
|
||||
m_model->skeletonData.rootNode = BuildBoneNodeTree(m_scene->mRootNode);
|
||||
m_model->skeletonData.gInvTsf =
|
||||
InverseFloat4x4(AiToRowMajorFloat4x4(m_scene->mRootNode->mTransformation));
|
||||
|
||||
// 填充 AnimationClip
|
||||
if (m_scene->HasAnimations()) {
|
||||
m_model->ar_animationClips.reserve(m_scene->mNumAnimations);
|
||||
for (const auto *aiAnim: std::span(m_scene->mAnimations, m_scene->mNumAnimations)) {
|
||||
m_model->ar_animationClips.emplace_back(FillAnimClip(aiAnim));
|
||||
}
|
||||
}
|
||||
|
||||
return m_model;
|
||||
}
|
||||
|
||||
private:
|
||||
//region 创建MeshData,建立Bone的索引表,递归创建skeleten data
|
||||
Z::Mesh CreateMeshData(const aiMesh *am, int index) const {
|
||||
Z::Mesh mesh{};
|
||||
mesh.index = index;
|
||||
mesh.name = am->mName.C_Str();
|
||||
//装填material data
|
||||
// mesh.material = LoadMaterialData(am);
|
||||
// mesh.material=nullptr;
|
||||
//装填vertex data
|
||||
const uint32_t c_vert = am->mNumVertices;
|
||||
mesh.ar_vertices.reserve(c_vert);
|
||||
for (uint32_t i = 0; i < c_vert; ++i) {
|
||||
Z::VertexData vert{};
|
||||
vert.ar_boneIndex.fill(-1);
|
||||
vert.pos = {am->mVertices[i].x, am->mVertices[i].y, am->mVertices[i].z};
|
||||
if (am->HasNormals()) {
|
||||
vert.normal = {am->mNormals[i].x, am->mNormals[i].y, am->mNormals[i].z};
|
||||
}
|
||||
if (am->HasTangentsAndBitangents()) {
|
||||
vert.tangent = {am->mTangents[i].x, am->mTangents[i].y, am->mTangents[i].z};
|
||||
}
|
||||
if (am->HasTextureCoords(0)) {
|
||||
vert.uv0 = {am->mTextureCoords[0][i].x, am->mTextureCoords[0][i].y};
|
||||
}
|
||||
if (am->HasTextureCoords(1)) {
|
||||
vert.uv1 = {am->mTextureCoords[1][i].x, am->mTextureCoords[1][i].y};
|
||||
}
|
||||
if (am->HasVertexColors(0)) {
|
||||
vert.color = {
|
||||
am->mColors[0][i].r, am->mColors[0][i].g,
|
||||
am->mColors[0][i].b, am->mColors[0][i].a
|
||||
};
|
||||
}
|
||||
mesh.ar_vertices.push_back(vert);
|
||||
}
|
||||
// 构建bone name <-> IDX 表,并填写每个Vertex的骨骼ID,对应权重
|
||||
for (auto *bone: std::span(am->mBones, am->mNumBones)) {
|
||||
if (bone->mNumWeights > 0) {
|
||||
mesh.hasSkin = true;
|
||||
UINT boneID = GetOrCreateBoneID(bone);
|
||||
std::cout << "= = = = 创建Mesh阶段读取aiMesh->mBones[],aiBone->name=" <<
|
||||
bone->mName.C_Str() << " BoneNumWeights=" << bone->mNumWeights <<
|
||||
" 当前BoneID=" << boneID << std::endl;
|
||||
for (aiVertexWeight &vWeight: std::span(bone->mWeights, bone->mNumWeights)) {
|
||||
auto &vert = mesh.ar_vertices[vWeight.mVertexId];
|
||||
for (int i = 0; i < Z::MAX_BONES_PER_VERT; ++i) {
|
||||
if (vert.ar_boneIndex[i] == -1) {
|
||||
vert.ar_boneIndex[i] = static_cast<int>(boneID);
|
||||
vert.ar_boneWeights[i] = vWeight.mWeight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//装填 INDICES DATA
|
||||
|
||||
mesh.ar_indices.reserve(am->mNumFaces * 3);
|
||||
for (const auto &face: std::span(am->mFaces, am->mNumFaces)) {
|
||||
if (face.mNumIndices != 3) continue;
|
||||
mesh.ar_indices.push_back(face.mIndices[0]);
|
||||
mesh.ar_indices.push_back(face.mIndices[1]);
|
||||
mesh.ar_indices.push_back(face.mIndices[2]);
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
// Z::MaterialData LoadMaterialData(const aiMesh *am) const {
|
||||
// Z::MaterialData out{};
|
||||
// if (!am || am->mMaterialIndex >= m_scene->mNumMaterials) return out;
|
||||
//
|
||||
// aiMaterial *mat = m_scene->mMaterials[am->mMaterialIndex];
|
||||
//
|
||||
// out.name = mat->GetName().C_Str();
|
||||
//
|
||||
// // // baseColor: PBR 优先
|
||||
// // aiColor4D base{};
|
||||
// // if (mat->Get(AI_MATKEY_BASE_COLOR, base) == AI_SUCCESS) {
|
||||
// // out.baseColor = {base.r, base.g, base.b, base.a};
|
||||
// // } else {
|
||||
// // // fallback: 老式 diffuse
|
||||
// // aiColor3D diff{};
|
||||
// // if (mat->Get(AI_MATKEY_COLOR_DIFFUSE, diff) == AI_SUCCESS) {
|
||||
// // out.baseColor = {diff.r, diff.g, diff.b, 1.0f};
|
||||
// // }
|
||||
// // }
|
||||
//
|
||||
// auto LoadTextures = [&](aiTextureType type) {
|
||||
// const unsigned int n = mat->GetTextureCount(type);
|
||||
//
|
||||
// for (unsigned int i = 0; i < n; ++i) {
|
||||
// aiString path;
|
||||
// if (mat->GetTexture(type, i, &path) != AI_SUCCESS) continue;
|
||||
//
|
||||
// Z::TextureData tex{};
|
||||
// tex.path = path.C_Str();
|
||||
// tex.type = static_cast<Z::TextureType>(type);
|
||||
//
|
||||
// out.ar_textures.push_back(std::move(tex));
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// // 常用纹理
|
||||
// LoadTextures(aiTextureType_BASE_COLOR); // PBR base color / albedo
|
||||
// LoadTextures(aiTextureType_DIFFUSE); // 传统 diffuse
|
||||
// LoadTextures(aiTextureType_NORMALS); // normal
|
||||
// LoadTextures(aiTextureType_HEIGHT); // 有些格式把 normal 放这里
|
||||
// LoadTextures(aiTextureType_SPECULAR); // specular
|
||||
// LoadTextures(aiTextureType_METALNESS); // metallic
|
||||
// LoadTextures(aiTextureType_DIFFUSE_ROUGHNESS); // roughness
|
||||
// LoadTextures(aiTextureType_AMBIENT_OCCLUSION); // ao
|
||||
// LoadTextures(aiTextureType_EMISSIVE); // emissive
|
||||
//
|
||||
// return out;
|
||||
// }
|
||||
|
||||
|
||||
UINT GetOrCreateBoneID(const aiBone *bone) const {
|
||||
auto *name = bone->mName.C_Str();
|
||||
auto idx = 0;
|
||||
auto &arr = m_model->skeletonData.ar_boneInfo;
|
||||
for (auto it = arr.begin(); it != arr.end(); ++it) {
|
||||
if (it->name == name) {
|
||||
return idx;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
arr.push_back({name, AiToRowMajorFloat4x4(bone->mOffsetMatrix)});
|
||||
idx = static_cast<int>(arr.size() - 1);
|
||||
m_model->skeletonData.map_boneName_idx.emplace(name, idx);
|
||||
return idx;
|
||||
}
|
||||
|
||||
// 处理skeleton[0] 的 NodeTree
|
||||
Z::Node BuildBoneNodeTree(const aiNode *an) {
|
||||
Z::Node node{};
|
||||
node.name = an->mName.C_Str();
|
||||
std::cout << "+ + + + 正在递归处理NodeTree,当前node.name=" << node.name << std::endl;
|
||||
node.localTsf = AiToRowMajorFloat4x4(an->mTransformation);
|
||||
// 处理mesh
|
||||
for (UINT meshIdx: std::span(an->mMeshes, an->mNumMeshes)) {
|
||||
Z::Mesh *meshData = &m_model->ar_meshDatas[meshIdx];
|
||||
std::cout << "+ + Node中处理Mesh,meshIdx=" << meshIdx <<
|
||||
", MeshData.Name=" << meshData->name << std::endl;
|
||||
meshData->localTsf = AiToRowMajorFloat4x4(an->mTransformation); //非骨骼模型的统一偏移矩阵
|
||||
}
|
||||
|
||||
int cc = static_cast<int>(an->mNumChildren);
|
||||
node.ar_child.reserve(cc);
|
||||
for (int i = 0; i < cc; ++i) {
|
||||
node.ar_child.push_back(BuildBoneNodeTree(an->mChildren[i]));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region 填充 AnimationClip
|
||||
|
||||
static DirectX::XMFLOAT3 AiVector3ToXMFloat3(const aiVector3D &vec3) {
|
||||
return {vec3.x, vec3.y, vec3.z};
|
||||
}
|
||||
static DirectX::XMFLOAT4 AiVector4ToXMFloat4(const aiQuaternion &vec4) {
|
||||
return {vec4.x, vec4.y, vec4.z, vec4.w};
|
||||
}
|
||||
|
||||
static Z::AnimationClip FillAnimClip(const aiAnimation *ai) {
|
||||
Z::AnimationClip anim{};
|
||||
anim.name = ai->mName.C_Str();
|
||||
anim.durationTicks = ai->mDuration;
|
||||
anim.tickPerSec = ai->mTicksPerSecond;
|
||||
|
||||
anim.map_channels.reserve(ai->mNumChannels);
|
||||
for (int i = 0; i < ai->mNumChannels; ++i) {
|
||||
aiNodeAnim *c = ai->mChannels[i];
|
||||
Z::NodeAnimation nodeAnim{};
|
||||
nodeAnim.name = ai->mName.C_Str();
|
||||
nodeAnim.ar_posKeys.reserve(c->mNumPositionKeys);
|
||||
for (int j = 0; j < c->mNumPositionKeys; ++j) {
|
||||
nodeAnim.ar_posKeys.emplace_back(
|
||||
c->mPositionKeys[j].mTime, AiVector3ToXMFloat3(c->mPositionKeys[j].mValue));
|
||||
}
|
||||
nodeAnim.ar_rotationKeys.reserve(c->mNumRotationKeys);
|
||||
for (int j = 0; j < c->mNumRotationKeys; ++j) {
|
||||
nodeAnim.ar_rotationKeys.emplace_back(
|
||||
c->mRotationKeys[j].mTime, AiVector4ToXMFloat4(c->mRotationKeys[j].mValue));
|
||||
}
|
||||
nodeAnim.ar_scaleKeys.reserve(c->mNumScalingKeys);
|
||||
for (int j = 0; j < c->mNumScalingKeys; ++j) {
|
||||
nodeAnim.ar_scaleKeys.emplace_back(
|
||||
c->mScalingKeys[j].mTime, AiVector3ToXMFloat3(c->mScalingKeys[j].mValue));
|
||||
}
|
||||
anim.map_channels.emplace(std::string{c->mNodeName.C_Str()}, std::move(nodeAnim));
|
||||
}
|
||||
|
||||
return anim;
|
||||
}
|
||||
|
||||
//endregion
|
||||
private :
|
||||
const std::filesystem::path modelPath;
|
||||
const aiScene *const m_scene;
|
||||
Z::Model *m_model{};
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool Importer::ImportFromFile(const std::filesystem::path &path, Z::Model **out) const {
|
||||
// 导入器
|
||||
Assimp::Importer importer;
|
||||
|
||||
// 导入前配置选项
|
||||
// 打开FBX单位从 cm->m 的转换开关
|
||||
importer.SetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, true);
|
||||
// 设置全局缩放因子
|
||||
// importer.SetPropertyFloat(AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 0.01f);
|
||||
|
||||
// 导入文件,初步处理
|
||||
const aiScene *scene = importer.ReadFile(
|
||||
reinterpret_cast<const char *>(path.u8string().c_str()),
|
||||
GetFlag_NoSmoothNormal() | aiProcess_GlobalScale);
|
||||
|
||||
// 导入后验证
|
||||
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
|
||||
MessageBox(nullptr, L"Error", L"Assimp ReadFile Error",MB_OK);
|
||||
return false;
|
||||
}
|
||||
|
||||
// for (auto *am: std::span(scene->mMeshes, scene->mNumMeshes)) {
|
||||
// for (int i = 0; i < am->mNumBones; ++i) {
|
||||
// aiBone *bone = am->mBones[i];
|
||||
// std::cout << "BoneName = " << bone->mName.C_Str() <<
|
||||
// "\nNumWeights = " << bone->mNumWeights << std::endl;
|
||||
// }
|
||||
// }
|
||||
|
||||
// RecursePrintAiNodeInfo(scene->mRootNode);
|
||||
|
||||
Processor pro(path, scene);
|
||||
*out = pro.ProcessAiScene();
|
||||
|
||||
return true;
|
||||
}
|
||||
void Importer::ReleaseLoadedModel(const Z::Model *model) const {
|
||||
delete model;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
Reference in New Issue
Block a user