您的位置:首页 > 移动开发 > Cocos引擎

Cocos2d-x 3.x 图形学渲染系列二十八

2017-02-02 08:05 453 查看
笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。 CSDN视频网址:http://edu.csdn.net/lecturer/144 昨天,刚从丈母娘家回来,继续博客的更新,接着Cocos2d-x 3.x图形学渲染系列二十七继续系列二十八的编写。 接下来读取FBX模型文件信息,首先要做的是把读取的模型信息进行归类并且序列化,在前面系列中介绍的c3t模型文件中关于模型的信息包括:attributes属性信息,顶点信息,子网格信息,索引信息,骨骼动画信息,材质信息等等,读取FBX模型文件信息类的完整代码实现如下所示: #include "Node.h"
#include "NodePart.h"
#include "Animation.h"
#include "NodeAnimation.h"
#include "Keyframe.h"
#include "Material.h"
#include "Attributes.h"
#include "MeshPart.h"
#include "Mesh.h"
#include "Model.h"

namespace fbxconv {
namespace modeldata {

staticconstchar* getPrimitiveTypeString(constint&primitiveTypeId) {
switch(primitiveTypeId){
case0:
return"POINTS";
case1:
return"LINES";
case3:
return"LINE_STRIP";
case4:
return"TRIANGLES";
case5:
return"TRIANGLE_STRIP";
default:
return"UNKNOWN";
}
}
staticconstchar* getWrapModeUseString(constFbxFileTexture::EWrapMode&textureUse)
{
switch(textureUse){
caseFbxFileTexture::eRepeat:
return"REPEAT";
caseFbxFileTexture::eClamp:
return"CLAMP";
default:
return"UNKNOWN";
}
}
staticconstchar* getTextureUseString(constMaterial::Texture::Usage&textureUse) {
switch(textureUse){
caseMaterial::Texture::Ambient:
return"AMBIENT";
caseMaterial::Texture::Bump:
return"BUMP";
caseMaterial::Texture::Diffuse:
return"DIFFUSE";
caseMaterial::Texture::Emissive:
return"EMISSIVE";
caseMaterial::Texture::None:
return"NONE";
caseMaterial::Texture::Normal:
return"NORMAL";
caseMaterial::Texture::Reflection:
return"REFLECTION";
caseMaterial::Texture::Shininess:
return"SHININESS";
caseMaterial::Texture::Specular:
return"SPECULAR";
caseMaterial::Texture::Transparency:
return"TRANSPARENCY";
default:
return"UNKNOWN";
}
}

voidModel::serialize(json::BaseJSONWriter&writer) const {

std::list<std::string> _bonenames;
for (std::vector<Node *>::const_iterator itr = nodes.begin(); itr != nodes.end(); ++itr)
{
(*itr)->loadBoneNames(_bonenames);
}
for (std::vector<Node *>::const_iterator itr = nodes.begin(); itr != nodes.end(); ++itr)
{
bool skeleton=false;
(*itr)->checkIsSkeleton(skeleton,_bonenames);
(*itr)->setSkeleton(skeleton);
}
writer.obj(6);
char szVersion[64] = {0};
sprintf(szVersion, "%d.%d", VERSION_HI, VERSION_LO);
writer <<"version" = szVersion;
if(exportPart == EXPORT_PART_ALL || exportPart == EXPORT_PART_MODEL)
{
writer <<"id" = id;
writer <<"meshes" = meshes;
writer <<"materials" = materials;
writer <<"nodes" = nodes;
}

if(exportPart == EXPORT_PART_ALL || exportPart == EXPORT_PART_ANIMATION)
{
writer <<"animations" = animations;
}
writer.end();
}

voidMesh::serialize(json::BaseJSONWriter&writer) const {
writer.obj(3);
writer <<"attributes" = attributes;
writer.val("vertices").is().data(vertices, vertexSize);
writer <<"parts" = parts;
writer.end();
}

voidAttributes::serialize(json::BaseJSONWriter&writer) const {
constunsignedint len = length();
writer.arr(len, 8);
for (unsignedint i = 0; i < len; i++)
{
writer <<json::obj;

MeshVertexAttrib v;
std::string key = name(i);
v = attributemap.find(key)->second;

writer <<"size"<< v.size;
writer <<"type"<< v.type;
writer <<"attribute"<< v.name;
writer <<json::end;
if(key == "VERTEX_ATTRIB_BLEND_INDEX")
{
break;
}
}
writer.end();
}
voidMeshPart::serialize(json::BaseJSONWriter&writer) const {
writer.obj(4);
writer <<"id" = id;
writer <<"type" = getPrimitiveTypeString(primitiveType);
writer.val("indices").is().data(indices, 12);
writer <<"aabb" = aabb;
writer <<json::end;
}

voidMaterial::serialize(json::BaseJSONWriter&writer) const {
writer <<json::obj;
writer <<"id" = id;
if (ambient.valid)
writer <<"ambient" = ambient.value;
if (diffuse.valid)
writer <<"diffuse" = diffuse.value;
if (emissive.valid)
writer <<"emissive" = emissive.value;
if (opacity.valid)
writer <<"opacity" = opacity.value;
if (specular.valid)
writer <<"specular" = specular.value;
if (shininess.valid)
writer <<"shininess" = shininess.value;
if (!textures.empty())
writer <<"textures" = textures;
writer <<json::end;
}

voidMaterial::Texture::serialize(json::BaseJSONWriter&writer) const {
writer <<json::obj;
writer <<"id" = id;
writer <<"filename" = path;
if (uvTranslation[0] != 0.f || uvTranslation[1] != 0.f)
writer <<"uvtranslation" = uvTranslation;
if (uvScale[0] != 1.f || uvScale[1] != 1.f)
writer <<"uvscaling" = uvScale;
writer <<"type" = getTextureUseString(usage);
writer <<"wrapModeU" = getWrapModeUseString(wrapModeU);
writer <<"wrapModeV" =getWrapModeUseString(wrapModeV);
writer <<json::end;
}

voidNode::serialize(json::BaseJSONWriter&writer) const {
writer <<json::obj;
writer <<"id" = id;
writer <<"skeleton" = _skeleton;
writer <<"transform"<<transforms;
if (!parts.empty())
writer <<"parts" = parts;
if (!children.empty())
writer <<"children" = children;
writer <<json::end;
}

template<class T, size_t n>void writeAsFloat(json::BaseJSONWriter&writer, constchar *k, const T(&v)
) {
staticfloat tmp
;
for (int i = 0; i < n; ++i)
tmp[i] = (float)v[i];
writer << k << tmp;
}

voidNodePart::serialize(json::BaseJSONWriter&writer) const {
writer <<json::obj;
writer <<"meshpartid" = meshPart->id;
writer <<"materialid" = material->id;
if (!bones.empty()) {
writer.val("bones").is().arr();
for (std::vector<std::pair<Node *, FbxAMatrix>>::const_iterator it = bones.begin(); it != bones.end(); ++it) {
writer <<json::obj;
writer <<"node" = it->first->id;
float tmp[16];
for(int i = 0; i <4; i++)
{
for(int j = 0; j <4; j++)
{
tmp[i*4 + j] = it->second.Double44()[i][j];
}
}
writer <<"transform"<< tmp;
writer <<json::end;
}
writer.end();
}
if (!uvMapping.empty()) {
writer.val("uvMapping").is().arr(uvMapping.size(), 16);
for (std::vector<std::vector<Material::Texture *>>::const_iterator it = uvMapping.begin(); it != uvMapping.end(); ++it) {
writer.arr((*it).size(), 16);
for (std::vector<Material::Texture *>::const_iterator tt = (*it).begin(); tt != (*it).end(); ++tt)
writer <<material->getTextureIndex(*tt);
writer.end();
}
writer.end();
}
writer <<json::end;
}

voidAnimation::serialize(json::BaseJSONWriter&writer) const {
writer.obj(2);
writer <<"id" = id;
writer <<"length" = length;
writer <<"bones" = nodeAnimations;
writer.end();
}

voidNodeAnimation::serialize(json::BaseJSONWriter&writer) const {
writer.obj(2);
writer <<"boneId" = node->id;
writer <<"keyframes" = keyframes;
writer.end();
}

voidKeyframe::serialize(json::BaseJSONWriter&writer) const {
writer <<json::obj;
writer <<"keytime" = time;
if (hasRotation)
writer <<"rotation" = rotation;
if (hasScale)
writer <<"scale" = scale;
if (hasTranslation)
writer <<"translation" = translation;
writer <<json::end;
}

}
}

下面开始c3b或者c3t模型文件的写入操作,写入类主要工作实现了c3b或者c3t模型的信息写入操作,类的完整代码实现如下所示:
#include "Node.h"
#include "NodePart.h"
#include "Animation.h"
#include "NodeAnimation.h"
#include "Keyframe.h"
#include "Material.h"
#include "Attributes.h"
#include "MeshPart.h"
#include "Mesh.h"
#include "Model.h"
#include "Reference.h"
#include "FileIO.h"
#include "Reference.h"
namespace fbxconv {
namespace modeldata {
static const char* getTextureUseString(const Material::Texture::Usage& textureUse) {
switch(textureUse){
case Material::Texture::Ambient:
return"AMBIENT";
case Material::Texture::Bump:
return"BUMP";
case Material::Texture::Diffuse:
return"DIFFUSE";
case Material::Texture::Emissive:
return"EMISSIVE";
case Material::Texture::None:
return"NONE";
case Material::Texture::Normal:
return"NORMAL";
case Material::Texture::Reflection:
return"REFLECTION";
case Material::Texture::Shininess:
return"SHININESS";
case Material::Texture::Specular:
return"SPECULAR";
case Material::Texture::Transparency:
return"TRANSPARENCY";
default:
return"UNKNOWN";
}
}
static const char* getWrapModeUseString(const FbxFileTexture::EWrapMode& textureUse)
{
switch(textureUse){
case FbxFileTexture::eRepeat:
return"REPEAT";
case FbxFileTexture::eClamp:
return"CLAMP";
default:
return"UNKNOWN";
}
}
void Model::writeBinary(FILE* file)
{
if(exportPart == EXPORT_PART_ALL || exportPart == EXPORT_PART_MODEL)
{
std::list<std::string> _bonenames;
for (std::vector<Node *>::const_iterator itr = nodes.begin(); itr != nodes.end(); ++itr)
{
(*itr)->loadBoneNames(_bonenames);
}
for (std::vector<Node *>::const_iterator itr = nodes.begin(); itr != nodes.end(); ++itr)
{
bool skeleton=false;
(*itr)->checkIsSkeleton(skeleton,_bonenames);
(*itr)->setSkeleton(skeleton);
}
unsigned int size = meshes.size();
if(size>0)
{
meshes[0]->object.fPosition = ftell(file);
}
write(size, file);
// 写入网格
for(auto itr = meshes.begin(); itr != meshes.end(); itr++)
{
(*itr)->writeBinary(file);
}

//写入材质
size = materials.size();
if(size>0)
{
materials[0]->object.fPosition = ftell(file);
}
write(size, file);
for(auto itr = materials.begin(); itr != materials.end(); itr++)
{
(*itr)->writeBinary(file);
}
// 结点数量
size = nodes.size();
if(size>0)
{
nodes[0]->object.fPosition = ftell(file);
}
write(size, file);
for(auto itr = nodes.begin(); itr != nodes.end(); itr++)
{
(*itr)->writeBinary(file);
}
}

// 骨骼信息
if (exportPart == EXPORT_PART_ALL || exportPart == EXPORT_PART_ANIMATION)
{
for(auto itr : animations)
{
itr->object.fPosition = ftell(file);
itr->writeBinary(file);
}
}
}

void Mesh::writeBinary(FILE* file)
{
// 属性
attributes.writeBinary(file);

// 写入顶点
if(vertices.size() >0)
{
unsignedint size = vertices.size();
write(size, file);
write(&vertices[0],size,file);
}
else
{
write((unsignedint)0, file);
}

// 写入模型部分
unsignedint size = parts.size();
write(size, file);
for(auto itr = parts.begin(); itr != parts.end(); itr++)
{
(*itr)->writeBinary(file);
}
}
void MeshPart::writeBinary(FILE* file)
{
write(id, file);
// 索引大小
unsignedint size = indices.size();
write(size, file);
// 索引
for(auto itr1 = indices.begin(); itr1 != indices.end(); itr1++)
write(*itr1,file);
//aabb碰撞体
write(aabb, 6, file);
}
void Attributes::writeBinary(FILE* file)
{

std::vector<MeshVertexAttrib> attribs;
MeshVertexAttrib attrib;
for (unsignedint i = 0; i <length(); i++)
{
std::string key = name(i);
attrib = attributemap.find(key)->second;
attribs.push_back(attrib);
if(key == "VERTEX_ATTRIB_BLEND_INDEX")
{
break;
}
}
unsigned int size = attribs.size();
write(size, file);
for( int i = 0 ; i < size ; i++ )
{
write(attribs[i].size, file);
write(attribs[i].type, file);
write(attribs[i].name, file);
}
}

void Material::writeBinary(FILE* file)
{
write(id, file);
write(diffuse.value, 3, file);
write(ambient.value, 3, file);
write(emissive.value, 3, file);
write(opacity.value,file);
write(specular.value, 3, file);
write(shininess.value,file);
unsigned int size = textures.size();
write(size, file);
for(auto itr = textures.begin(); itr != textures.end(); itr++)
{
write((*itr)->id, file);
write((*itr)->path, file);
write((*itr)->uvTranslation, 2, file);
write((*itr)->uvScale, 2, file);
std::string wrapModeU=getWrapModeUseString((*itr)->wrapModeU);
std::string wrapModeV=getWrapModeUseString((*itr)->wrapModeV);
std::string type= getTextureUseString((*itr)->usage);
write(type,file);
write(wrapModeU,file);
write(wrapModeV,file);
}
}

void Node::writeBinary(FILE* file)
{
write(id, file);
write(_skeleton, file);
// 旋转,缩放,变换
write(transforms, 16, file);
// 结点部分
unsigned int partsSize = parts.size();
write(partsSize,file);
if(parts.size()>0)
{
for(int i = 0 ; i <parts.size() ; i++ )
{
NodePart* nodepart = parts[i];
if(nodepart)
{
if(nodepart->meshPart)
{
//网格部分id
write(nodepart->meshPart->id, file);
}
else
{
write("null", file);
}
//材质id
if(nodepart->material)
{
write(nodepart->material->id, file);
}
else
{
write("null", file);
}
// 骨骼数量
unsigned int size = nodepart->bones.size();
write(size, file);
for(auto itr = nodepart->bones.begin(); itr != nodepart->bones.end(); itr++)
{
// 写入名字
write(itr->first->id, file);
// 写入转换
float tmp[16];
for(int i = 0; i <4; i++)
{
for(int j = 0; j <4; j++)
{
tmp[i*4 + j] = itr->second.Double44()[i][j];
}
}
write(tmp, 16, file);
}

size = nodepart->uvMapping.size();
write(size, file);
for(auto itr = nodepart->uvMapping.begin(); itr != nodepart->uvMapping.end(); itr++)
{
unsigned int size = itr->size();
write(size, file);
//纹理索引
for (auto tt = (*itr).begin(); tt != (*itr).end(); ++tt)
{
unsigned int index = nodepart->material->getTextureIndex(*tt);
write(index, file);
}
}
}
}
}
// 孩子结点
unsigned int childrenSize = children.size();
write(childrenSize,file);
for(auto itr = children.begin(); itr != children.end(); itr++)
{
Node* node = *itr;
if(node)
{
node->writeBinary(file);
}
}
}
void Animation::writeBinary(FILE* file)
{
write(id, file);
write(length, file);
write(static_cast<unsignedint>(nodeAnimations.size()), file);

for(auto itr = nodeAnimations.begin(); itr != nodeAnimations.end(); itr++)
{
NodeAnimation* nodeanim = *itr;
write(nodeanim->node->id, file);

write(static_cast<unsigned int>(nodeanim->keyframes.size()), file);

for(auto itr1 = nodeanim->keyframes.begin(); itr1 != nodeanim->keyframes.end(); itr1++)
{
Keyframe* keyframe = *itr1;

// 写入帧动画时间
write(keyframe->time, file);

// 写入转换标记
unsigned char transformflag(0);
if (keyframe->hasRotation)
transformflag |= 0x01;
if (keyframe->hasScale)
transformflag |= (0x01<<1);
if (keyframe->hasTranslation)
transformflag |= (0x01<<2);

write(transformflag, file);

// 写入旋转数值
if (keyframe->hasRotation)
write(keyframe->rotation, 4, file);

// 写入缩放数值
if (keyframe->hasScale)
write(keyframe->scale, 3, file);

// 写入转换数值
if (keyframe->hasTranslation)
write(keyframe->translation, 3, file);
}
}
}
}
}

文件写入的操作也有了,后面就需要自己封装一个具体实现的类用于文件的保存,这个实现起来比较简单,头文件的完整代码如下所示:#ifndef _GPBFILE_H_
#define _GPBFILE_H_

#include "FileIO.h"
#include "Model.h"
#include "ReferenceTable.h"

namespace fbxconv {

class C3BFile
{
public:
// 构造函数
C3BFile(void);

~C3BFile(void);

// 析构函数
bool saveBinary(conststd::string& filepath);

void addToRefTable(ObjRef* obj);

void AddModel(modeldata::Model* model);

private:
FILE* _file;
modeldata::Model* _models;
ReferenceTable _refTable;

};
}

#endif

对应的源文件完整代码实现如下所示:#include "C3BFile.h"
#include "Animation.h"
#include "Model.h"
namespace fbxconv{
using namespace modeldata;
unsigned char GPB_VERSION[2] ={VERSION_HI, VERSION_LO};
C3BFile::C3BFile(void)
:_file(NULL)
{

}

C3BFile::~C3BFile(void)
{

}

bool C3BFile::saveBinary(const std::string& filepath)
{
_file = fopen(filepath.c_str(), "w+b");

//文件加密标识
char identifier[] = {'C','3','B','\0'};

fwrite(identifier, 1, sizeof(identifier), _file);

//版本号
fwrite(GPB_VERSION, 1, sizeof(GPB_VERSION), _file);

_refTable.writeBinary(_file);

if(_models)
{
_models->writeBinary(_file);
}

_refTable.updateOffset(_file);

fclose(_file);
returntrue;
}

void C3BFile::addToRefTable(ObjRef* obj)
{
if(obj)
{
std::string&id = obj->id;
if(id.length()>0)
{
if(_refTable.get(id)== NULL)
{
_refTable.add(id, obj);
}
}
}
}

void C3BFile::AddModel(modeldata::Model*model)
{
_models = model;

//增加模型
if(model->meshes.size()>0)
{
Mesh* mesh = model->meshes[0];
addToRefTable(mesh->GetObj());
}
//增加材质
if(model->materials.size()>0)
{
Material* mat = model->materials[0];
addToRefTable(mat->GetObj());
}
//增加结点
if(model->nodes.size()>0)
{
Node* node = model->nodes[0];
addToRefTable(node->GetObj());
}

// 增加动画
if(model->animations.size()>0)
{
for (int i= 0; i < model->animations.size(); i++)
{
Animation* anim = model->animations[i];
addToRefTable(anim->GetObj());
}
}

}

}

 另外其他类的实现比如:Animation,Attribute,Keyframe,Material,Mesh,MeshPart,Model,Node等等,这里就不一一列举了。后续讲解敬请期待。。。。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息