3ds max sdk导出插件编写的心得
3ds max sdk导出插件编写的心得
作者:yhchinabest
来自:CG先生-3D图形插件开发网http://www.cgsir.com
写在前面
由于时间精力有限,只写出导出程序的一些体会,以后会写出导入程序的体会。希望大家多批评指教。
环境配置
步骤1.首先你得有VS2005,3ds Max 9,如果有就好办了,否则想办法搞到手吧,在中国做到这点应该不难。至于其他相近版本的IDE和MAX,情况基本类似。
步骤2.在3ds Max9 SDK/maxsdk/howto/3dsmaxPluginWizard中有个readme.txt,它会向你介绍如何配置3ds Max9 plugin的向导。
步骤3.启动vs2005,新建Visual C++项目,如果在右侧的模板组中能够找到”3dsmaxPluginWizard”,并且选择后能够弹出欢迎界面,说面配置已经成功了。
第一个导出程序
这里仅仅是为了让大家更好的了解导出插件是如何工作的,所以什么都不导出,做个测试而已。
2. 找到class MyExport的函数const TCHAR *MyExport::Ext(int n)定义。该函数用来显示导出文件的扩展名,改一下,例如return _T(“My3D”)。
3. 再找到const TCHAR *MyExport:: ShortDesc ()的定义,该函数显示插件的描述信息,也改一下,例如return _T(“MyExportPlugin”)
AllocConsole(); _cprintf( "Export Begin/n" );//记得#include <conio.h> |
Mesh,Material,Light,Camera,让我们找到它们
2. IScene有个重要的函数: int EnumTree ( ITreeEnumProc *proc )。看看这个函数的描述:
Remarks: Implemented by the System.. |
Parameters: ITreeEnumProc *proc |
Returns: Nonzero if the process was aborted by the callback (TREE_ABORT); otherwise 0. |
可以看出,这个函数会被系统自动调用。它会枚举场景中的每个结点。对每个结点,它再调用ITreeEnumProc *proc,估计这个proc就是用来解析每个结点的东西。
Description: This is the callback object used by IScene::EnumTree(). To use it, derive a class from this class, and implement the callback method. |
它有个一个成员函数int callback(INode *node)看来我们需要的就是它了,这个函数会让系统传给你你要的node,你来实现这个callback函数。
4. 看来我们要写些代码了(我估计你也早等不及了),让我们写一个继承ITreeEnumProc的类:
class MyTreeEnum : public ITreeEnumProc { public: MyTreeEnum(void); ~MyTreeEnum(void); public: int callback( INode *node ); }; |
然后实现int callback( INode *node ):
int MyTreeEnum::callback(INode *node) { ObjectState os = node->EvalWorldState(10); if ( os.obj->CanConvertToType( Class_ID(TRIOBJ_CLASS_ID, 0) ) ) { _cprintf( "TRIOBJECT %s/n", node->GetName()); Mtl *pMtl = node->GetMtl(); if ( pMtl ) { _cprintf( "MATERIAL %s/n",pMtl->GetName() ); } return TREE_CONTINUE; } if (os.obj) { switch(os.obj->SuperClassID()) { case CAMERA_CLASS_ID: _cprintf( "CAMERA %s/n", node->GetName()); break; case LIGHT_CLASS_ID: _cprintf( "LIGHT %s/n", node->GetName()); break; } } return TREE_CONTINUE; } |
接着,让我们调用这个函数,这只需要修改DoExport()函数
int MaxExportTest::DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options) { MyTreeEnum tempProc; ei->theScene->EnumTree( &tempProc ); return TRUE; } |
最后,编译它,开始调试,找一个有物体,材质,灯光,摄像机的场景进行导出,如果你能在控制台输出窗口看到每个结点的名字,说明你的代码成功了。
|
Subclasses of Built into core
Subclasses of LIGHT_CLASS_ID:
|
6. 好了,我们大概找到了我们需要的东西,下一章,我会示范如何从这些较大的范围中得到我所感兴趣的具体的信息,如灯光的位置和方向,以及最重要的Mesh的顶点信息等。
Mesh,Material,Light,Camera,让我们解析他们
1. 首先说说mesh和material吧,这两者结合相当密切。上一章说到如何获得TriObject,通过它可以获得一个mesh:
Mesh* pMesh = &tri->GetMesh();
Mesh* pMesh = &tri->GetMesh(); int VerticesNum = pMesh->getNumVerts() for ( int i=0; i<VerticesNum; i++ ) { Point3 Coord, Normal, TCoord, VColor; if( pMesh->getNumVerts()>0 ) //导出顶点坐标 { Coord = pMesh->getVert( i ); } if ( pMesh->faces ) //导出法线向量 { Normal = pMesh->getNormal( j ); } if ( pMesh->getNumTVerts()>0 )//导出纹理坐标 { TCoord = pMesh->tVerts[j] ; } if ( pMesh-> vertCol ) //导出顶点颜色 { VColor = pMesh->vertCol[i]; } } |
Mtl *pMtl = pNode->GetNode if ( pMtl!=NULL ) { Texmap *pTexMap = pMtl->GetSubTexmap(ID_DI); //获取漫反射材质的贴图 BitmapTex *pBMPTex = (BitmapTex *) pTexMap; if ( pBMPTex ) { char *MapName = pBMPTex->GetMapName(); //获取漫反射贴图的路径 } } |
struct FaceVertex { int m_Index; //表示该顶点在原mesh里的索引值 int m_FaceIndex; //表示该顶点所属的面在原mesh里的索引值 int m_TriIndex; //表示该顶点在所属三角形里的索引值,值为0,1,2 bool operator == (const FaceVertex &refVertex) { if ( m_Index == refVertex.m_Index ) { return true; } else { return false; } } }; class MaxDivideMesh { public: vector<FaceVertex> m_VertexArray; vector<int> m_IndexArray; }; void MyTreeEnum::CreateMutilMesh( INode *pNode, Mesh *pMesh, Mtl *pMtl ) { vector <int> MeshMtls; //该Mesh用到的子材质的数量,用来计算子Mesh的划分 //每个元素表示一个材质ID。 for( int i=0; i<pMesh->getNumFaces(); i++ ) { /*计算子Mesh数量,通过计算所有面使用的非重复材质数量而得*/ int MatID = pMesh->getFaceMtlIndex(i); vector<int>::iterator MatIndex = find( MeshMtls.begin(), MeshMtls.end(), MatID ); if ( MatIndex == MeshMtls.end() ) { MeshMtls.push_back(MatID );//该材质未在MeshMtls里出现过,说明是个 //新材质 } } //DivideMeshArray,计算Mesh划分的拓扑信息 vector<MaxDivideMesh> DivideMeshArray; DivideMeshArray.resize( MeshMtls.size() );//指定划分数量 // //此处有内存的分配 //GMeshD3D是我自己设计的一个类型,用来表示一个子Mesh //GTextureD3D用来表示Texture GMeshD3D *pMeshArray = new GMeshD3D[MeshMtls.size()]; GTextureD3D *pTextureArray = new GTextureD3D[MeshMtls.size()]; GObjectMAXD3D tempObj; //GObjectMAXD3D表示一个模型,有n个mesh和texture组成 tempObj.SetMeshNum( MeshMtls.size() ); tempObj.SetTextureNum( MeshMtls.size() ); for ( int i=0; i<MeshMtls.size(); i++ ) { if ( pMtl!=NULL ) { Mtl *pSubMtl = pMtl->GetSubMtl( MeshMtls[i] ); Texmap *pTexMap = pSubMtl->GetSubTexmap(ID_DI); //获取漫反射材质的贴//图 BitmapTex *pBMPTex = (BitmapTex *) pTexMap; if ( pBMPTex ) { char *MapName = pBMPTex->GetMapName(); //获取漫反射贴图名称 if ( MapName!=NULL ) { pTextureArray[i].SetMapName(MapName); } } pMeshArray[i].m_MatID = i; tempObj.SetTexture( &pTextureArray[i], i ); } } /*这里开始对原有mesh进行重新划分*/ for( int i=0; i<pMesh->getNumFaces(); i++ ) { int MatID = pMesh->getFaceMtlIndex(i); //计算该面的材质ID vector<int>::iterator MatIndex = find( MeshMtls.begin(), MeshMtls.end(), MatID ); int MeshID = MatIndex - MeshMtls.begin(); //计算该MatID在TextureArray的纹理索引,使MeshID从0开始编号 for ( int j=0; j<3; j++) { int Index = pMesh->faces[i].v[j];//Index表示在全局顶点数组里的索引 FaceVertex tempVertex; tempVertex.m_Index = Index; vector<FaceVertex>::iterator VertexIter = find( DivideMeshArray[MeshID].m_VertexArray.begin(), DivideMeshArray[MeshID].m_VertexArray.end(), tempVertex ); if ( VertexIter == DivideMeshArray[MeshID].m_VertexArray.end() ) //在DivideMeshArray里寻找顶点索引值相同的顶点,如果没找到该顶点,表示//要添加该顶点 { int VertexIndex = VertexIter - DivideMeshArray[MeshID].m_VertexArray.begin(); FaceVertex tempFVertex; tempFVertex.m_Index = Index; tempFVertex.m_FaceIndex = i; tempFVertex.m_TriIndex = j; DivideMeshArray[MeshID].m_VertexArray.push_back( tempFVertex ); } } } /*计算顶点在每个子mesh中的索引*/ for( int i=0; i<pMesh->getNumFaces(); i++ ) { int MatID = pMesh->getFaceMtlIndex(i); vector<int>::iterator MatIndex = find( MeshMtls.begin(), MeshMtls.end(), MatID ); int MeshID = MatIndex - MeshMtls.begin(); //计算该MatID的纹理索引 for ( int j=0; j<3; j++) { int Index = pMesh->faces[i].v[j]; FaceVertex tempVertex; tempVertex.m_Index = Index; //若在子mesh里能找到该点,则计算该点在子mesh的索引 vector<FaceVertex>::iterator VertexIter = find( DivideMeshArray[MeshID].m_VertexArray.begin(), DivideMeshArray[MeshID].m_VertexArray.end(), tempVertex ); if ( VertexIter != DivideMeshArray[MeshID].m_VertexArray.end() ) { int VertexIndex = VertexIter - DivideMeshArray[MeshID].m_VertexArray.begin();//VertexIndex表//示该顶点在子Mesh的索引 DivideMeshArray[MeshID].m_IndexArray.push_back( VertexIndex ); } } } /*余下部分开始到处顶点信息*/ for ( int i=0; i<MeshMtls.size(); i++ ) { pMeshArray[i].SetFVF( GFVF ); pMeshArray[i].SetVerticeNum( DivideMeshArray[i].m_VertexArray.size() ); int VerticesNum; pMeshArray[i].GetVerticeNum( VerticesNum ); for ( int j=0; j<VerticesNum; j++ ) { GVertex tempVertex; Point3 Coord,Normal,TCoord; if( pMesh->getNumVerts()>0 ) //导出顶点坐标 { int index = DivideMeshArray[i].m_VertexArray[j].m_Index; Coord = pMesh->getVert( DivideMeshArray[i].m_VertexArray[j].m_Index ); tempVertex.PosCoord = D3DXVECTOR3( Coord.x, Coord.y, -Coord.z ); } if ( pMesh->faces ) //导出法线向量 { Normal = pMesh->getNormal( DivideMeshArray[i].m_VertexArray[j].m_Index ); tempVertex.NormalVector = D3DXVECTOR3( Normal.x, Normal.y, Normal.z ); } if ( pMesh->getNumTVerts()>0 )//导出纹理坐标 { FaceVertex tempFVertex = DivideMeshArray[i].m_VertexArray[j]; TCoord = pMesh->tVerts[pMesh->tvFace[tempFVertex.m_FaceIndex].getTVert(tempFVertex.m_TriIndex)] ; int TCoordIndex = pMesh->tvFace[tempFVertex.m_FaceIndex].getTVert(tempFVertex.m_TriIndex); _cprintf( "TextureCoord Index%d/n", TCoordIndex ); tempVertex.TexCoord = D3DXVECTOR2( TCoord.x, TCoord.y ); } DWORD VColor = 0xffffffff; tempVertex.Color = VColor; pMeshArray[i].SetVertex( tempVertex, j ); } pMeshArray[i].SetFaceNum( DivideMeshArray[i].m_IndexArray.size()/3 ); WORD FaceNum; pMeshArray[i].GetFaceNum( FaceNum ); for ( int j=0; j<FaceNum; j++ ) { pMeshArray[i].SetIndex( DivideMeshArray[i].m_IndexArray[j*3], j*3 ); pMeshArray[i].SetIndex( DivideMeshArray[i].m_IndexArray[j*3+2], j*3+1 ); pMeshArray[i].SetIndex( DivideMeshArray[i].m_IndexArray[j*3+1], j*3+2 ); } } tempObj.WritetoFile(); delete []pMeshArray; delete []pTextureArray; } |
void MyTreeEnum::CreateCamera( INode *pNode ) { ObjectState os = pNode->EvalWorldState( 10 ); CameraObject* CameraObj = (CameraObject*)os.obj; struct CameraState cs; Interval valid = FOREVER; CameraObj->EvalCameraState( 10, valid, &cs ); Matrix3 SourceMat = pNode->GetNodeTM( 10 );//获取摄像机源点的变换矩阵 Matrix3 destMatrix; pNode->GetTargetTM( 0, destMatrix ); //获取摄像机目标点的变换矩阵 cs.fov; //获取FOV角 cs.hither; //获取摄像机近平面 cs.yon; //获取摄像机远平面 } |
3. 导出灯光,与摄像机差不多,也是位置和方向需要结点函数来获得,而其余信息通过访问Light,LightState,GenLight,LightObject获得。
void MyTreeEnum::CreateLight( INode *pNode ) { ObjectState os = pNode->EvalWorldState(10); GenLight* light = (GenLight*)os.obj; struct LightState ls; Interval valid = FOREVER; light->EvalLightState( 10, valid, &ls ); Matrix3 SourceMat = pNode->GetNodeTM( 10 ); Matrix3 TargetMat; pNode->GetTargetTM( 10, TargetMat ); float Theta, Phi; Theta = ls.hotsize; Phi = ls.fallsize; switch(ls.type) //导出灯光类型 { case OMNI_LIGHT: _cprintf( "%s/n", "ID_LIGHT_TYPE_OMNI" ); break; case TSPOT_LIGHT: _cprintf( "%s/n", "ID_LIGHT_TYPE_TARG" ); break; case DIR_LIGHT: _cprintf( "%s/n", "ID_LIGHT_TYPE_DIR" ); break; case FSPOT_LIGHT: _cprintf( "%s/n", "ID_LIGHT_TYPE_FREE" ); break; } } |
呼。。。。。终于写完了,感觉还是很多东西没说清楚。希望大家多学学3ds Max SDK,给我多提出意见,呵呵。
3ds max sdk导出插件编写的心得相关推荐
- 3D MAX导出插件编写
文章版权归博客园 BigCoder所有,转载请于明显位置标明原文作者及出处,以示尊重!! 原文出处:http://www.cnblogs.com/csyisong/archive/2009/09/01 ...
- 3D MAX导出插件编写I
3D MAX导出插件编写I 想想研究3D MAX 的SDK已经有了不短的时间,真正算起来也有两个月了吧,但是讲到收获,确实不大.作为一个3D MAX二次开发的学习者,我首先学习了导出插件的编写,网上有 ...
- MAX SDK之插件概述(一)
一.MAX 插件概述 1.1 插件的功能 所谓插件,就是开发者自己开发的一组程序用以扩展MAX的功能.如果使用过3DS MAX,我们会发现,MAX的每组功能都封装在某一类插件中,如创建物体,修改器等等 ...
- 3ds max文件导出osg或者ive格式
osg/osgEarth系列文章目录 文章目录 osg/osgEarth系列文章目录 前言 参考 前言 首先下载插件osgexp Osgexp的下载地址 安装上之后,如果3ds max导出里面已经可以 ...
- 3DsMax导出插件编写(一)——vs2010和3dsmax2011(64位)的配置方法
作为3D开发人员,特别是3D引擎开发人员,经常会接触到各种三维模型的数据.虽然说3dsmax已经可以导出很多格式的模型,不过总是不一定合适自己用.所以总有一种想自己写一个导出插件,让3dmax导出自己 ...
- 炫云云渲染3ds max支持的插件有哪些?
很多人都想知道炫云3ds max都支持哪些插件,下面就为大家介绍下. 炫云支持的插件有SigerScratches.ThinFilm.tyfolw.Anima.Autograss.Autohedge. ...
- 3ds Max模型导出/转换为Revit族文件(可编辑材质)
目标 将3ds Max模型中的整体或局部模型导出/转换为Revit族文件,族文件的每部分支持单独设置及编辑材质. → 步骤 1.3ds Max端操作 (1)将需转换模型导出为3ds格式文件 选中需转换 ...
- png在ai转为路径_AI路径导入3ds Max并导出图像
在使用3ds Max的过程中有时会用到比较复杂的路径,虽然它本身的路径功能很齐全,但对于我来说,它操作起来很别扭,不顺手. 相比之下,AI 的路径功能比较强大,且操作起来很方便.所以我一般是在AI画好 ...
- 3DsMax导出插件编写(三)——使用IGame收集模型信息
之前介绍过用SDK的常规方法来获取模型的网格信息.这里再介绍另外一种方法. MaxSdk里面带了一个叫做IGame的包,里面包含有很多方便我们获取模型信息的方法.在sdk的自带例子里面,同样也有这个I ...
最新文章
- 总结个人项目设计保障5大原则
- Web开发工具包收藏
- 使用Mockito模拟自动装配的字段
- .net framework4与其client profile版本的区别
- Eclipse安装lombook
- Hands-on Lab (15) - 使用Prometheus Operator监控应用
- 2018程序员必读书单
- 16 | 把大象装进冰箱:HTTP传输大文件的方法
- web安全攻防渗透+赵雨佳43
- QGIS加载在线地图:高德、天地图等
- girl_noise.jpg恢复去噪
- jQuery如何根据元素值删除数组元素
- 斐波那契堆的实现和比较(相对二项堆)
- Mybatis--动态sql之choose、when、otherwise语句(只匹配其中的一个条件)
- 【立创开发板】梁山派初体验
- keil遇到FCARM - Output Name not specified, please check 'Options for Target - Utilities'解决方法
- 解决ReactNative老项目跑不起来的问题
- 2021 最新分享 Java 面试题库万字精华 github 上标星 80
- 【誉天】这几道华为云计算认证题,你答对了
- 学生怎么在网上赚钱,想赚钱就要学会这些!