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

本文介绍了一种利用3Ds Max SDK中的IGame包高效获取3D模型网格信息的方法,包括初始化IGame、获取模型节点信息及网格数据等,并对比了与传统SDK方法的不同之处。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前介绍过用SDK的常规方法来获取模型的网格信息。这里再介绍另外一种方法。
MaxSdk里面带了一个叫做IGame的包,里面包含有很多方便我们获取模型信息的方法。在sdk的自带例子里面,同样也有这个IGame的例子,不过我自己没有编译通过,存在一些错误。所以我自己看SDK的API,摸索出了使用的方法。

#include <IGame.h> 。

使用IGame之前需要先给他做一个初始化,以下是我的初始化的方法,直接在DoExport方法里面先调用就可以了:

void InitIGame(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options)
{
//保存路径
savePath = name;
//是否支持显示界面
showPrompts = suppressPrompts ? FALSE : TRUE;
//导出选中物体还是导出全部
exportSelected = (options & SCENE_EXPORT_SELECTED) ? true : false;
//获取IGame的版本,这个如果你用不到可以不要
igameVersion  = GetIGameVersion();
//IGameScene* pScene;这个东西就是代替了常规方法里面的场景树节点管理
pScene = GetIGameInterface();
//获取IGameConversionManager 是为了设置左右手坐标系,如果你不需要设置,可以不要。
//具体的SetUserCoordSystem数值可以去查maxSdk的api,有详细说明
IGameConversionManager * cm = GetConversionManager();
cm->SetUserCoordSystem(leftHandCoord);
//初始化场景
pScene->InitialiseIGame(exportSelected);
}

这里和常规方法做一个对比,这个初始化的方法其实和常规方法差不多,不过方便的地方在于,我们可以直接的使用IGameScene类来管理场景,而不需要自己重新写一个树节点管理类,再自己写回调的方法了。然后可以设置左右手坐标系。常规方法如果你要转换坐标系,就要自己在导出的时间计算一下,调换一下坐标系的值了。最后,之前我们做是否导出选择的物体,需要自己在回调的方法里面判断exportSelected,但IGame却是在pScene->InitialiseIGame(exportSelected);这一步就已经把这件事完全的做好了。如果你是选择选中的物体导出,那么初始化的时候只会把选中的物体放进场景树节点里面,没有选择的东西是没有任何的信息的。这样做,好处是方便了操作,坏处是你想只导出选中的物体,又想遍历一些没有选中的物体做计算的时候,就不行了,但常规方法却可以。

接下来再看看怎样获取物体节点的各种信息:

void CollectObjects()
{
int num = pScene->GetTopLevelNodeCount();   
IGameNode* pNode;
for(int i = 0; i < num; i++)
{
    pNode = pScene->GetTopLevelNode(i);
    if(pNode->IsTarget())
   {
      continue;
   }
   IGameObject* obj = pNode->GetIGameObject();
   char* oName = pNode->GetName();
   IGameObject::ObjectTypes type = obj->GetIGameType();
   switch (type)
   {
       case IGameObject::IGAME_MESH:
           meshObjRoots.push_back(pNode);
            DebugLog(oName);
            DebugLog(" is a Mesh!!\n");
            break;
       case IGameObject::IGAME_BONE:
           boneObjRoots.push_back(pNode);
            DebugLog(oName);
            DebugLog(" is a Bone!!\n");
            break;
       default:
           break;
   }
}

}
以上的方法,就可以获取到场景里面的所有作为根节点的物体了。所谓的根节点,就是父物体为空(也就是父物体为场景Root)的物体,它有可能还有很多子物体,这里我们先不获取,然后我们下一步可以通过获取子物体的方法做递归,就可以获取全部的物体了。
在这里我们先来和常规方法对比一下:
常规方法我们要判断物体的类型,需要判断节点物体的ClassID(),而且ClassID()的类别繁多,骨骼还分为bone和biped需要分别作判断。而IGame却比较方便了,直接GetIGameType,然后就可以判断类型,而且不管是bone还是biped,它都是IGAME_BONE,比较方便。

获得了父物体之后,我们可以写一个方法来遍历他们的子物体,做一个递归,最后返回的数据就是当前父物体的所有子物体了,自己想办法存起来

vector<IGameNode*> getChildNode(IGameNode* pObj)
{
vector<IGameNode*> tempList;
tempList.push_back(pObj);
int childNum = pObj->GetChildCount();
if(childNum>0)
{
    for(int i = 0;i<childNum;i++)
    {
        IGameNode* subNode = pObj->GetNodeChild(i);
        vector<IGameNode*> subVector = getChildNode(subNode);
        if(subVector.size()>0)
        {
            for(int j = 0;j<subVector.size();j++)
            {
                tempList.push_back(subVector[j]);
            }
        }

    }
}
return tempList;
}

获取到所有物体的节点之后,我们就可以来获取所有的网格信息了,在获取每一个节点的网格信息之前,有一个步骤是必须的:
IGameObject* iObj = node->GetIGameObject();
IGameMesh * iMesh = (IGameMesh*)iObj;
iMesh->InitializeData();//这个就是初始化数据
如果不先这样初始化一下IGameObject,那么你之后获取模型信息的时候,很可能会遇到顶点索引是空的,之类的问题。
获取顶点信息:

int vertSum = iMesh->GetNumberOfVerts();
for(i = 0;i<vertSum;i++)
{
    Vertex_t vInfo;
    Point3 pos = iMesh->GetVertex(i);
    //然后自己存起来
}

获取顶点索引的信息:

int faceSum = iMesh->GetNumberOfFaces();
    for(i = 0;i<faceSum;i++)
    {
        FaceEx* face = iMesh->GetFace(i);
        vector<int> indexs;
        for(j = 0;j<3;j++)
        {
            indexs.push_back(face->vert[j]);
        }
        //然后自己存起来
    }

获取UV信息:获取uv信息其实也是用上面的FaceEx方法的数据,不过由于3DsMax的贴图通道可能有很多个,我们习惯上就用第一个通道的,不过有些人会使用多套UV,可能会使用多个通道,所以我们先要获取一下所有的UV通道:

Tab<int> mapNums = iMesh->GetActiveMapChannelNum();

这样,这里面就存了可能用到的UV通道的序号。

int mapCount = mapNums.Count();
for(int i=0;i < mapCount;i++)
{
    for(j = 0;j<faceSum;j++)
    {
        FaceEx* face = iMesh->GetFace(j);
        for(int k = 0;k<3;k++)
        {
            int ind = face->vert[k];
            int induv = face->texCoord[k];
            Point3 tv;
            if(iMesh->GetMapVertex(mapNums[i],induv,tv))
            {
                if(ind<objList.vert.size())
                {
                    //这里是注意重点,看下面解释
                    objList.vert[ind].uv = tv;
                }
            }
        }
    }

}

上面代码里面有一个非常重要的问题,很多人如果直接获取uv坐标,最终的模型很可能会出错的。我这里详细的说明一下。
之前用常规的SDK方法获取UV坐标,我们通过了一个TVFace来获取模型的实际uv点的坐标。这里我们使用了一个face->texCoord来获取UV坐标。其实他们的道理是一样的。
本来我们知道了顶点的总数,逐个去遍历他们的GetMapVertex,应该就可以获取到所有的顶点uv坐标,这是没错的。不过问题在于,顶点的位移坐标的排序,和uv坐标的排序是不一致的。有可能一个点在位移坐标时的排序index是10,但在uv坐标的排序index是20之类。所以如果你按照统一的一个顺序去遍历所有点再记录位移坐标和uv坐标,获取的数据是对的,但顺序很可能就错了,而导致之后你的模型的点和uv点对不上,导致了模型能显示出来,但uv贴图乱了。
这个uv坐标的排序index是怎样获得的呢?常规SDK是用TVFace和一般的Face相对应,然后获取Face对于的TVFace的顶点序号。而用IGame,它没有两种Face了,就只有一种FaceEx,里面的 face->vert是顶点位移的排序坐标,然后face->texCoord就是该位移顶点对应的uv坐标的排序了。

以上就是使用IGame获取模型网格信息的方法了。需要获取其他的数据,可以去查查api,里面都有说明。然后我们还是用C++提供的写文本方法,把数据保存起来就可以了。
接下来我将会再介绍一下骨骼动画的导出方法,

包含 for 3DS MAX3 for 3DS MAX4 0 for 3DS MAX4 0+ CS3 1 for 3DS MAX4 2 CS3 2+ or MAX 5 0 for 3DS Max 6 0+ CS4 2 and 3DS Max 7 0 for 3DS Max 8 0 for 3DS Max 9 0 Panda Directx Exporter x64 5 9 67 0 for 3DS Max 9 64 bit Panda Directx Exporter x64 5 2008 67 0 for 3DS Max 2008 64 bit Panda Directx Exporter x64 5 2009 67 0 for 3DS Max 2009 64 bit Panda Directx Exporter x64 6 9 72 0 for 3DS Max 9 64 bit Panda Directx Exporter x64 6 2008 71 0 for 3DS Max 2008 64 bit Panda Directx Exporter x64 6 2009 72 0 for 3DS Max 2009 64 bit Panda Directx Exporter x64 6 2010 72 0 for 3DS Max 2010 64 bit Panda Directx Exporter x64 6 2011 72 0 for 3DS Max 2011 64 bit Panda Directx Exporter x64 6 2012 72 0 for 3DS Max 2012 64 bit Panda Directx Exporter x86 5 8 66 0 for 3DS Max 8 32 bit Panda Directx Exporter x86 5 9 67 0 for 3DS Max 9 32 bit Panda Directx Exporter x86 5 2008 67 0 for 3DS Max 2008 32 bit Panda Directx Exporter x86 5 2009 67 0 for 3DS Max 2009 32 bit Panda Directx Exporter x86 6 9 72 0 for 3DS Max 9 32 bit Panda Directx Exporter x86 6 2008 71 0 for 3DS Max 2008 32 bit Panda Directx Exporter x86 6 2009 72 0 for 3DS Max 2009 32 bit Panda Directx Exporter x86 6 2010 72 0 for 3DS Max 2010 32 bit Panda Directx Exporter x86 6 2011 72 0 for 3DS Max 2011 32 bit Panda Directx Exporter x86 6 2012 72 0 for 3DS Max 2012 32 bit ">包含 for 3DS MAX3 for 3DS MAX4 0 for 3DS MAX4 0+ CS3 1 for 3DS MAX4 2 CS3 2+ or MAX 5 0 for 3DS Max 6 0+ CS4 2 and 3DS Max 7 0 for 3DS Max 8 0 for 3DS Max 9 0 Panda Directx Exporter x64 5 9 67 0 for 3DS Max 9 64 bit Panda Directx Exporter x64 5 2008 67 0 for 3DS Max 2008 64 bit Panda Direc [更多]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值