在3D MAX导出插件编写I中已经具有了一个插件程序的基本框架,但这还是远远不够的,接下来,我们对I中的程序来补充肌肉、注入血液。
在正式写插件之前,也必须要弄清楚一些概念,比如说Node,Object,Mesh,Face,以及他们之间的关系,还有就是3D MAX场景的组织方式,关于这些内容我会专门安排一篇文章来进行归纳总结,请阅读:
3D MAX中的重要概念及场景组织方式
在写程序之前,有一点必须弄明白的是,需要导出的什么数据,在这里,我们需要的导出的数据有:
1.几何信息——顶点坐标、顶点法线向量、面法线向量、顶点颜色
2.材质信息——基本材质信息(材质的Ambient、Diffuse、Specular、Shininess)、纹理信息(纹理图片的文件名)
从读程序开始,首先找到程序的入口点DoExport()函数,在这个函数中有一个重要的类ExpInterface,在这个类中包含IScene这个类,这个类很关键,我们先看它在SDK 9.0中的描述:
Description:
Methods of this class may be used to enumerate the scene and to flag certain nodes in the scene. Nodes chosen by the plug-in may be flagged using the
EnumTree() method. Selected nodes may be flagged using
FlagFGSelected(). 由上面的描述可以看出这个类的作用是列举场景中所有的Nodes,而这个功能是由函数EnumTree()实现的,而这个函数会在程序的入口处被调用。对于每个结点,都有一个ITreeEnumProc *proc对象来描述它,proc对象是作为EnumTree()的参数传递进去的,再看类ITreeEnumProc的描述:
Description:
还有,由上面描述可知ITreeEnumProc这个类必须通过继承去实现其中的callback()函数才行,继承类MyTreeEnum如下:
而在程序的入口函数处需要修改一下:
后面导出信息的代码,就自己写了一些,继续。
1. 在SDK中有一个Mesh的概念,Node的几何信息都可以通过这个Mesh类获取到。在获取到TriObject对象后,获取Mesh对象就很容易了。
Mesh* pMesh = &tri->GetMesh();
//Access the material information
Mtl* pMtl = inode->GetMtl();
if(pMtl->ClassID() == Class_ID(DMTL_CLASS_ID, 0))
{
StdMat* std = (StdMat *)pMtl;
//Access the ambient,diffuse,specular,shininess,emission
const MSTR& sMtlName = pMtl->GetName();
Color m_Ambient = std->GetAmbient(0);
Color m_diffuse = std->GetDiffuse(0);
Color m_specular = std->GetSpecular(0);
float m_shininess = std->GetShininess(0);
float m_shiniStrength = std->GetShinStr(0);
if (pMtl->GetSelfIllumColorOn())
{
float r = pMtl->GetSelfIllumColor().r;
float g = pMtl->GetSelfIllumColor().g;
float b = pMtl->GetSelfIllumColor().b;
}
else
Texmap *tmap = pMtl->GetSubTexmap(ID_DI);
if (tmap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0))
{
BitmapTex *bmt = (BitmapTex*)tmap;
if(bmt)
{
char* MapName = bmt->GetMapName();
}
}
}
在写程序之前,有一点必须弄明白的是,需要导出的什么数据,在这里,我们需要的导出的数据有:
1.几何信息——顶点坐标、顶点法线向量、面法线向量、顶点颜色
2.材质信息——基本材质信息(材质的Ambient、Diffuse、Specular、Shininess)、纹理信息(纹理图片的文件名)
从读程序开始,首先找到程序的入口点DoExport()函数,在这个函数中有一个重要的类ExpInterface,在这个类中包含IScene这个类,这个类很关键,我们先看它在SDK 9.0中的描述:
Description:
Description:
- This is the callback object used by IScene::EnumTree(). To use it, derive a class from this class, and implement the callback method.
还有,由上面描述可知ITreeEnumProc这个类必须通过继承去实现其中的callback()函数才行,继承类MyTreeEnum如下:
class MyTreeEnum : public ITreeEnumProc
{
public:
MyTreeEnum(void);
~MyTreeEnum(void);
public:
int callback( INode *node );
};
{
public:
MyTreeEnum(void);
~MyTreeEnum(void);
public:
int callback( INode *node );
};
有了这样类,剩下的导出数据的工作就都在这个函数中实现。
接下来,是一个callback函数的简单框架:
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;
}
{
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;
}
而在程序的入口函数处需要修改一下:
int MaxExportTest::DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options)
{
MyTreeEnum tempProc;
ei->theScene->EnumTree( &tempProc );
return TRUE;
}
{
MyTreeEnum tempProc;
ei->theScene->EnumTree( &tempProc );
return TRUE;
}
最后,编译它,开始调试,找一个有物体,材质,灯光,摄像机的场景进行导出,如果你能在控制台输出窗口看到每个结点的名字,说明你的代码成功了。
1. 在SDK中有一个Mesh的概念,Node的几何信息都可以通过这个Mesh类获取到。在获取到TriObject对象后,获取Mesh对象就很容易了。
然后分别获取顶点信息、法向量信息、纹理坐标。
Mesh *pMesh = &triobj->GetMesh();
int VerticesNum = pMesh->getNumVerts();
for ( int i=0; i<VerticesNum; i++ )
{
Point3 Coord,Normal,VColor,TCoord;
int FaceNumber;
//导出顶点坐标
Coord = pMesh->getVert(i);
//导出顶点的法向量
pMesh->buildNormals();
Normal = pMesh->getNormal(i);
//导出顶点的纹理坐标
TCoord = pMesh->tVerts[i];
}
int VerticesNum = pMesh->getNumVerts();
for ( int i=0; i<VerticesNum; i++ )
{
Point3 Coord,Normal,VColor,TCoord;
int FaceNumber;
//导出顶点坐标
Coord = pMesh->getVert(i);
//导出顶点的法向量
pMesh->buildNormals();
Normal = pMesh->getNormal(i);
//导出顶点的纹理坐标
TCoord = pMesh->tVerts[i];
}
寥寥几行代码,就可以导出node的几何信息。除此,还需要导出node的材质信息,对于单纹理的node,可以简单的获取材质信息。
//Access the material information
Mtl* pMtl = inode->GetMtl();
if(pMtl->ClassID() == Class_ID(DMTL_CLASS_ID, 0))
{
StdMat* std = (StdMat *)pMtl;
//Access the ambient,diffuse,specular,shininess,emission
const MSTR& sMtlName = pMtl->GetName();
Color m_Ambient = std->GetAmbient(0);
Color m_diffuse = std->GetDiffuse(0);
Color m_specular = std->GetSpecular(0);
float m_shininess = std->GetShininess(0);
float m_shiniStrength = std->GetShinStr(0);
if (pMtl->GetSelfIllumColorOn())
{
float r = pMtl->GetSelfIllumColor().r;
float g = pMtl->GetSelfIllumColor().g;
float b = pMtl->GetSelfIllumColor().b;
}
else
Texmap *tmap = pMtl->GetSubTexmap(ID_DI);
if (tmap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0))
{
BitmapTex *bmt = (BitmapTex*)tmap;
if(bmt)
{
char* MapName = bmt->GetMapName();
}
}
}
以上获取的这些信息还只是孤立的数据,要想把这些数据组织起来,需要获取Mesh类中的Face信息。