3ds max sdk導(dǎo)出插件編寫的心得
作者:yhchinabest
來自:CG先生-
3D圖形插件開發(fā)網(wǎng)http://www.cgsir.com/
目錄

寫在前面

環(huán)境配置

第一個(gè)導(dǎo)出程序

Mesh,Material,Light,Camera,讓我們找到它們

Mesh,Material,Light,Camera,讓我們解析他們


 

 寫在前面

       為什么要寫這個(gè)心得?去年11月份的時(shí)候我寫過一篇3ds Max導(dǎo)出程序的一些嘗試,抱著學(xué)習(xí)的態(tài)度把一些心得發(fā)到網(wǎng)上供大家參考,不過當(dāng)時(shí)寫的還很不完善,很多東西沒說清楚。今年6月份開始又做了3ds Max導(dǎo)出導(dǎo)入程序的一些研究,感覺3ds Max SDK實(shí)在是博大精深,初學(xué)者入門還是很不方便,所以覺得以前發(fā)的心得應(yīng)該得到補(bǔ)充,因而寫了這樣一個(gè)導(dǎo)出程序介紹,還是抱著學(xué)習(xí)的態(tài)度,不過還是希望能夠?qū)Υ蠹矣兴鶐椭?/span>

    由于時(shí)間精力有限,只寫出導(dǎo)出程序的一些體會(huì),以后會(huì)寫出導(dǎo)入程序的體會(huì)。希望大家多批評(píng)指教。
環(huán)境配置

步驟1.首先你得有VS2005,3ds Max 9,如果有就好辦了,否則想辦法搞到手吧,在中國(guó)做到這點(diǎn)應(yīng)該不難。至于其他相近版本的IDEMAX,情況基本類似。

 

步驟2.3ds Max9 SDK"maxsdk"howto"3dsmaxPluginWizard中有個(gè)readme.txt,它會(huì)向你介紹如何配置3ds Max9 plugin的向?qū)А?/span>

 

步驟3.啟動(dòng)vs2005,新建Visual C++項(xiàng)目,如果在右側(cè)的模板組中能夠找到”3dsmaxPluginWizard”,并且選擇后能夠彈出歡迎界面,說面配置已經(jīng)成功了。


第一個(gè)導(dǎo)出程序

這里僅僅是為了讓大家更好的了解導(dǎo)出插件是如何工作的,所以什么都不導(dǎo)出,做個(gè)測(cè)試而已。

1.       plugin Type中選擇File Export。選擇下一步,然后給你的導(dǎo)出類起個(gè)名字,比如”MyExport”。選擇下一步,再輸入你的MAXSDK路徑,插件存放的路徑,3dsmax.exe存放路徑。然后Finish

 

2.       找到class MyExport的函數(shù)const TCHAR *MyExport::Ext(int n)定義。該函數(shù)用來顯示導(dǎo)出文件的擴(kuò)展名,改一下,例如return _T(“My3D”)

 

3.       再找到const TCHAR *MyExport:: ShortDesc ()的定義,該函數(shù)顯示插件的描述信息,也改一下,例如return _T(MyExportPlugin)

 

4.       為了了解導(dǎo)出程序的入口,在函數(shù)DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options)內(nèi)添加:

    AllocConsole();

    _cprintf( "Export Begin"n" );//記得#include <conio.h>

 

生成并調(diào)試你的插件,系統(tǒng)會(huì)執(zhí)行3dsmax.exe以啟動(dòng)3ds Max,然后選擇“文件”->導(dǎo)出,希望你能看到MyExportPlugin (*.My3D)的選項(xiàng),然后隨便敲個(gè)什么名字,“確定”,如果能看到控制臺(tái)輸出Export Begin,那么第一個(gè)導(dǎo)出程序的實(shí)驗(yàn)便成功了。當(dāng)然,你一定不會(huì)對(duì)導(dǎo)出文件的描述字符串和擴(kuò)展名感興趣,那么請(qǐng)你重點(diǎn)關(guān)注DoExport這個(gè)函數(shù),特別是ExpInterface *ei這個(gè)參數(shù),請(qǐng)?jiān)?ds Max SDK中查閱ExpInterface的相關(guān)信息,下一章將會(huì)使用到它。另外,你應(yīng)該已經(jīng)了解到,導(dǎo)出程序是從DoExport這個(gè)函數(shù)開始的。


Mesh,Material,Light,Camera,讓我們找到它們

1. 首先說說上一章提到的ExpInterface,在3ds Max9SDK中找到它,可以看到,它繼承于MaxHeapOperators,并包含IScene *theScene。按照它的描述,thsScene是用來枚舉場(chǎng)景中所有node的。看來這個(gè)node就是我們要尋找的對(duì)象。先不急著看node,先來看看IScene *theScene

 

2. IScene有個(gè)重要的函數(shù): int EnumTree ( ITreeEnumProc *proc )。看看這個(gè)函數(shù)的描述:

Remarks:

Implemented by the System..
This may be called to enumerate every INode in the scene. The callback may flag any of these nodes (using INode::FlagForeground()).

Parameters:

ITreeEnumProc *proc
This callback object is called once for each INode in the scene.

Returns:

Nonzero if the process was aborted by the callback (TREE_ABORT); otherwise 0.

可以看出,這個(gè)函數(shù)會(huì)被系統(tǒng)自動(dòng)調(diào)用。它會(huì)枚舉場(chǎng)景中的每個(gè)結(jié)點(diǎn)。對(duì)每個(gè)結(jié)點(diǎn),它再調(diào)用ITreeEnumProc *proc,估計(jì)這個(gè)proc就是用來解析每個(gè)結(jié)點(diǎn)的東西。

 

3. 再看看ITreeEnmuProc的描述:

Description:

This is the callback object used by IScene::EnumTree(). To use it, derive a class from this class, and implement the callback method.

它有個(gè)一個(gè)成員函數(shù)int callback(INode *node)看來我們需要的就是它了,這個(gè)函數(shù)會(huì)讓系統(tǒng)傳給你你要的node,你來實(shí)現(xiàn)這個(gè)callback函數(shù)。

 

4.    看來我們要寫些代碼了(我估計(jì)你也早等不及了),讓我們寫一個(gè)繼承ITreeEnumProc的類:

class MyTreeEnum : public ITreeEnumProc

{

public:

    MyTreeEnum(void);

    ~MyTreeEnum(void);

public:

    int callback( INode *node );

};

然后實(shí)現(xiàn)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;

}

 

接著,讓我們調(diào)用這個(gè)函數(shù),這只需要修改DoExport()函數(shù)

int MaxExportTest::DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options)

{

    MyTreeEnum tempProc;

ei->theScene->EnumTree( &tempProc );

    return TRUE;

}

最后,編譯它,開始調(diào)試,找一個(gè)有物體,材質(zhì),燈光,攝像機(jī)的場(chǎng)景進(jìn)行導(dǎo)出,如果你能在控制臺(tái)輸出窗口看到每個(gè)結(jié)點(diǎn)的名字,說明你的代碼成功了。

 

5.    讓我們?cè)賮砜催@些代碼,首先來關(guān)注INode,根據(jù)3ds max sdk的說明,INode是場(chǎng)景中每個(gè)結(jié)點(diǎn)的接口,它可以代表不同類型的物體,如幾何體,燈光,攝像機(jī)。要訪問這些物體,你就得調(diào)用INode::EvalWorldState,它會(huì)返回一個(gè)ObjectState,這個(gè)ObjectState又包含一個(gè)Object *obj,越說越復(fù)雜了,這兩個(gè)類型都很重要,但我們現(xiàn)在只需要這個(gè)obj來幫助我們判斷當(dāng)前傳入的node是屬于什么類型。這就需要兩個(gè)函數(shù),canConvertToType()SuperClassID(),它們是obj的成員函數(shù)。在這之前,先看看Super Class IDClass ID,這是Super Class ID的一段摘要:

GEOMOBJECT_CLASS_ID - Used by geometric objects.

CAMERA_CLASS_ID - Used by plug-in cameras.

LIGHT_CLASS_ID - Used by plug-in lights.

SHAPE_CLASS_ID - Used by spline shapes.

HELPER_CLASS_ID - Used by helper objects.

SYSTEM_CLASS_ID - Used by system plug-ins.

OSM_CLASS_ID - Used by Object Space Modifiers.

WSM_CLASS_ID - Used by Space Warp Modifiers (World Space Modifiers).

 

這是Class ID的一段摘要:

Subclasses of GEOMOBJECT_CLASS_ID

Built into core

TRIOBJ_CLASS_ID - TriObject

PATCHOBJ_CLASS_ID - PatchObject

Subclasses of LIGHT_CLASS_ID:

OMNI_LIGHT_CLASS_ID - Omni Light

SPOT_LIGHT_CLASS_ID - Spot Light

DIR_LIGHT_CLASS_ID - Directional Light

FSPOT_LIGHT_CLASS_ID - Free Spot Light

TDIR_LIGHT_CLASS_ID - Target Directional Light

 

由此可見,Class ID 應(yīng)該是Super Class ID的一個(gè)子集,比如要判斷是否是燈光,只要看它的Super Class ID是否是LIGHT_CLASS_ID,函數(shù)SuperClassID()可以達(dá)到這個(gè)目的。而要看它具體是哪種燈光,就需要canConvertToType函數(shù)了。不過讓我不解的是,攝像機(jī)和燈光的目標(biāo)節(jié)點(diǎn)也被歸為GEOMOBJECT_CLASS_ID了,我不知道3ds Max為什么要這樣設(shè)計(jì),所以我只好用canConverToType來判斷這個(gè)物體是否為三角網(wǎng)物體。

 

6.    好了,我們大概找到了我們需要的東西,下一章,我會(huì)示范如何從這些較大的范圍中得到我所感興趣的具體的信息,如燈光的位置和方向,以及最重要的Mesh的頂點(diǎn)信息等。


Mesh,Material,Light,Camera,讓我們解析他們

1.         首先說說meshmaterial吧,這兩者結(jié)合相當(dāng)密切。上一章說到如何獲得TriObject,通過它可以獲得一個(gè)mesh

Mesh* pMesh = &tri->GetMesh();

 

SDK里查看mesh的描述,發(fā)現(xiàn)它可以導(dǎo)出很多信息,而我們一般希望從mesh中獲得頂點(diǎn)坐標(biāo),法線向量,紋理坐標(biāo),頂點(diǎn)顏色等信息,以及頂點(diǎn)的索引值。對(duì)于只貼了一個(gè)紋理的mesh,我們可以簡(jiǎn)單的獲得這些信息。

Mesh* pMesh = &tri->GetMesh();

int VerticesNum = pMesh->getNumVerts()

for ( int i=0; i<VerticesNum; i++ )

{

    Point3 Coord, Normal, TCoord, VColor;

    if( pMesh->getNumVerts()>0 ) //導(dǎo)出頂點(diǎn)坐標(biāo)

    {

        Coord = pMesh->getVert( i );

    }

 

    if ( pMesh->faces )          //導(dǎo)出法線向量

    {

        Normal = pMesh->getNormal( j );

    }

 

    if ( pMesh->getNumTVerts()>0 )//導(dǎo)出紋理坐標(biāo)

    {

        TCoord = pMesh->tVerts[j] ;

    }

 

if ( pMesh-> vertCol ) //導(dǎo)出頂點(diǎn)顏色

{

    VColor = pMesh->vertCol[i];

}

}

然后是這個(gè)mesh使用的紋理,這里僅列舉漫反射貼圖:

Mtl *pMtl = pNode->GetNode 

if ( pMtl!=NULL )

{              

    Texmap *pTexMap = pMtl->GetSubTexmap(ID_DI); //獲取漫反射材質(zhì)的貼圖

    BitmapTex *pBMPTex = (BitmapTex *) pTexMap

    if ( pBMPTex )

    {

        char *MapName = pBMPTex->GetMapName();    //獲取漫反射貼圖的路徑

    }

}

而對(duì)于貼了多個(gè)紋理的情況,就要復(fù)雜的多,例如一個(gè)立方體,每個(gè)面都貼了一個(gè)紋理,那么就需要知道這個(gè)mesh面的數(shù)量,材質(zhì)的數(shù)量,面和材質(zhì)的對(duì)應(yīng)關(guān)系,面和頂點(diǎn)的對(duì)應(yīng)關(guān)系,等等。我設(shè)計(jì)了一種解析方法,經(jīng)過一些模型的測(cè)試,結(jié)果正確,拿出來供大家參考參考。

先說說具體思想,假設(shè)一個(gè)立方體每個(gè)面都貼了一張紋理,那么可以把這個(gè)mesh看作劃分了6個(gè)子mesh,一個(gè)子mesh就是一個(gè)面。首先,遍歷原來mesh的所有面,計(jì)算非重復(fù)的材質(zhì)ID。從而得知這個(gè)mesh的子mesh個(gè)數(shù)。然后再次遍歷原來mesh所有的面,將所有具有相同材質(zhì)ID的面的頂點(diǎn)集合到一個(gè)子mesh,這些頂點(diǎn)只存儲(chǔ)該頂點(diǎn)在原mesh中的索引。當(dāng)然,需要重新計(jì)算這些頂點(diǎn)在子mesh里的索引值。因此再遍歷原mesh的所有面。

struct FaceVertex

{

    int m_Index;     //表示該頂點(diǎn)在原mesh里的索引值

    int m_FaceIndex; //表示該頂點(diǎn)所屬的面在原mesh里的索引值

    int m_TriIndex; //表示該頂點(diǎn)在所屬三角形里的索引值,值為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用到的子材質(zhì)的數(shù)量,用來計(jì)算子Mesh的劃分

                           //每個(gè)元素表示一個(gè)材質(zhì)ID。

    for( int i=0; i<pMesh->getNumFaces(); i++ )

    {

        /*計(jì)算子Mesh數(shù)量,通過計(jì)算所有面使用的非重復(fù)材質(zhì)數(shù)量而得*/

        int MatID = pMesh->getFaceMtlIndex(i);

vector<int>::iterator MatIndex = find( MeshMtls.begin(), MeshMtls.end(), MatID );

        if ( MatIndex == MeshMtls.end() )

        {

MeshMtls.push_back(MatID );//該材質(zhì)未在MeshMtls里出現(xiàn)過,說明是個(gè)

//新材質(zhì)

        }

    }

 

    //DivideMeshArray,計(jì)算Mesh劃分的拓?fù)湫畔?/span>

    vector<MaxDivideMesh> DivideMeshArray;

    DivideMeshArray.resize( MeshMtls.size() );//指定劃分?jǐn)?shù)量

 

    //

    //此處有內(nèi)存的分配

//GMeshD3D是我自己設(shè)計(jì)的一個(gè)類型,用來表示一個(gè)子Mesh

    //GTextureD3D用來表示Texture

    GMeshD3D *pMeshArray = new GMeshD3D[MeshMtls.size()];

    GTextureD3D *pTextureArray = new GTextureD3D[MeshMtls.size()];

 

    GObjectMAXD3D tempObj; //GObjectMAXD3D表示一個(gè)模型,有n個(gè)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); //獲取漫反射材質(zhì)的貼//圖

            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 );    

        }

    }

 

    /*這里開始對(duì)原有mesh進(jìn)行重新劃分*/

    for( int i=0; i<pMesh->getNumFaces(); i++ )

    {

        int MatID = pMesh->getFaceMtlIndex(i); //計(jì)算該面的材質(zhì)ID

vector<int>::iterator MatIndex = find( MeshMtls.begin(), MeshMtls.end(), MatID );

        int MeshID = MatIndex - MeshMtls.begin(); //計(jì)算該MatID在TextureArray的紋理索引,使MeshID從0開始編號(hào)

        for ( int j=0; j<3; j++)

        {

            int Index = pMesh->faces[i].v[j];//Index表示在全局頂點(diǎn)數(shù)組里的索引

            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里尋找頂點(diǎn)索引值相同的頂點(diǎn),如果沒找到該頂點(diǎn),表示//要添加該頂點(diǎn)

            {

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 );

            }

        }

    }

 

/*計(jì)算頂點(diǎn)在每個(gè)子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(); //計(jì)算該MatID的紋理索引

        for ( int j=0; j<3; j++)

        {

            int Index = pMesh->faces[i].v[j];

            FaceVertex tempVertex;

            tempVertex.m_Index = Index;

//若在子mesh里能找到該點(diǎn),則計(jì)算該點(diǎn)在子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表//示該頂點(diǎn)在子Mesh的索引

                DivideMeshArray[MeshID].m_IndexArray.push_back( VertexIndex );

            }

        }

    }

 

/*余下部分開始到處頂點(diǎn)信息*/

    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 ) //導(dǎo)出頂點(diǎn)坐標(biāo)

            {

                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 )          //導(dǎo)出法線向量

            {

                Normal = pMesh->getNormal( DivideMeshArray[i].m_VertexArray[j].m_Index );

                tempVertex.NormalVector = D3DXVECTOR3( Normal.x, Normal.y, Normal.z );

            }

 

            if ( pMesh->getNumTVerts()>0 )//導(dǎo)出紋理坐標(biāo)

            {

                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;

}

 

2.       導(dǎo)出攝像機(jī),我們一般關(guān)注攝像機(jī)的位置,方向,FOV角,近遠(yuǎn)平面之類的信息。還好3ds Max SDK 里提供CameraObjectCameraStateCameraGenCamera等對(duì)象來訪問這些信息。不過攝像機(jī)的位置和方向等信息需要通過節(jié)點(diǎn)函數(shù)來訪問。比較簡(jiǎn)單,直接見代碼。

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 );//獲取攝像機(jī)源點(diǎn)的變換矩陣

    Matrix3 destMatrix;

    pNode->GetTargetTM( 0, destMatrix ); //獲取攝像機(jī)目標(biāo)點(diǎn)的變換矩陣

    cs.fov;      //獲取FOV角

    cs.hither;   //獲取攝像機(jī)近平面

    cs.yon;      //獲取攝像機(jī)遠(yuǎn)平面

}

 

3.       導(dǎo)出燈光,與攝像機(jī)差不多,也是位置和方向需要結(jié)點(diǎn)函數(shù)來獲得,而其余信息通過訪問LightLightStateGenLightLightObject獲得。

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) //導(dǎo)出燈光類型

{

        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;

    }

}

呼。。。。。終于寫完了,感覺還是很多東西沒說清楚。希望大家多學(xué)學(xué)3ds Max SDK,給我多提出意見,呵呵。