之前已经把配置vs和maxSdk的方法介绍过了, 下面来介绍一下导出插件的具体写法。不过这不是一个容易说的很详细的问题。因为我们要写导出插件,通常都是因为想根据自己想要的信息来导出,所以就算我把我整个工程都公开,意义也不大的,因为那是根据我自己需要的数据写的业务,估计不太可能和你想要的一样的。所以我也只能简单的说明一些几个关键获取数据的方法,和保存文件的方法,如果想看具体的导出范例工程,你可以去maxSdk文件夹下面找到maxsdk\samples\import_export\3dsexp.cpp,这是一个完整的导出3ds文件的例子。

然后maxSdk提供的操作数据的方法有两种,一种是常规方法,一种是IGame方法。下面我们先来介绍一下常规的方法,至于IGame的方法,我会在另外一遍文章里面说明。

我建议先看看常规方法,对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。

3DsMax导出插件编写(二)——常规SDK方法进行信息获取和保存文件相关推荐

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

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

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

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

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

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

  4. 3D MAX导出插件编写

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

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

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

  6. php保存文件的方法,php实现编辑和保存文件的方法

    本文实例讲述了php实现编辑和保存文件的方法.分享给大家供大家参考.具体如下: save_file.php: session_start(); $handle = fopen($_post['orig ...

  7. java输入学生信息_要求编写一个java程序,输入学生信息,并能保存与显示学生信息。...

    Java 代码如下:import java.util.ArrayList; import java.util.Scanner;public class student{ private int num ...

  8. VS2010插件编写学习总结

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

  9. Revit 导出插件使用说明及注意事项

    一.使用条件: 1.系统安装有 Revit 软件: 2.需要超图组件许可: 3.在 Revit 中的三维视图下导出数据. 二.使用方法: 1.根据安装的 Revit 版本,将对应版本的插件库文件 Re ...

  10. 干电池电量采集_干电池电量的检测方法,干电池的常用保存方法

    有关干电池电量的检测方法,检测普通锌锰干电池的电量是否充足,一般有两种常用的方法,一是测量电池瞬时短路电流来估算电池的内阻,二是用电流表串联一只阻值适当的电阻,测量电池的放电电流计算出电池内阻. 干电 ...

最新文章

  1. 心得丨从0到1,教你如何利用大学四年无师自通,学好机器学习!
  2. Visual Studio 2010 的新机遇
  3. 解决Word 2007启动时弹出“无法访问您试图使用功能所在的网络位置”的问题...
  4. ECShop 增加收藏商品排行榜功能
  5. 亲爱的SAP从业者们,烦请做个SAP知识学习种类的小调查
  6. STM8学习笔记---点亮LED灯
  7. 【Google设计冲刺】一种适合于创新小组的协作方式
  8. 老罗Android视频教程第二季(全套视频+源码)
  9. 谷歌浏览器屏蔽广告插件
  10. JAVA中ResourceBundle使用详解
  11. python和basic语言的区别_Python语言是什么?学Python语言有前途吗?
  12. Flutter实战】文本组件及五大案例
  13. 修改北京定点医院amp;查询医保信息
  14. HAL - RTC分析
  15. z-index失效的几种情况,父标签position属性为relative的时候,详解
  16. Fortran语法汇总(下)(持续更新中)
  17. Js构造函数创建Person类
  18. PNAS | 香港理工李向东等揭示全球空气细菌群落与地球微生物组和人类活动的互作...
  19. 怎么利用matlab建模仿真,仿真建模与MATLAB
  20. CEO说其实福特造的是“计算机”,流水线工人靠外骨骼变身钢铁侠

热门文章

  1. Linaro ABE(高级构建环境)构建GNU交叉工具链
  2. 计算机色彩学,浅析色彩原理
  3. 快去抢票!今天开始!2020元旦春节火车票购票日程攻略来了
  4. 工信部定级备案和等保备案有什么区别
  5. github rust 项目Travis ci配置
  6. CAT分布式监控系统(一):CAT功能介绍 CAT监控系统是什么、能做什么?
  7. 第十篇、线性表中的链式存储结构--双链表
  8. 支付网关路由之设计方案实战
  9. 少有人走的路--心智成熟的旅程
  10. java 开源esb_五大开源ESB项目