Cocos2d-x 3.x 图形学渲染系列二十七
Posted 海洋_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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,代码架构图如下:可以看到两个窗口中都出现了许多命令行,这些命令行是在一个类里面通过代码设置的,完整代码如下所示:
#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日志的打印,代码目录结构如下图:
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模型文件信息,会在下一个系列中继续介绍。。。。。。。。
以上是关于Cocos2d-x 3.x 图形学渲染系列二十七的主要内容,如果未能解决你的问题,请参考以下文章