OpenGL Assimp 加载3D模型介绍
Assimp对应模型结构体解说
-
所有的模型、场景数据都包含在scene对象中,如所有的材质和Mesh。同样,场景的根节点引用也包含在这个scene对象中
-
场景的Root node(根节点)可能也会包含很多子节点和一个指向保存模型点云数据mMeshes[]的索引集合。根节点上的mMeshes[]里保存了实际了Mesh对象,而每个子节点上的mMesshes[]都只是指向根节点中的mMeshes[]的一个引用
-
一个Mesh对象本身包含了渲染所需要的所有相关数据,像是顶点位置、法向量、纹理坐标、面(Face)和物体的材质。
-
一个Mesh会包含多个面片。一个Face(面片)表示渲染中的一个最基本的形状单位,即图元(基本图元有点、线、三角面片、矩形面片)。一个面片记录了一个图元的顶点索引,通过这个索引,可以在mMeshes[]中寻找到对应的顶点位置数据。顶点数据和索引分开存放,可以便于我们使用缓存(VBO、NBO、TBO、IBO)来高速渲染物体
-
一个Mesh还会包含一个Material(材质)对象用于指定物体的一些材质属性。如颜色、纹理贴图(漫反射贴图、高光贴图等)
所以第一步加载一个模型文件为scene对象,获取根节点,通过根节点获取每个节点对应的Mesh对象(递归搜索每个节点的子节点来获取所有的节点),并处理每个Mesh对象对应的顶点数据、索引以及它的材质属性。得到一个只包含我们需要的数据的Mesh集合。
Assimp调用解析流程介绍
importer.ReadFile
进行加载模型获取aiScene
对象,这个所有数据来源,后面只要解析aiScene
,解析对应节点,所要每个节点材质和aiMesh对应的顶点、纹理、法向量、网格面、骨骼信息
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
int loadModel() {
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile("model.FBX", aiProcess_Triangulate);
// process model data
// ...
return 0;
}
scene->mRootNode
相当一个aiNode
,获取根节点,然后递归找出所有子节点,AssimpMesh3DS
为了解析aiMesh
void processNode(aiNode *node, const aiScene *scene, std::string& directory) {
for(unsigned int i = 0; i < node->mNumMeshes; i++) {
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
LOGE("=====Operator3DS=====mesh->mName====: %s", mesh->mName.C_Str());
AssimpMesh3DS *assimpMesh = new AssimpMesh3DS();
assimpMesh->setServiceObjRef(this);
if ( assimpMesh->init(mesh, scene, m_boneNumber, m_boneOffsets, m_boneMapping, m_paramRef, directory) ) {
m_meshes.push_back( assimpMesh );
}
}
for(unsigned int i = 0; i < node->mNumChildren; i++) {
LOGE("=====Operator3DS=====mChildren->mName====: %s", node->mChildren[i]->mName.C_Str());
processNode(node->mChildren[i], scene, directory);
}
}
processMaterial
方法处理各种材质生成相应的纹理D是环境光材质、N是顶点法向量数据材质,S是反色光材质,根据自己路径进行加载相关材质生成纹理,mesh->mNumVertices
进行顶点坐标、纹理坐标等处理,mesh->mNumFaces
是网格面处理,mesh->mNumBones
是骨骼处理
void AssimpMesh3DS::processMaterial(_3DModelParam* param, std::string& directory, Texture*& texture, aiMaterial* material, aiTextureType type) {
aiString str;
LOGE("====Operator3DS===aiTextureType=%d======",type);
if ( material->GetTexture(type, 0, &str) == aiReturn_SUCCESS ) {
std::string path = str.C_Str();
// LOGE("====Operator3DS===111=path=%s======",path.c_str());
size_t pos = path.find_last_of("\\");
if (pos == std::string::npos) {
pos = path.find_last_of("/");
}
if (pos != std::string::npos) {
path = path.substr(pos + 1, str.length);
}
path = directory + path;
LOGE("====Operator3DS===2222=path=%s======",path.c_str());
if ( FileCheck::checkFileExist(path) ) {
if (type == aiTextureType_DIFFUSE && param->frameInfo.size() == 5 && param->radomObjcet.empty()) {
FrameParam frame;
frame.preloadFrameNumber = param->frameInfo[0];
frame.cacheNumberMax = param->frameInfo[1];
frame.firstFrameExtend = param->frameInfo[2];
frame.lastFrameExtend = param->frameInfo[3];
frame.frameNumber = param->frameInfo[4];
frame.path = path;
m_diffuseCache = m_bufferService->createTextureCache(frame, param->framefps, true);
} else if (param->radomObjcet == path) {
texture = m_bufferService->createTexture(param->radomPath);
} else {
texture = m_bufferService->createTexture(path);
}
}
}
}
bool AssimpMesh3DS::init(aiMesh *mesh, const aiScene *scene, short& boneNumber, std::vector<aiMatrix4x4>& boneOffsets, std::map<std::string, uint16_t>& boneMapping, _3DModelParam* param, std::string& directory) {
if (!mesh->HasPositions()) {
return false;
}
m_defines = "";
m_materialName = "";
m_parame = param;
// 材质
if (scene->HasMaterials()) {
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
m_materialName = material->GetName().C_Str();
LOGE("====Operator3DS====m_materialName=%s======",m_materialName.c_str());
processMaterial(param, directory, m_diffuse, material, aiTextureType_DIFFUSE);
processMaterial(param, directory, m_normals, material, aiTextureType_NORMALS);
processMaterial(param, directory, m_specular, material, aiTextureType_SPECULAR);
processMaterial(param, directory, m_reflection, material, aiTextureType_OPACITY);
processMaterial(param, directory, m_emissive, material, aiTextureType_AMBIENT);
if (m_normals) {
LOGE("====Operator3DS==m_normals====");
GLUtils::addDefine(m_defines, "DEFINE_NORMALS");
}
if (m_specular) {
LOGE("====Operator3DS==m_specular====");
GLUtils::addDefine(m_defines, "DEFINE_SPECULAR");
}
if (m_reflection) {
GLUtils::addDefine(m_defines, "DEFINE_REFLECTION");
LOGE("====Operator3DS==m_reflection====");
}
if (m_emissive) {
GLUtils::addDefine(m_defines, "DEFINE_EMISSIVE");
LOGE("====Operator3DS==m_emissive====");
}
}
std::vector<GLushort> indices;
std::vector<AssimpVertex3DS> vertices;
LOGE("====Operator3DS====mNumVertices=%d==%d==%d==",mesh->mNumVertices,mesh->mNumFaces,mesh->mNumBones);
for(unsigned int i = 0; i < mesh->mNumVertices; i++) {
AssimpVertex3DS vertex;
// 顶点坐标
vertex.position.x = mesh->mVertices[i].x;
vertex.position.y = mesh->mVertices[i].y;
vertex.position.z = mesh->mVertices[i].z;
// 纹理坐标
if (mesh->HasTextureCoords(0)) {
vertex.texcoord.x = mesh->mTextureCoords[0][i].x;
vertex.texcoord.y = mesh->mTextureCoords[0][i].y;
}
// 法线坐标
if (mesh->HasNormals()) {
vertex.normal.x = mesh->mNormals[i].x;
vertex.normal.y = mesh->mNormals[i].y;
vertex.normal.z = mesh->mNormals[i].z;
}
// 切线坐标,有法线贴图时,才处理切线坐标
if (m_normals && mesh->HasTangentsAndBitangents()) {
vertex.tangent.x = mesh->mTangents[i].x;
vertex.tangent.y = mesh->mTangents[i].y;
vertex.tangent.z = mesh->mTangents[i].z;
// vertex.bitangent.x = mesh->mBitangents[i].x;
// vertex.bitangent.y = mesh->mBitangents[i].y;
// vertex.bitangent.z = mesh->mBitangents[i].z;
}
if(param->Filter3DType==Filter_3DSticker_Face)
{
vertices_3d.push_back(vertex);
}
else
{
vertices.push_back(vertex);
}
}
// 网格的面数,顶点数
for(unsigned int i = 0; i < mesh->mNumFaces; i++) {
m_indexCount += mesh->mFaces[i].mNumIndices;
indices.push_back(mesh->mFaces[i].mIndices[0]);
indices.push_back(mesh->mFaces[i].mIndices[1]);
indices.push_back(mesh->mFaces[i].mIndices[2]);
}
if (m_indexCount > 65535) { //使用unsigned short, 精度范围 0 ~ 65536
LOGW("AssimpMesh3DS::init: too many trangles, max 65535, current %d", m_indexCount);
}
// 骨骼
for (unsigned int i = 0; i < mesh->mNumBones; i++) {
unsigned short boneIndex = 0;
auto bone = mesh->mBones[i];
std::string boneName(bone->mName.data);
if (boneMapping.find(boneName) == boneMapping.end()) {
boneIndex = boneNumber++;
boneMapping[boneName] = boneIndex;
boneOffsets.push_back(bone->mOffsetMatrix);
} else {
boneIndex = boneMapping[boneName];
}
for (unsigned int j = 0 ; j < bone->mNumWeights ; j++) {
unsigned int vertexID = bone->mWeights[j].mVertexId;
float Weight = bone->mWeights[j].mWeight;
// 限制每个顶点,受影响的骨骼数(权重),不超过4个
vertices[vertexID].addBoneData(boneIndex, Weight);
}
}
//新3d方式,顶点数据作为全局变量保存,后面需要再调用
if(param->Filter3DType==Filter_3DSticker_Face)
{
setupMesh(indices, vertices_3d);
}
else
{
setupMesh(indices, vertices);
}
return true;
}