内容简介:Cocos2d-x 3.x 图形学渲染系列二十七
笔者介绍: 姜雪伟 , IT 公司技术合伙人, IT 高级讲师, CSDN 社区专家,特邀编辑,畅销书作者,国家专利发明人 ; 已出版书籍:《手把手教你 架构 3D 游戏引擎》电子工业出版社 和《 Unity3D 实战核心技术详解》电子工业出版社等。
CSDN视频网址: http://edu.csdn.net/lecturer/144
笔者以前在游戏公司开发大型MMOARPG游戏时,游戏中使用的模型为了防止产品发布后被破解,程序再做模型导出插件时对模型进行了加密处理。针对模型加密的方式非常多,通常的做法是通过已经编写的模型插件方式对其加密,类似AutoDesk提供的FBX模型插件,它的内容是二进制的。在Cocos2d-x引擎中的模型也做了加密处理,它就是c3b模型文件。模型实现过程是通过工具fbx-conv转化FBX模型文件得到的,这说明模型之间是相通的。c3b模型文件也是二进制的,本章给读者解密Cocos2d-x引擎使用的c3b模型文件如何生成二进制加密格式的。
本节真正涉及到模型文件的加密处理,加密的处理方法非常多:一种是对其内容进行二进制加密;还有就是对模型文件开头、中间、结尾等增加二进制标示加密。c3b模型本身是二进制的,然后再在文件开头处进行文件标示加密,加密方式是通过自定义的字符串,在Cocos2d-x引擎中可查看到读取c3b模型文件代码段,截取引擎中加密的代码段如下所示:
// 读取文件标示 char identifier[] = { 'C', '3', 'B', '\0'}; char sig[4]; if (_binaryReader.read(sig, 1, 4) != 4 || memcmp(sig, identifier, 4) != 0) { clear(); CCLOG("warning: Invalid identifier: %s", path.c_str()); return false; }
这段代码作用是加载模型时需要验证的加密标示,现在打开一个c3b模型文件给读者展示就一目了然了。文件内容如下图:
在第一行有三个字母“C3B”,这就是在模型中加的加密代码标示,加密的字符串,是通过工具fbx-conv转化时生成的,下面给读者进行加密模块的代码编写。
下面开始Cocos2d-x引擎加密 工具 的编写,相对来说这个模块架构比较简单,整个工程代码存放网址是:
https://github.com/cocos2d-x/fbx-conv ,代码架构图如下:
除了上图所列的模块外,Fbx-conv还包括三个子模块:FbxConv模块、FbxConvCommand模块、Setting模块,三个子模块的功能是围绕工具的界面设置的,读者首先打开Cocos2d-x引擎中的tools文件夹,在tools文件夹下有fbx-conv文件夹在该文件夹中找到fbx-conv工具,接下来运行fbx-conv工具,首先在Windows中打开“运行”窗口并且输入cmd,然后把fbx-conv.exe拖入进去,点击回车键,效果如下窗口:
另外在Mac系统,打开终端,把fbx-conv执行程序拖到其中,按回车键,显示效果如下:
可以看到两个窗口中都出现了许多命令行,这些命令行是在一个类里面通过代码设置的,完整代码如下所示:
#ifndef FBXCONVCOMMAND_H #define FBXCONVCOMMAND_H //#define ALLOW_INPUT_TYPE #include "Settings.h" #include <string> #include "log/log.h" namespace fbxconv { struct FbxConvCommand { constint argc; constchar **argv; int error; bool help; Settings *settings; log::Log *log; FbxConvCommand(log::Log *log, constint&argc, constchar** argv, Settings *settings) : log(log), argc(argc), argv(argv), settings(settings), error(log::iNoError) { help = (argc <= 1); settings->flipV = false; settings->packColors = false; settings->verbose = false; settings->maxNodePartBonesCount = 40; settings->maxVertexBonesCount = 4; settings->forceMaxVertexBoneCount = true; settings->maxVertexCount = (1<<15)-1; settings->maxIndexCount = (1<<15)-1; settings->outType = FILETYPE_C3B; settings->inType = FILETYPE_AUTO; settings->needReusableMesh = true; settings->normalizeVertexNormal = false; settings->exportPart = EXPORT_PART_ALL; settings->normalMap = false; settings->compressLevel = COMPRESS_LEVEL_DEFAULT; for (int i = 1; i < argc; i++) { constchar *arg = argv[i]; constint len = (int)strlen(arg); if (len >1&& arg[0] == '-') { if (arg[1] == '?') help = true; elseif (arg[1] == 'f') settings->flipV = true; elseif (arg[1] == 'v') settings->verbose = true; elseif (arg[1] == 'g') settings->needReusableMesh = false; elseif (arg[1] == 'r') settings->normalizeVertexNormal = true; elseif ((arg[1] == 'n') && (i + 1< argc)) settings->maxNodePartBonesCount = atoi(argv[++i]); elseif ((arg[1] == 'm') && (i + 1< argc)) settings->maxVertexCount = settings->maxIndexCount = atoi(argv[++i]); elseif((arg[1] == 'c') && (i + 1< argc)) settings->compressLevel = (COMPRESS_LEVEL)atoi(argv[++i]); elseif(arg[1] == 'b') settings->outType = FILETYPE_C3B; elseif(arg[1] == 't') settings->outType = FILETYPE_C3T; elseif(arg[1] == 'a') settings->outType = FILETYPE_ALL; elseif(arg[1] == 'l') settings->exportPart = EXPORT_PART_MODEL; elseif(arg[1] == 'j') settings->exportPart = EXPORT_PART_ANIMATION; elseif(arg[1] == 'p') settings->normalMap = true; else log->error(error = log::eCommandLineUnknownOption, arg); } elseif (settings->inFile.length() <1) settings->inFile = arg; elseif (settings->outFile.length() <1) settings->outFile = arg; else log->error(error = log::eCommandLineUnknownArgument, arg); if (error != log::iNoError) break; } if (!help&&error == log::iNoError) validate(); } void printCommand() const { for (int i = 1; i <argc; i++) { if (i >1) printf(" "); printf(argv[i]); } printf("\n"); } void printHelp() const { printf("\nUsage: fbx-conv [options] <input>\n"); printf("\nExample: fbx-conv -a xx.fbx \n"); printf("\n"); printf("Options:\n"); printf("-? : Display this help information.\n"); printf("-f : Flip the V texture coordinates.\n"); printf("-p : Export tagent binormal data.\n"); printf("-m <size>: The maximum amount of vertices or indices a mesh may contain (default: 32k)\n"); printf("-n <size>: The maximum amount of bones a nodepart can contain (default: 40)\n"); printf("-v : Verbose: print additional progress information\n"); printf("-g : Merge the mesh which have same vertex attribute\n"); printf("-a : Export l3b(binary) and c3t(text)\n"); printf("-b : Export l3b(binary)\n"); printf("-t : Export c3t(text)\n"); printf("-c <size>: The compression level: 0 , 1 (default: 0)\n"); printf("-l : Export model data only.\n"); printf("-j : Export animation data only.\n"); printf("\n"); printf("<input> : The filename of the file to convert.\n"); } private: void validate() { if (settings->inFile.empty()) { log->error(error = log::eCommandLineMissingInputFile); return; } #ifdef ALLOW_INPUT_TYPE if (inType == FILETYPE_AUTO) inType = guessType(inFile, FILETYPE_IN_DEFAULT); #else settings->inType = FILETYPE_IN_DEFAULT; #endif if (settings->outFile.empty()) setExtension( settings->outFile = settings->inFile, settings->outType = (settings->outType == FILETYPE_AUTO ? FILETYPE_OUT_DEFAULT : settings->outType)); elseif (settings->outType == FILETYPE_AUTO) settings->outType = guessType(settings->outFile); if (settings->maxVertexBonesCount<0 || settings->maxVertexBonesCount>8) { log->error(error = log::eCommandLineInvalidVertexWeight); return; } if (settings->maxNodePartBonesCount<settings->maxVertexBonesCount) { log->error(error = log::eCommandLineInvalidBoneCount); return; } if (settings->maxVertexCount<0 || settings->maxVertexCount> (1<<15)-1) { log->error(error = log::eCommandLineInvalidVertexCount); return; } if (settings->compressLevel<COMPRESS_LEVEL_DEFAULT || settings->compressLevel>= COMPRESS_LEVEL_NUM) { log->error(error = log::eCommandLineUnknownCompressLevel, (COMPRESS_LEVEL_NUM - 1)); return; } } int parseType(constchar* arg, constint&def = -1) { if (stricmp(arg, "fbx")==0) returnFILETYPE_FBX; elseif (stricmp(arg, "g3db")==0) returnFILETYPE_G3DB; elseif (stricmp(arg, "g3dj")==0) returnFILETYPE_G3DJ; elseif (stricmp(arg, "c3b")==0) returnFILETYPE_G3DB; elseif (stricmp(arg, "c3t")==0) returnFILETYPE_G3DJ; if (def <0) log->error(error = log::eCommandLineUnknownFiletype, arg); return def; } int guessType(conststd::string&fn, constint&def = -1) { int o = (int)fn.find_last_of('.'); if (o == std::string::npos) return def; std::string ext = fn.substr(++o, fn.length() - o); returnparseType(ext.c_str(), def); } void setExtension(std::string&fn, conststd::string&ext) const { int o = (int)fn.find_last_of('.'); if (o == std::string::npos) fn += "." + ext; else fn = fn.substr(0, ++o) + ext; } void setExtension(std::string&fn, constint&type) const { switch(type) { caseFILETYPE_FBX: returnsetExtension(fn, "fbx"); caseFILETYPE_G3DB: returnsetExtension(fn, "c3b"); caseFILETYPE_G3DJ: returnsetExtension(fn, "c3t"); default: returnsetExtension(fn, ""); } } }; }
这个类主要作用是提供操作命令行设置,用户根据这些设置导出含有不同文件内容的模型文件,比如如果在模型中增加切向量坐标和法线坐标,可以通过在命令行中加入-p指令即可。接下来就是在窗口根据不同的指令执行不同的操作了,通过命令行窗口也可以完成FBX模型的加载以及c3b模型的转化保存,同时可以输出操作的日志信息,完整代码如下所示:
#ifdef _MSC_VER #pragma once #endif //_MSC_VER #ifndef FBXCONV_FBXCONV_H #define FBXCONV_FBXCONV_H #ifndef BUILD_NUMBER #define BUILD_NUMBER "0000" #endif #ifndef BUILD_ID #ifdef DEBUG #define BUILD_ID "debug version, cocos2d-x-3.4-beta0 or later version can use" #else #define BUILD_ID "release version, cocos2d-x-3.4-beta0 or later version can use" #endif #endif #define BIT_COUNT (sizeof(void*)*8) #include "log/messages.h" #include "modeldata/Model.h" #include "Settings.h" #include "readers/Reader.h" #include "FbxConvCommand.h" #include "json/JSONWriter.h" #include "json/UBJSONWriter.h" #include "readers/FbxConverter.h" #include "modeldata/C3BFile.h" namespace fbxconv { void simpleTextureCallback(std::map<std::string, readers::TextureFileInfo>&textures) { for (std::map<std::string, readers::TextureFileInfo>::iterator it = textures.begin(); it != textures.end(); ++it) { it->second.path = it->first.substr(it->first.find_last_of("/\\")+1); } } class FbxConv { public: fbxconv::log::Log *log; FbxConv(fbxconv::log::Log *log) : log(log) { log->info(log::iNameAndVersion, modeldata::VERSION_HI, modeldata::VERSION_LO, /*BUILD_NUMBER,*/BIT_COUNT, BUILD_ID); } constchar *getVersionString() { returnlog->format(log::iVersion, modeldata::VERSION_HI, modeldata::VERSION_LO, BUILD_NUMBER, BIT_COUNT, BUILD_ID); } constchar *getNameAndVersionString() { returnlog->format(log::iNameAndVersion, modeldata::VERSION_HI, modeldata::VERSION_LO, /*BUILD_NUMBER,*/BIT_COUNT, BUILD_ID); } bool execute(intconst&argc, constchar** const&argv) { Settings settings; FbxConvCommand command(log, argc, argv, &settings); #ifdef DEBUG log->filter |= log::Log::LOG_DEBUG; #else log->filter &= ~log::Log::LOG_DEBUG; #endif if (settings.verbose) log->filter |= log::Log::LOG_VERBOSE; else log->filter&= ~log::Log::LOG_VERBOSE; if (command.error != log::iNoError) command.printCommand(); elseif (!command.help) returnexecute(&settings); command.printHelp(); returnfalse; } bool execute(Settings * const&settings) { bool result = false; modeldata::Model *model = newmodeldata::Model(); if (load(settings, model)) { if (settings->verbose) info(model); if (save(settings, model)) result = true; } delete model; return result; } readers::Reader *createReader(constSettings * const&settings) { returncreateReader(settings->inType); } readers::Reader *createReader(constint&type) { switch(type) { caseFILETYPE_FBX: returnnewreaders::FbxConverter(log, simpleTextureCallback); caseFILETYPE_G3DB: caseFILETYPE_G3DJ: default: log->error(log::eSourceLoadFiletypeUnknown); return0; } } bool load(Settings * const&settings, modeldata::Model *model) { log->status(log::sSourceLoad); readers::Reader *reader = createReader(settings); if (!reader) returnfalse; bool result = reader->load(settings); if (!result) log->error(log::eSourceLoadGeneral, settings->inFile.c_str()); else { result = reader->convert(settings, model); log->status(log::sSourceConvert); } log->status(log::sSourceClose); delete reader; return result; } bool save(Settings * const&settings, modeldata::Model *model) { bool result = false; json::BaseJSONWriter *jsonWriter = 0; model->exportPart = settings->exportPart; if(settings->outType == FILETYPE_ALL || settings->outType == FILETYPE_C3T) { std::stringout = settings->outFile; int o = out.find_last_of("."); out = out.substr(0, o+1) + "c3t"; std::ofstream myfile; myfile.open (out.c_str(), std::ios::binary); log->status(log::sExportToG3DJ, out.c_str()); jsonWriter = newjson::JSONWriter(myfile); (*jsonWriter) << model; delete jsonWriter; result = true; myfile.close(); } if(settings->outType == FILETYPE_ALL || settings->outType == FILETYPE_C3B) { std::stringout = settings->outFile; int o = out.find_last_of("."); out = out.substr(0, o+1) + "c3b"; C3BFile file; file.AddModel(model); file.saveBinary(out); log->status(log::sExportToG3DB, out.c_str()); } log->status(log::sExportClose); return result; } void info(modeldata::Model *model) { if (!model) log->verbose(log::iModelInfoNull); else { log->verbose(log::iModelInfoStart); log->verbose(log::iModelInfoID, model->id.c_str()); log->verbose(log::iModelInfoVersion, model->version[0], model->version[1]); log->verbose(log::iModelInfoMeshesSummary, model->meshes.size(), model->getTotalVertexCount(), model->getMeshpartCount(), model->getTotalIndexCount()); log->verbose(log::iModelInfoNodesSummary, model->nodes.size(), model->getTotalNodeCount(), model->getTotalNodePartCount()); log->verbose(log::iModelInfoMaterialsSummary, model->materials.size(), model->getTotalTextureCount()); } } }; }
上述代码中提供了load函数用于加载模型文件和save函数用于保存模型信息,这两个函数是非常重要的。不同的模型类型表示利用宏定义实现的,模型文件格式定义如下:
#define FILETYPE_AUTO 0x00 #define FILETYPE_FBX 0x10 #define FILETYPE_G3DB 0x20 #define FILETYPE_G3DJ 0x21 #define FILETYPE_OUT_DEFAULT FILETYPE_G3DB #define FILETYPE_IN_DEFAULT FILETYPE_FBX #define FILETYPE_C3B 0X30 #define FILETYPE_C3T 0X31
定义文件格式的作用是方便程序扩展,json模块和log模块,这两个模块对读者来说比较熟悉,这里就不把代码列出来了,只把文件目录给读者看一下,它主要实现的功能是json文件的读写和转换模型时log日志的打印,代码目录结构如下图:
下面介绍在模型转换时最重要的模块modeldata,它主要包括FBX SDK,Cocos2d-x引擎的原始模型是FBX,FBX模型格式是通过工具转化而成的,它是Max自带的插件,其代码可以从如下网址: http://usa.autodesk.com/adsk/servlet/pc/item?siteID=123112&id=10775847 下载程序所需要的SDK,SDK代码目录如下图:
FBX的SDK支持window系统,mac系统,linux系统并且为这些操作系统都提供了相应版本。因为fbx-conv工具需要加载FBX模型,需要下载到fbx sdk代码。下面开始将FBX转化后的c3b二进制文件,也就是写入类的创建,将二进制文件写入流的头文件完整代码如下所示:
#ifndef FILEIO_H_ #define FILEIO_H_ #include <cstdio> #include <list> #include <vector> #include <stdio.h> #include <string> namespace fbxconv { /** * 写二进制数据到指定的数据流. */ void write(unsignedchar value, FILE* file); void write(char value, FILE* file); void write(constchar* str, FILE* file); void write(unsignedint value, FILE* file); void write(unsignedshort value, FILE* file); void write(bool value, FILE* file); void write(float value, FILE* file); void write(constfloat* values, int length, FILE* file); /** *把字符串和字符串的长度写入到二进制文件流 */ void write(conststd::string& str, FILE* file); void writeZero(FILE* file); void skipString(FILE* file); void skipUint(FILE* file); } #endif 对应的完整源文件实现如下所示: #include "FileIO.h" #include <cassert> namespace fbxconv { void write(unsignedchar value, FILE* file) { size_t r = fwrite(&value, sizeof(unsignedchar), 1, file); assert(r == 1); } void write(char value, FILE* file) { size_t r = fwrite(&value, sizeof(char), 1, file); assert(r == 1); } void write(constchar* str, FILE* file) { size_t length = strlen(str); size_t r = fwrite(str, 1, length, file); assert(r == length); } void write(unsignedint value, FILE* file) { size_t r = fwrite(&value, sizeof(unsignedint), 1, file); assert(r == 1); } void write(unsignedshort value, FILE* file) { size_t r = fwrite(&value, sizeof(unsignedshort), 1, file); assert(r == 1); } void write(bool value, FILE* file) { // 把布尔值写成一个无符号字符 unsignedchar b = value; write(b, file); } void write(float value, FILE* file) { fwrite(&value, sizeof(float), 1, file); } void write(constfloat* values, int length, FILE* file) { fwrite(values, sizeof(float), length, file); } void write(conststd::string& str, FILE* file) { // 写字符串长度 write((unsignedint)str.size(), file); if (str.size() >0) { write(str.c_str(), file); } } void writeZero(FILE* file) { write((unsignedint)0, file); } void skipString(FILE* file) { // 获取字符数组大小 unsignedint length = 0; fread(&length, sizeof(unsignedint), 1, file); if (length >0) { //跳过字符数组 long seek = (long)(length * sizeof(char)); fseek(file, seek, SEEK_CUR); } } void skipUint(FILE* file) { fseek(file, sizeof(unsignedint), SEEK_CUR); } }
接下来读取FBX模型文件信息,会在下一个系列中继续介绍。。。。。。。。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- CubeEngine v0.3B 更新,图形渲染引擎
- TensorFlow也可以做图形渲染了:当神经网络遇上计算机图形学
- Basemark推出Rocksolid图形渲染解决方案
- Ogre 1.12.3 发布,三维图形渲染引擎
- Rust 移动端跨平台复杂图形渲染项目开发系列总结(目录)
- Cocos2d-x 3.x 图形学渲染系列总结
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
统计自然语言处理基础
Chris Manning、Hinrich Schütze / 苑春法、李伟、李庆中 / 电子工业出版社 / 2005-1 / 55.00元
《统计自然语言处理基础:国外计算机科学教材系列》是一本全面系统地介绍统计自然语言处理技术的专著,被国内外许多所著名大学选为计算语言学相关课程的教材。《统计自然语言处理基础:国外计算机科学教材系列》涵盖的内容十分广泛,分为四个部分,共16章,包括了构建自然语言处理软件工具将用到的几乎所有理论和算法。全书的论述过程由浅入深,从数学基础到精确的理论算法,从简单的词法分析到复杂的语法分析,适合不同水平的读......一起来看看 《统计自然语言处理基础》 这本书的介绍吧!