原文链接:http://liweizhaolili.blog.163.com/blog/static/162307442013111592849927/

下面来介绍一下导出插件的具体写法。不过这不是一个容易说的很详细的问题。因为我们要写导出插件,通常都是因为想根据自己想要的信息来导出,所以就算我把我整个工程都公开,意义也不大的,因为那是根据我自己需要的数据写的业务,估计不太可能和你想要的一样的。所以我也只能简单的说明一些几个关键获取数据的方法,和保存文件的方法,如果想看具体的导出范例工程,你可以去maxSdk文件夹下面找到maxsdk\samples\import_export\3dsexp.cpp,这是一个完整的导出3ds文件的例子。
然后maxSdk提供的操作数据的方法有两种,一种是常规方法,一种是IGame方法。下面我们先来介绍一下常规的方法,至于IGame的方法,我会在另外一遍文章里面说明:http://liweizhaolili.blog.163.com/blog/static/16230744201311219926255/
我建议先看看常规方法,对maxSdk有一个了解,然后我在介绍IGame的时候,会将两种方法做一个对比。
一、数据获取
首先要知道的是,maxSdk是通过一个ITreeEnumProc类来遍历场景里面所有的节点的。
然后需要知道,在场景里面,每一个物体就是一个节点,包括网格模型、灯光、摄像机、骨骼、辅助物体等,都是节点。我们可以通过获取节点的ClassID来判断该节点的实际类型。
最后,我们需要知道导出整个场景和导出选择中的物体的区别。这个并不是自动功能来的,也是需要自己来写代码判断的。
我这里举的例子比较简单,大家可以自己扩展。
先讲讲判断导出整个场景和导出选择中物体的判断方法:
我们先定义一个布尔变量,用作判断导出的模型,比如
static BOOL exportSelected;
然后,在DoExport方法里面,我们判断:
exportSelected = (options & SCENE_EXPORT_SELECTED) ? TRUE : FALSE;
如果是true,那就是导出单独选择的物体,如果是false,就是导出全部物体了。
最后,我们可以在节点树回调方法里面,判断,如果
if(exportSelected && node->Selected() == FALSE)这说明了开启了导出选择物体的模式,并且当前的节点没有被选中,我们直接return掉就行了。
下面正式开始导出数据:
1、写一个管理树节点的类,并写回调的方法
class MyTreeEnum : public ITreeEnumProc
{
public:
int callback(INode*node);
};
int MyTreeEnum::callback(INode*node)
{
//判断是否导出选中的物体
if(exportSelected && node->Selected() == FALSE)
return TREE_CONTINUE;
ObjectState os = node->EvalWorldState(0);
if(os.obj)
{
if(os.obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID,0)))
{
//这个节点是网格模型,根据需要对数据进行操作
return TREE_CONTINUE;
}
else
{
switch (os.obj->SuperClassID()) 
 { 
 case CAMERA_CLASS_ID: 
 //这个节点是摄像机,根据需要对数据进行操作
 break; 
 case LIGHT_CLASS_ID: 
  //这个节点是灯光,根据需要对数据进行操作
 break; 
  default:
 break; 
 }
}
}
上面是一个简单判断当前节点是什么类型的方法,我们还可以根据自己的需要,写一些判断某种单独类型的方法,比如判断一个节点是否为骨骼,可以这样写:
bool isBone(INode*thisBone)
{
ObjectState pObs=thisBone->EvalWorldState(0);
Class_ID id = pObs.obj->ClassID();
SClass_ID sid = pObs.obj->SuperClassID();
if (pObs.obj->ClassID()==Class_ID(BONE_CLASS_ID,0))
{
return true; 
}
if (pObs.obj->ClassID()==BONE_OBJ_CLASSID)
{
return true; 
}
if (pObs.obj->ClassID()==Class_ID(37157,0))
{
return true; 
}
return false;
}
最后在DoExport方法里面
MyTreeEnum tempProc;
ei->theScene->EnumTree(&tempProc);
这样程序就会遍历所有的节点,然后做回调时的处理。
值得注意的问题是:
maxSdk里面的BONE_CLASS_ID或者BONE_OBJ_CLASSID,指的都是3dsmax的经典骨骼,也就是bones,不包括biped的。所以如果只用这两个ClassId来判断骨骼,是不行的,会把biped漏掉。阿赵我自己通过断点找出biped的ClassId是37157,但找不到sdk里面对应的类ID,所以只能直接ClassID()==Class_ID(37157,0)来判断它就是biped骨骼了。
上面这个判断也不是绝对的,比如有些动画师喜欢拿辅助物体作为骨骼来控制动画,那么这个判断骨骼的方法里面,就需要加上DUMMY_CLASS_ID的判断了,诸如此类,各位可以根据自己的业务去扩展。
2、获取网格模型信息:
刚才我们已经能判断到某个节点是网格模型了,所以我们接下来就可以对其进行信息的获取。
获取节点的名称:
node->GetName();
获取网格模型的具体网格:
TriObject* tri = (TriObject*)os.obj->ConvertToType(0, Class_ID(TRIOBJ_CLASS_ID,0)); 
Mesh *pMesh = &tri->GetMesh();
获取网格的点的数量:
int VerticesNum = pMesh->getNumVerts();
获取网格面的数量:
 int FaceNum = pMesh->getNumFaces();
遍历所有的面,然后获取每个面的顶点索引:
for(i = 0;i<FaceNum;i++)
 { 
 Face face = pMesh->faces[i]; 
//自己写个数组把它们存起来
 indexList.push_back(face.v[0]); 
 indexList.push_back(face.v[1]); 
 indexList.push_back(face.v[2]); 
}
遍历所有顶点,获取顶点坐标:
for(i = 0;i<VerticesNum;i++)
Vertex_t vertInfo; 
int FaceNumber;
Point3 pos = pMesh->getVert(i);
//自己写个数组把它们存起来
vertList.push_back(pos); 
}
//uv坐标
for(i = 0;i<FaceNum;i++)
{
TVFace tvFace = pMesh->tvFace[i];
for (int k=0; k<3; k++)  
{
Point3 uv = pMesh->tVerts[tvFace.t[k]];
//自己写个数组存起来
uvList.push_back(uv); 
}
}
这样,网格模型需要的顶点坐标、索引、uv都有了,如果还需要法线或者其他信息,自己可以扩展。
3、获取蒙皮修改器:
对于已经蒙皮的模型,我们需要查找它的蒙皮修改器(这里用的是skin修改器)
ISkin * FindSkinModifier(INode *pINode)
Object * pObject = pINode->GetObjectRef(); 
if(pObject == 0) return 0; 
// 循环检测所有的DerivedObject 
while(pObject->SuperClassID() == GEN_DERIVOB_CLASS_ID){ 
IDerivedObject * pDerivedObject = static_cast<IDerivedObject *>(pObject); 
for(int stackId = 0; stackId < pDerivedObject->NumModifiers(); stackId++){ 
Modifier * pModifier = pDerivedObject->GetModifier(stackId); 
//检测ClassID是不是Skin修改器
if(pModifier->ClassID() == SKIN_CLASSID) { return (ISkin*)pModifier->GetInterface(I_SKIN);} 
pObject = pDerivedObject->GetObjRef();//下一个Derived Object 
return 0; 
}
在得到了有蒙皮信息之后,就可以获取它上面的蒙皮信息数据:
ISkinContextData* pSkinCtx = skin->GetContextInterface(node);
int nBones = pSkinCtx->GetNumAssignedBones(i);
for (j = 0;j<nBones;j++)
{
INode *pBone = skin->GetBone(j);
//骨骼名称
char* bName = pBone->GetName();
//权重
float weight = pSkinCtx->GetBoneWeight(i,j);
//其他信息可以自己去扩展
//……自己想办法存起来
}
我建议在获取蒙皮信息之前,最好先遍历所以节点把骨骼全部找出来,并排好序。因为我们最后保存的蒙皮信息里面的骨骼名称,我们可以变成骨骼的索引,包括骨骼父物体也是保存成索引,那么文件的容量会减少很多。
以上我是简单的说明了几种常用的信息的获取方法,在完全理解了之后,其他的信息的获取方法是一样的,具体可以查找一下maxSdk的api文档
二、保存文件
获取好数据之后,最后我们就要来保存了。很多人以为这个保存也是maxSdk提供的方法,其实不是的,保存文件,就是直接用c++自己的方法。比如我们可以用ofstream来保存。我们可以把之前获得的所有数据,都集中的转换成字符串,比如json,或者自己觉得解析起来比较方便的格式的字符串,然后就可以用ofstream来保存了。
具体的保存路径,是DoExport的传入参数name。

3DMAX导出插件编写(续)相关推荐

  1. 详细讲解3DMAX导出插件-tiamo

    转自:详细讲解3DMAX导出插件-tiamo 3dmax的导出插件是用来把做好的3d模型导出成自己引擎需要的格式的一个dll,它由3dmax加载调用.具体怎样去写一个插件,小T不多说,在3dmax的s ...

  2. 3ds max sdk导出插件编写的心得

    3ds max sdk导出插件编写的心得 作者:yhchinabest 来自:CG先生-3D图形插件开发网http://www.cgsir.com 写在前面 为什么要写这个心得?去年11月份的时候我写 ...

  3. 3D MAX导出插件编写

    文章版权归博客园 BigCoder所有,转载请于明显位置标明原文作者及出处,以示尊重!! 原文出处:http://www.cnblogs.com/csyisong/archive/2009/09/01 ...

  4. 3D MAX导出插件编写I

    3D MAX导出插件编写I 想想研究3D MAX 的SDK已经有了不短的时间,真正算起来也有两个月了吧,但是讲到收获,确实不大.作为一个3D MAX二次开发的学习者,我首先学习了导出插件的编写,网上有 ...

  5. 3DMAX导出插件的编写

    原文链接: http://liweizhaolili.blog.163.com/blog/static/162307442013117731953/ 作为3D开发人员,特别是3D引擎开发人员,经常会接 ...

  6. 3DsMax导出插件编写(一)——vs2010和3dsmax2011(64位)的配置方法

    作为3D开发人员,特别是3D引擎开发人员,经常会接触到各种三维模型的数据.虽然说3dsmax已经可以导出很多格式的模型,不过总是不一定合适自己用.所以总有一种想自己写一个导出插件,让3dmax导出自己 ...

  7. 3DsMax导出插件编写(三)——使用IGame收集模型信息

    之前介绍过用SDK的常规方法来获取模型的网格信息.这里再介绍另外一种方法. MaxSdk里面带了一个叫做IGame的包,里面包含有很多方便我们获取模型信息的方法.在sdk的自带例子里面,同样也有这个I ...

  8. VS2010插件编写学习总结

    VS2010 Addins 外接程序(插件)开发 http://www.cnblogs.com/Leo_wl/archive/2013/03/21/2973886.html 简单做了一个添加文件头注视 ...

  9. 从3dMax导出供threeJS使用的带动作模型与加载(认真修改详尽版)

    评论区发现的建议,最近没空测试,先贴这 还有好多人说找不到插件的 https://pan.baidu.com/s/1Q5g0... 密码:b43e . 应该是他们现在只是维护blender,只有这个的 ...

  10. 3DMax导入插件的编写(二)

    这是距上次插件向导安装配置完毕以后的插件编写经历,其中遇到了不少问题.由于对3DMAX的接口了解很少,很多资料都是从网上整理的.很多资料需要自己筛检. 1.插件向导安装完毕以后就可以创建3Dmax导入 ...

最新文章

  1. python连接mysql中报错1064修改方法
  2. 《研磨设计模式》chap13 命令模式
  3. android 过滤cmcc,Android 无法通过cmcc wap2.0 test解决
  4. mysql5.1安装失败_解决MySQL5.1安装时出现Cannot create windows service for mysql.error:0
  5. Asp.net输出Excel文件并且下载该文件以及某些细节问题解决
  6. 分区函数Partition By的与row_number()的用法以及与排序rank()的用法详解(获取分组(分区)中前几条记录)...
  7. Web3.0网关Deeper Network计划于本月末在四个平台上进行IDO
  8. Word 2003中打开最近操作过的文档的两种推荐的方法
  9. mysql整段注释_MySQL 添加注释(comment)
  10. 利用百度OCR实现验证码自动识别
  11. opencv中关于cvtColor函数性能测试
  12. GoldWave音频混合剪辑教程
  13. 回文子串、回文子序列相关题目
  14. MYSQL union 联合查询
  15. 【历史上的今天】4 月 28 日:人工智能理论之父出生;大众点评上线;苹果开设 iTunes 音乐商店
  16. 程序与数学:平方根计算与牛顿迭代法
  17. Qlik之创建任务调度(五)
  18. 露珠----让人窒息
  19. 【ML】第六章 决策树
  20. 使用mpx开发外卖小程序完整教程(附源码)

热门文章

  1. 数据与广告系列三:合约广告与与衍生的第三方广告数据监控
  2. 阿里云短信服务(解决个人无法申请问题)
  3. 计算机里比较器原理,数值比较器,数值比较器的作用和原理是 - 电子发烧友网...
  4. 2017埙箫简谱清单分享(附音频Demo)
  5. S3C2440-裸机篇-05 | S3C2440时钟体系详解(FCLK、PCLK、HCLK)
  6. python 100以内的奇数和_python入门:输出1-100之内的所有奇数和偶数
  7. C#进阶之路(四):拉姆达
  8. 偶然在github开源项目中发现了.travis.yml这货
  9. 超长正整数的加法(酷勤网)
  10. 黄巾之乱-服务器线程控制