source: OGRE/trunk/ogrenew/Tools/MilkshapeExport/src/MilkshapePlugin.cpp @ 657

Revision 657, 37.2 KB checked in by mattausch, 19 years ago (diff)

added ogre dependencies and patched ogre sources

Line 
1/*
2-----------------------------------------------------------------------------
3This source file is part of OGRE
4    (Object-oriented Graphics Rendering Engine)
5For the latest info, see http://www.ogre3d.org/
6
7Copyright (c) 2000-2005 The OGRE Team
8Also see acknowledgements in Readme.html
9
10This program is free software; you can redistribute it and/or modify it under
11the terms of the GNU Lesser General Public License as published by the Free Software
12Foundation; either version 2 of the License, or (at your option) any later
13version.
14
15This program is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
18
19You should have received a copy of the GNU Lesser General Public License along with
20this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21Place - Suite 330, Boston, MA 02111-1307, USA, or go to
22http://www.gnu.org/copyleft/lesser.txt.
23-----------------------------------------------------------------------------
24*/
25
26#include "MilkshapePlugin.h"
27#include "Ogre.h"
28#include "msLib.h"
29#include "resource.h"
30#include "OgreStringConverter.h"
31#include "OgreDefaultHardwareBufferManager.h"
32#include "OgreHardwareVertexBuffer.h"
33#include "OgreVertexIndexData.h"
34#include "OgreResourceGroupManager.h"
35
36
37//---------------------------------------------------------------------
38MilkshapePlugin::MilkshapePlugin ()
39{
40    strcpy(mTitle, "OGRE Mesh / Skeleton...");
41
42}
43//---------------------------------------------------------------------
44MilkshapePlugin::~MilkshapePlugin ()
45{
46    // do nothing
47}
48//---------------------------------------------------------------------
49int MilkshapePlugin::GetType ()
50{
51    return cMsPlugIn::eTypeExport;
52}
53//---------------------------------------------------------------------
54const char* MilkshapePlugin::GetTitle ()
55{
56    return mTitle;
57}
58//---------------------------------------------------------------------
59int MilkshapePlugin::Execute (msModel* pModel)
60{
61    // Do nothing if no model selected
62    if (!pModel)
63        return -1;
64
65        Ogre::LogManager logMgr;
66        logMgr.createLog("msOgreExporter.log", true);
67        logMgr.logMessage("OGRE Milkshape Exporter Log");
68        logMgr.logMessage("---------------------------");
69        Ogre::ResourceGroupManager resGrpMgr;
70
71        //
72    // check, if we have something to export
73    //
74    if (msModel_GetMeshCount (pModel) == 0)
75    {
76        ::MessageBox (NULL, "The model is empty!  Nothing exported!", "OGRE Export", MB_OK | MB_ICONWARNING);
77        return 0;
78    }
79
80    if (!showOptions()) return 0;
81
82    if (exportMesh)
83    {
84        doExportMesh(pModel);
85    }
86
87    return 0;
88
89}
90//---------------------------------------------------------------------
91MilkshapePlugin *plugin;
92BOOL MilkshapePlugin::DlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
93{
94    HWND hwndDlgItem;
95
96    switch (iMsg)
97    {
98
99    case WM_INITDIALOG:
100        // Center myself
101        int x, y, screenWidth, screenHeight;
102        RECT rcDlg;
103        GetWindowRect(hDlg, &rcDlg);
104        screenWidth = GetSystemMetrics(SM_CXFULLSCREEN);
105        screenHeight = GetSystemMetrics(SM_CYFULLSCREEN);
106
107        x = (screenWidth / 2) - ((rcDlg.right - rcDlg.left) / 2);
108        y = (screenHeight / 2) - ((rcDlg.bottom - rcDlg.top) / 2);
109
110        MoveWindow(hDlg, x, y, (rcDlg.right - rcDlg.left),
111            (rcDlg.bottom - rcDlg.top), TRUE);
112
113        // Check mesh export
114        hwndDlgItem = GetDlgItem(hDlg, IDC_EXPORT_MESH);
115        SendMessage(hwndDlgItem, BM_SETCHECK, BST_CHECKED,0);
116
117        // Set default LOD options
118        hwndDlgItem = GetDlgItem(hDlg, IDC_NUM_LODS);
119        SetWindowText(hwndDlgItem, "5");
120        hwndDlgItem = GetDlgItem(hDlg, IDC_LOD_DEPTH);
121        SetWindowText(hwndDlgItem, "500");
122        hwndDlgItem = GetDlgItem(hDlg, IDC_LOD_VRQ);
123        SetWindowText(hwndDlgItem, "25");
124        hwndDlgItem = GetDlgItem(hDlg, IDC_CBO_LOD_STYLE);
125        SendMessage(hwndDlgItem, CB_ADDSTRING, 0, (LPARAM)"percent");
126        SendMessage(hwndDlgItem, CB_ADDSTRING, 0, (LPARAM)"vertices");
127        SendMessage(hwndDlgItem, CB_SETCURSEL, 0, 0);
128
129        // Check edge lists
130        hwndDlgItem = GetDlgItem(hDlg, IDC_EDGE_LISTS);
131        SendMessage(hwndDlgItem, BM_SETCHECK, BST_CHECKED,0);
132
133        // Check skeleton export
134        hwndDlgItem = GetDlgItem(hDlg, IDC_EXPORT_SKEL);
135        SendMessage(hwndDlgItem, BM_SETCHECK, BST_CHECKED,0);
136
137        // Set default FPS
138        hwndDlgItem = GetDlgItem(hDlg, IDC_FPS);
139        SetWindowText(hwndDlgItem, "24");
140
141
142        return TRUE;
143    case WM_COMMAND:
144        switch (LOWORD(wParam))
145        {
146            case IDOK:
147                char val[20];
148
149                // Set options
150                hwndDlgItem = GetDlgItem(hDlg, IDC_EXPORT_MESH);
151                plugin->exportMesh = (SendMessage(hwndDlgItem, BM_GETCHECK, 0, 0) == BST_CHECKED) ? true : false;
152
153                hwndDlgItem = GetDlgItem(hDlg, IDC_GENERATE_LOD);
154                plugin->generateLods = (SendMessage(hwndDlgItem, BM_GETCHECK, 0, 0) == BST_CHECKED) ? true : false;
155                if (plugin->generateLods)
156                {
157                    hwndDlgItem = GetDlgItem(hDlg, IDC_NUM_LODS);
158                    GetWindowText(hwndDlgItem, val, 20);
159                    plugin->numLods = atoi(val);
160                    if (!plugin->numLods)
161                    {
162                        MessageBox(hDlg, "Invalid number of LODs specified", "Validation error", MB_OK | MB_ICONEXCLAMATION);
163                        return TRUE;
164                    }
165                    hwndDlgItem = GetDlgItem(hDlg, IDC_LOD_DEPTH);
166                    GetWindowText(hwndDlgItem, val, 20);
167                    plugin->lodDepthIncrement = atof(val);
168                    if (!plugin->lodDepthIncrement)
169                    {
170                        MessageBox(hDlg, "Invalid LOD depth increment specified", "Validation error", MB_OK | MB_ICONEXCLAMATION);
171                        return TRUE;
172                    }
173                    hwndDlgItem = GetDlgItem(hDlg, IDC_LOD_VRQ);
174                    GetWindowText(hwndDlgItem, val, 20);
175                    plugin->lodReductionAmount = atof(val);
176                    if (!plugin->lodReductionAmount)
177                    {
178                        MessageBox(hDlg, "Invalid LOD reduction amount specified", "Validation error", MB_OK | MB_ICONEXCLAMATION);
179                        return TRUE;
180                    }
181                    hwndDlgItem = GetDlgItem(hDlg, IDC_CBO_LOD_STYLE);
182                    int sel = SendMessage(hwndDlgItem, CB_GETCURSEL,0,0);
183                    if (sel == 0)
184                    {
185                        // percent
186                        plugin->lodReductionMethod = Ogre::ProgressiveMesh::VRQ_PROPORTIONAL;
187                        // adjust percent to parametric
188                        plugin->lodReductionAmount *= 0.01;
189                    }
190                    else if (sel == 1)
191                    {
192                        // absolute
193                        plugin->lodReductionMethod = Ogre::ProgressiveMesh::VRQ_CONSTANT;
194                    }
195                    else
196                    {
197                        MessageBox(hDlg, "Invalid LOD reduction method specified", "Validation error", MB_OK | MB_ICONEXCLAMATION);
198                        return TRUE;
199                    }
200
201                }
202
203                hwndDlgItem = GetDlgItem(hDlg, IDC_EDGE_LISTS);
204                plugin->generateEdgeLists = (SendMessage(hwndDlgItem, BM_GETCHECK, 0, 0) == BST_CHECKED) ? true : false;
205
206                hwndDlgItem = GetDlgItem(hDlg, IDC_TANGENT_VECTORS);
207                plugin->generateTangents = (SendMessage(hwndDlgItem, BM_GETCHECK, 0, 0) == BST_CHECKED) ? true : false;
208
209                hwndDlgItem = GetDlgItem(hDlg, IDC_EXPORT_SKEL);
210                plugin->exportSkeleton = (SendMessage(hwndDlgItem, BM_GETCHECK, 0, 0) == BST_CHECKED) ? true : false;
211
212
213                hwndDlgItem = GetDlgItem(hDlg, IDC_SPLIT_ANIMATION);
214                plugin->splitAnimations = (SendMessage(hwndDlgItem, BM_GETCHECK, 0, 0) == BST_CHECKED) ? true : false;
215
216                                hwndDlgItem = GetDlgItem(hDlg, IDC_EXPORT_MATERIALS);
217                                plugin->exportMaterials = (SendMessage(hwndDlgItem, BM_GETCHECK, 0, 0) == BST_CHECKED) ? true : false;
218
219                                hwndDlgItem = GetDlgItem(hDlg, IDC_FPS);
220                GetWindowText(hwndDlgItem, val, 20);
221                plugin->fps = atof(val);
222                if (!plugin->fps)
223                {
224                    MessageBox(hDlg, "Invalid frame rate specified", "Validation error", MB_OK | MB_ICONEXCLAMATION);
225                    return TRUE;
226                }
227
228                EndDialog(hDlg, TRUE);
229                return TRUE;
230            case IDCANCEL:
231                EndDialog(hDlg, FALSE);
232                return FALSE;
233        }
234    }
235
236    return FALSE;
237
238}
239
240//---------------------------------------------------------------------
241bool MilkshapePlugin::showOptions(void)
242{
243    HINSTANCE hInst = GetModuleHandle("msOGREExporter.dll");
244    plugin = this;
245    exportMesh = true;
246    exportSkeleton = false;
247
248        return (DialogBox(hInst, MAKEINTRESOURCE(IDD_OPTIONS), NULL, DlgProc) == TRUE);
249
250
251
252
253
254
255}
256
257void MilkshapePlugin::doExportMesh(msModel* pModel)
258{
259
260
261    // Create singletons
262    Ogre::SkeletonManager skelMgr;
263    Ogre::DefaultHardwareBufferManager defHWBufMgr;
264        Ogre::LogManager& logMgr = Ogre::LogManager::getSingleton();
265        Ogre::MeshManager meshMgr;
266
267
268    //
269    // choose filename
270    //
271    OPENFILENAME ofn;
272    memset (&ofn, 0, sizeof (OPENFILENAME));
273
274    char szFile[MS_MAX_PATH];
275    char szFileTitle[MS_MAX_PATH];
276    char szDefExt[32] = "mesh";
277    char szFilter[128] = "OGRE .mesh Files (*.mesh)\0*.mesh\0All Files (*.*)\0*.*\0\0";
278    szFile[0] = '\0';
279    szFileTitle[0] = '\0';
280
281    ofn.lStructSize = sizeof (OPENFILENAME);
282    ofn.lpstrDefExt = szDefExt;
283    ofn.lpstrFilter = szFilter;
284    ofn.lpstrFile = szFile;
285    ofn.nMaxFile = MS_MAX_PATH;
286    ofn.lpstrFileTitle = szFileTitle;
287    ofn.nMaxFileTitle = MS_MAX_PATH;
288    ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
289    ofn.lpstrTitle = "Export to OGRE Mesh";
290
291    if (!::GetSaveFileName (&ofn))
292        return /*0*/;
293
294    logMgr.logMessage("Creating Mesh object...");
295    Ogre::MeshPtr ogreMesh = Ogre::MeshManager::getSingleton().create("export",
296        Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
297    logMgr.logMessage("Mesh object created.");
298
299    bool foundBoneAssignment = false;
300
301    // No shared geometry
302    int i;
303    size_t j;
304    Ogre::Vector3 min, max, currpos;
305    Ogre::Real maxSquaredRadius;
306    bool first = true;
307    for (i = 0; i < msModel_GetMeshCount (pModel); i++)
308    {
309        msMesh *pMesh = msModel_GetMeshAt (pModel, i);
310
311
312        logMgr.logMessage("Creating SubMesh object...");
313        Ogre::SubMesh* ogreSubMesh = ogreMesh->createSubMesh();
314        logMgr.logMessage("SubMesh object created.");
315        // Set material
316        logMgr.logMessage("Getting SubMesh Material...");
317        int matIdx = msMesh_GetMaterialIndex(pMesh);
318
319        if (matIdx == -1)
320        {
321            // No material, use blank
322            ogreSubMesh->setMaterialName("BaseWhite");
323            logMgr.logMessage("No Material, using default 'BaseWhite'.");
324        }
325        else
326        {
327
328            msMaterial *pMat = msModel_GetMaterialAt(pModel, matIdx);
329            ogreSubMesh->setMaterialName(pMat->szName);
330            logMgr.logMessage("SubMesh Material Done.");
331        }
332
333
334        logMgr.logMessage("Setting up geometry...");
335        // Set up mesh geometry
336        ogreSubMesh->vertexData = new Ogre::VertexData();
337        ogreSubMesh->vertexData->vertexCount = msMesh_GetVertexCount (pMesh);
338        ogreSubMesh->vertexData->vertexStart = 0;
339        Ogre::VertexBufferBinding* bind = ogreSubMesh->vertexData->vertexBufferBinding;
340        Ogre::VertexDeclaration* decl = ogreSubMesh->vertexData->vertexDeclaration;
341        // Always 1 texture layer, 2D coords
342        #define POSITION_BINDING 0
343        #define NORMAL_BINDING 1
344        #define TEXCOORD_BINDING 2
345        decl->addElement(POSITION_BINDING, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
346        decl->addElement(NORMAL_BINDING, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
347        decl->addElement(TEXCOORD_BINDING, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES);
348        // Create buffers
349        Ogre::HardwareVertexBufferSharedPtr pbuf = Ogre::HardwareBufferManager::getSingleton().
350            createVertexBuffer(decl->getVertexSize(POSITION_BINDING), ogreSubMesh->vertexData->vertexCount,
351                Ogre::HardwareBuffer::HBU_DYNAMIC, false);
352        Ogre::HardwareVertexBufferSharedPtr nbuf = Ogre::HardwareBufferManager::getSingleton().
353            createVertexBuffer(decl->getVertexSize(NORMAL_BINDING), ogreSubMesh->vertexData->vertexCount,
354                Ogre::HardwareBuffer::HBU_DYNAMIC, false);
355        Ogre::HardwareVertexBufferSharedPtr tbuf = Ogre::HardwareBufferManager::getSingleton().
356            createVertexBuffer(decl->getVertexSize(TEXCOORD_BINDING), ogreSubMesh->vertexData->vertexCount,
357                Ogre::HardwareBuffer::HBU_DYNAMIC, false);
358        bind->setBinding(POSITION_BINDING, pbuf);
359        bind->setBinding(NORMAL_BINDING, nbuf);
360        bind->setBinding(TEXCOORD_BINDING, tbuf);
361
362        ogreSubMesh->useSharedVertices = false;
363
364        float* pPos = static_cast<float*>(
365            pbuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
366        float* pTex = static_cast<float*>(
367            tbuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
368
369        logMgr.logMessage("Doing positions and texture coords...");
370        for (j = 0; j < ogreSubMesh->vertexData->vertexCount; ++j)
371        {
372            logMgr.logMessage("Doing vertex " + Ogre::StringConverter::toString(j));
373            msVertex *pVertex = msMesh_GetVertexAt (pMesh, j);
374            msVec3 Vertex;
375            msVec2 uv;
376
377            msVertex_GetVertex (pVertex, Vertex);
378            msVertex_GetTexCoords (pVertex, uv);
379
380            *pPos++ = Vertex[0];
381            *pPos++ = Vertex[1];
382            *pPos++ = Vertex[2];
383            // Deal with bounds
384            currpos = Ogre::Vector3(Vertex[0], Vertex[1], Vertex[2]);
385            if (first)
386            {
387                min = max = currpos;
388                maxSquaredRadius = currpos.squaredLength();
389                first = false;
390            }
391            else
392            {
393                min.makeFloor(currpos);
394                max.makeCeil(currpos);
395                maxSquaredRadius = std::max(maxSquaredRadius, currpos.squaredLength());
396            }
397
398            *pTex++ = uv[0];
399            *pTex++ = uv[1];
400
401            int boneIdx = msVertex_GetBoneIndex(pVertex);
402            if (boneIdx != -1)
403            {
404                foundBoneAssignment = true;
405                Ogre::VertexBoneAssignment vertAssign;
406                vertAssign.boneIndex = boneIdx;
407                vertAssign.vertexIndex = j;
408                vertAssign.weight = 1.0; // Milkshape only supports single assignments
409                ogreSubMesh->addBoneAssignment(vertAssign);
410            }
411
412
413        }
414        pbuf->unlock();
415        tbuf->unlock();
416
417        logMgr.logMessage("Doing normals and indexes...");
418        // Aargh, Milkshape uses stupid separate normal indexes for the same vertex like 3DS
419        // Normals aren't described per vertex but per triangle vertex index
420        // Pain in the arse, we have to do vertex duplication again if normals differ at a vertex (non smooth)
421        // WHY don't people realise this format is a pain for passing to 3D APIs in vertex buffers?
422        float* pNorm = static_cast<float*>(
423            nbuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
424        ogreSubMesh->indexData->indexCount = msMesh_GetTriangleCount (pMesh) * 3;
425        // Always use 16-bit buffers, Milkshape can't handle more anyway
426        Ogre::HardwareIndexBufferSharedPtr ibuf = Ogre::HardwareBufferManager::getSingleton().
427            createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT,
428            ogreSubMesh->indexData->indexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);
429        ogreSubMesh->indexData->indexBuffer = ibuf;
430        unsigned short *pIdx = static_cast<unsigned short*>(
431            ibuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
432        for (j = 0; j < ogreSubMesh->indexData->indexCount; j+=3)
433        {
434            msTriangle *pTriangle = msMesh_GetTriangleAt (pMesh, j/3);
435
436            word nIndices[3];
437            msTriangle_GetVertexIndices (pTriangle, nIndices);
438
439            msVec3 Normal;
440            int k, normIdx, vertIdx;
441            for (k = 0; k < 3; ++k)
442            {
443                vertIdx = nIndices[k];
444                // Face index
445                pIdx[j+k] = vertIdx;
446
447                // Vertex normals
448                // For the moment, ignore any discrepancies per vertex
449                normIdx = pTriangle->nNormalIndices[k];
450                msMesh_GetVertexNormalAt (pMesh, normIdx, Normal);
451
452                pNorm[(vertIdx*3)] = Normal[0];
453                pNorm[(vertIdx*3)+1] = Normal[1];
454                pNorm[(vertIdx*3)+2] = Normal[2];
455
456            }
457
458
459        } // Faces
460        nbuf->unlock();
461        ibuf->unlock();
462
463        // Now use Ogre's ability to reorganise the vertex buffers the best way
464        Ogre::VertexDeclaration* newDecl =
465            ogreSubMesh->vertexData->vertexDeclaration->getAutoOrganisedDeclaration(
466                foundBoneAssignment);
467        Ogre::BufferUsageList bufferUsages;
468        for (size_t u = 0; u <= newDecl->getMaxSource(); ++u)
469            bufferUsages.push_back(Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);
470        ogreSubMesh->vertexData->reorganiseBuffers(newDecl, bufferUsages);
471
472
473        logMgr.logMessage("Geometry done.");
474    } // SubMesh
475
476    // Set bounds
477    ogreMesh->_setBoundingSphereRadius(Ogre::Math::Sqrt(maxSquaredRadius));
478    ogreMesh->_setBounds(Ogre::AxisAlignedBox(min, max), false);
479
480
481    // Keep hold of a Skeleton pointer for deletion later
482    // Mesh uses Skeleton pointer for skeleton name
483    Ogre::SkeletonPtr pSkel;
484
485    if (exportSkeleton && foundBoneAssignment)
486    {
487        // export skeleton, also update mesh to point to it
488        pSkel = doExportSkeleton(pModel, ogreMesh);
489    }
490    else if (!exportSkeleton && foundBoneAssignment)
491    {
492        // We've found bone assignments, but skeleton is not to be exported
493        // Prompt the user to find the skeleton
494        if (!locateSkeleton(ogreMesh))
495            return;
496
497    }
498
499    // Export
500    logMgr.logMessage("Creating MeshSerializer..");
501    Ogre::MeshSerializer serializer;
502    logMgr.logMessage("MeshSerializer created.");
503
504    // Generate LODs if required
505    if (generateLods)
506    {
507        // Build LOD depth list
508        Ogre::Mesh::LodDistanceList distList;
509        float depth = 0;
510        for (unsigned short depthidx = 0; depthidx < numLods; ++depthidx)
511        {
512            depth += lodDepthIncrement;
513            distList.push_back(depth);
514        }
515
516        ogreMesh->generateLodLevels(distList, lodReductionMethod, lodReductionAmount);
517    }
518
519    if (generateEdgeLists)
520    {
521        ogreMesh->buildEdgeList();
522    }
523
524    if (generateTangents)
525    {
526        ogreMesh->buildTangentVectors();
527    }
528
529    // Export
530    Ogre::String msg;
531        msg  = "Exporting mesh data to file '" + Ogre::String(szFile) + "'";
532    logMgr.logMessage(msg);
533    serializer.exportMesh(ogreMesh.getPointer(), szFile);
534    logMgr.logMessage("Export successful");
535
536    Ogre::MeshManager::getSingleton().remove(ogreMesh->getHandle());
537    if (!pSkel.isNull())
538        Ogre::SkeletonManager::getSingleton().remove(pSkel->getHandle());
539
540        if (exportMaterials && msModel_GetMaterialCount(pModel) > 0)
541        {
542                doExportMaterials(pModel);
543        }
544}
545
546
547Ogre::SkeletonPtr MilkshapePlugin::doExportSkeleton(msModel* pModel, Ogre::MeshPtr& mesh)
548{
549    Ogre::LogManager &logMgr = Ogre::LogManager::getSingleton();
550    Ogre::String msg;
551
552    //
553    // choose filename
554    //
555    OPENFILENAME ofn;
556    memset (&ofn, 0, sizeof (OPENFILENAME));
557
558    char szFile[MS_MAX_PATH];
559    char szFileTitle[MS_MAX_PATH];
560    char szDefExt[32] = "skeleton";
561    char szFilter[128] = "OGRE .skeleton Files (*.skeleton)\0*.skeleton\0All Files (*.*)\0*.*\0\0";
562    szFile[0] = '\0';
563    szFileTitle[0] = '\0';
564
565    ofn.lStructSize = sizeof (OPENFILENAME);
566    ofn.lpstrDefExt = szDefExt;
567    ofn.lpstrFilter = szFilter;
568    ofn.lpstrFile = szFile;
569    ofn.nMaxFile = MS_MAX_PATH;
570    ofn.lpstrFileTitle = szFileTitle;
571    ofn.nMaxFileTitle = MS_MAX_PATH;
572    ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
573    ofn.lpstrTitle = "Export to OGRE Skeleton";
574
575    if (!::GetSaveFileName (&ofn))
576        return Ogre::SkeletonPtr();
577
578    // Strip off the path
579    Ogre::String skelName = szFile;
580    size_t lastSlash = skelName.find_last_of("\\");
581    skelName = skelName.substr(lastSlash+1);
582
583    // Set up
584    logMgr.logMessage("Trying to create Skeleton object");
585    Ogre::SkeletonPtr ogreskel = Ogre::SkeletonManager::getSingleton().create(skelName,
586        Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
587    logMgr.logMessage("Skeleton object created");
588
589    // Complete the details
590
591    // Do the bones
592    int numBones = msModel_GetBoneCount(pModel);
593        msg = "Number of bones: " + Ogre::StringConverter::toString(numBones);
594    logMgr.logMessage(msg);
595
596    int i;
597    // Create all the bones in turn
598    for (i = 0; i < numBones; ++i)
599    {
600        msBone* bone = msModel_GetBoneAt(pModel, i);
601        Ogre::Bone* ogrebone = ogreskel->createBone(bone->szName);
602
603        msVec3 msBonePos, msBoneRot;
604        msBone_GetPosition(bone, msBonePos);
605        msBone_GetRotation(bone, msBoneRot);
606
607        Ogre::Vector3 bonePos(msBonePos[0], msBonePos[1], msBonePos[2]);
608        ogrebone->setPosition(bonePos);
609        // Hmm, Milkshape has chosen a Euler angle representation of orientation which is not smart
610        // Rotation Matrix or Quaternion would have been the smarter choice
611        // Might we have Gimbal lock here? What order are these 3 angles supposed to be applied?
612        // Grr, we'll try our best anyway...
613        Ogre::Quaternion qx, qy, qz, qfinal;
614        qx.FromAngleAxis(Ogre::Radian(msBoneRot[0]), Ogre::Vector3::UNIT_X);
615        qy.FromAngleAxis(Ogre::Radian(msBoneRot[1]), Ogre::Vector3::UNIT_Y);
616        qz.FromAngleAxis(Ogre::Radian(msBoneRot[2]), Ogre::Vector3::UNIT_Z);
617
618        // Assume rotate by x then y then z
619        qfinal = qz * qy * qx;
620        ogrebone->setOrientation(qfinal);
621
622                Ogre::StringUtil::StrStreamType msgStream;
623        msgStream << "Bone #" << i << ": " <<
624            "Name='" << bone->szName << "' " <<
625            "Position: " << bonePos << " " <<
626            "Ms3d Rotation: {" << msBoneRot[0] << ", " << msBoneRot[1] << ", " << msBoneRot[2] << "} " <<
627            "Orientation: " << qfinal;
628        logMgr.logMessage(msgStream.str());
629
630
631    }
632    // Now we've created all the bones, link them up
633    logMgr.logMessage("Establishing bone hierarchy..");
634    for (i = 0; i < numBones; ++i)
635    {
636        msBone* bone = msModel_GetBoneAt(pModel, i);
637
638        if (strlen(bone->szParentName) == 0)
639        {
640            // Root bone
641            msg = "Root bone detected: Name='" + Ogre::String(bone->szName) + "' Index="
642                                + Ogre::StringConverter::toString(i);
643            logMgr.logMessage(msg);
644        }
645        else
646        {
647            Ogre::Bone* ogrechild = ogreskel->getBone(bone->szName);
648            Ogre::Bone* ogreparent = ogreskel->getBone(bone->szParentName);
649
650            if (ogrechild == 0)
651            {
652                msg = "Error: could not locate child bone '" +
653                                        Ogre::String(bone->szName) + "'";
654                logMgr.logMessage(msg);
655                continue;
656            }
657            if (ogreparent == 0)
658            {
659                msg = "Error: could not locate parent bone '"
660                                        + Ogre::String(bone->szParentName) + "'";
661                logMgr.logMessage(msg);
662                continue;
663            }
664            // Make child
665            ogreparent->addChild(ogrechild);
666        }
667
668
669    }
670    logMgr.logMessage("Bone hierarchy established.");
671
672    // Create the Animation(s)
673    doExportAnimations(pModel, ogreskel);
674
675
676
677    // Create skeleton serializer & export
678    Ogre::SkeletonSerializer serializer;
679    msg = "Exporting skeleton to " + Ogre::String(szFile);
680    logMgr.logMessage(msg);
681    serializer.exportSkeleton(ogreskel.getPointer(), szFile);
682    logMgr.logMessage("Skeleton exported");
683
684
685    msg = "Linking mesh to skeleton file '" + skelName + "'";
686    Ogre::LogManager::getSingleton().logMessage(msg);
687
688    mesh->_notifySkeleton(ogreskel);
689
690    return ogreskel;
691
692}
693
694bool MilkshapePlugin::locateSkeleton(Ogre::MeshPtr& mesh)
695{
696    //
697    // choose filename
698    //
699    OPENFILENAME ofn;
700    memset (&ofn, 0, sizeof (OPENFILENAME));
701
702    char szFile[MS_MAX_PATH];
703    char szFileTitle[MS_MAX_PATH];
704    char szDefExt[32] = "skeleton";
705    char szFilter[128] = "OGRE .skeleton Files (*.skeleton)\0*.skeleton\0All Files (*.*)\0*.*\0\0";
706    szFile[0] = '\0';
707    szFileTitle[0] = '\0';
708
709    ofn.lStructSize = sizeof (OPENFILENAME);
710    ofn.lpstrDefExt = szDefExt;
711    ofn.lpstrFilter = szFilter;
712    ofn.lpstrFile = szFile;
713    ofn.nMaxFile = MS_MAX_PATH;
714    ofn.lpstrFileTitle = szFileTitle;
715    ofn.nMaxFileTitle = MS_MAX_PATH;
716    ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
717    ofn.lpstrTitle = "Locate OGRE Skeleton (since you're not exporting it)";
718
719    if (!::GetOpenFileName (&ofn))
720        return false;
721
722    // Strip off the path
723    Ogre::String skelName = szFile;
724    size_t lastSlash = skelName.find_last_of("\\");
725    skelName = skelName.substr(lastSlash+1);
726
727    Ogre::String msg = "Linking mesh to skeleton file '" + skelName + "'";
728    Ogre::LogManager::getSingleton().logMessage(msg);
729
730    // Create a dummy skeleton for Mesh to link to (saves it trying to load it)
731    Ogre::SkeletonPtr pSkel = Ogre::SkeletonManager::getSingleton().create(skelName,
732        Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
733    Ogre::LogManager::getSingleton().logMessage("Dummy Skeleton object created for link.");
734
735    mesh->_notifySkeleton(pSkel);
736
737    return true;
738
739}
740
741struct SplitAnimationStruct
742{
743    int start;
744    int end;
745    Ogre::String name;
746};
747
748void MilkshapePlugin::doExportMaterials(msModel* pModel)
749{
750        Ogre::LogManager& logMgr = Ogre::LogManager::getSingleton();
751        Ogre::MaterialManager matMgrSgl;
752        Ogre::String msg;
753
754    matMgrSgl.initialise();
755
756        int numMaterials = msModel_GetMaterialCount(pModel);
757        msg = "Number of materials: " + Ogre::StringConverter::toString(numMaterials);
758        logMgr.logMessage(msg);
759
760        OPENFILENAME ofn;
761        memset (&ofn, 0, sizeof (OPENFILENAME));
762
763        char szFile[MS_MAX_PATH];
764        char szFileTitle[MS_MAX_PATH];
765        char szDefExt[32] = "material";
766        char szFilter[128] = "OGRE .material Files (*.material)\0*.material\0All Files (*.*)\0*.*\0\0";
767        szFile[0] = '\0';
768        szFileTitle[0] = '\0';
769
770        ofn.lStructSize = sizeof (OPENFILENAME);
771        ofn.lpstrDefExt = szDefExt;
772        ofn.lpstrFilter = szFilter;
773        ofn.lpstrFile = szFile;
774        ofn.nMaxFile = MS_MAX_PATH;
775        ofn.lpstrFileTitle = szFileTitle;
776        ofn.nMaxFileTitle = MS_MAX_PATH;
777        ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
778        ofn.lpstrTitle = "Export to OGRE Material";
779
780        if (!::GetSaveFileName (&ofn))
781                return;
782
783        // Strip off the path
784        Ogre::String matName = szFile;
785        size_t lastSlash = matName.find_last_of("\\");
786        matName = matName.substr(lastSlash+1);
787
788        // Set up
789        logMgr.logMessage("Trying to create Material object");
790
791        Ogre::MaterialSerializer matSer;
792
793        for (int i = 0; i < numMaterials; ++i)
794        {
795                msMaterial *mat = msModel_GetMaterialAt(pModel, i);
796
797                msg = "Creating material " + Ogre::String(mat->szName);
798                logMgr.logMessage(msg);
799        Ogre::MaterialPtr ogremat = matMgrSgl.create(mat->szName,
800            Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
801                logMgr.logMessage("Created.");
802
803                ogremat->setAmbient(msVec4ToColourValue(mat->Ambient));
804                ogremat->setDiffuse(msVec4ToColourValue(mat->Diffuse));
805                ogremat->setSpecular(msVec4ToColourValue(mat->Specular));
806                ogremat->setShininess(mat->fShininess);
807
808                if (0 < strlen(mat->szDiffuseTexture))
809                        ogremat->getTechnique(0)->getPass(0)->createTextureUnitState(mat->szDiffuseTexture);
810
811        if (0 < strlen(mat->szAlphaTexture))
812                        ogremat->getTechnique(0)->getPass(0)->createTextureUnitState(mat->szAlphaTexture);
813
814
815                matSer.queueForExport(ogremat);
816        }
817
818        msg = "Exporting materials to " + matName;
819        logMgr.logMessage(msg);
820        matSer.exportQueued(matName);
821}
822
823Ogre::ColourValue MilkshapePlugin::msVec4ToColourValue(float prop[4])
824{
825        Ogre::ColourValue colour;
826        colour.r = prop[0];
827        colour.g = prop[1];
828        colour.b = prop[2];
829        colour.a = prop[3];
830
831        return colour;
832}
833
834void MilkshapePlugin::doExportAnimations(msModel* pModel, Ogre::SkeletonPtr& ogreskel)
835{
836
837    Ogre::LogManager& logMgr = Ogre::LogManager::getSingleton();
838    std::vector<SplitAnimationStruct> splitInfo;
839    Ogre::String msg;
840
841    int numFrames = msModel_GetTotalFrames(pModel);
842        msg = "Number of frames: " + Ogre::StringConverter::toString(numFrames);
843    logMgr.logMessage(msg);
844
845    if (splitAnimations)
846    {
847        // Explain
848        msg = "You have chosen to create multiple discrete animations by splitting up the frames in "
849                        "the animation sequence. In order to do this, you must supply a simple text file "
850            "describing the separate animations, which has a single line per animation in the format: \n\n"
851            "startFrame,endFrame,animationName\n\nFor example: \n\n"
852            "1,20,Walk\n21,35,Run\n36,40,Shoot\n\n"
853            "..creates 3 separate animations (the frame numbers are inclusive)."
854                        "You must browse to this file in the next dialog.";
855        MessageBox(0,msg.c_str(), "Splitting Animations",MB_ICONINFORMATION | MB_OK);
856        // Prompt for a file which contains animation splitting info
857        OPENFILENAME ofn;
858        memset (&ofn, 0, sizeof (OPENFILENAME));
859       
860        char szFile[MS_MAX_PATH];
861        char szFileTitle[MS_MAX_PATH];
862        char szDefExt[32] = "skeleton";
863        char szFilter[128] = "All Files (*.*)\0*.*\0\0";
864        szFile[0] = '\0';
865        szFileTitle[0] = '\0';
866
867        ofn.lStructSize = sizeof (OPENFILENAME);
868        ofn.lpstrDefExt = szDefExt;
869        ofn.lpstrFilter = szFilter;
870        ofn.lpstrFile = szFile;
871        ofn.nMaxFile = MS_MAX_PATH;
872        ofn.lpstrFileTitle = szFileTitle;
873        ofn.nMaxFileTitle = MS_MAX_PATH;
874        ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
875        ofn.lpstrTitle = "Open animation split configuration file";
876
877        if (!::GetOpenFileName (&ofn))
878        {
879            msg = "Splitting aborted, generating a single animation called 'Default'";
880            MessageBox(0, msg.c_str(), "Info", MB_OK | MB_ICONWARNING);
881            SplitAnimationStruct split;
882            split.start = 1;
883            split.end = numFrames;
884            split.name = "Default";
885            splitInfo.push_back(split);
886        }
887        else
888        {
889            // Read file
890            Ogre::String sline;
891            char line[256];
892            SplitAnimationStruct newSplit;
893
894            std::ifstream istr;
895            istr.open(szFile);
896
897            while (!istr.eof())
898            {
899                istr.getline(line, 256);
900                sline = line;
901
902                // Ignore blanks & comments
903                if (sline == "" || sline.substr(0,2) == "//")
904                    continue;
905
906                // Split on ','
907                                std::vector<Ogre::String> svec = Ogre::StringUtil::split(line, ",\n");
908
909                // Basic validation on number of elements
910                if (svec.size() != 3)
911                {
912                    MessageBox(0, "Warning: corrupt animation details in file. You should look into this. ",
913                        "Corrupt animations file", MB_ICONWARNING | MB_OK);
914                    continue;
915                }
916                // Remove any embedded spaces
917                                Ogre::StringUtil::trim(svec[0]);
918                Ogre::StringUtil::trim(svec[1]);
919                Ogre::StringUtil::trim(svec[2]);
920                // Create split info
921                newSplit.start = atoi(svec[0].c_str());
922                newSplit.end = atoi(svec[1].c_str());
923                newSplit.name = svec[2];
924                splitInfo.push_back(newSplit);
925
926            }
927
928
929        }
930
931    }
932    else
933    {
934        // No splitting
935        SplitAnimationStruct split;
936        split.start = 1;
937        split.end = numFrames;
938        split.name = "Default";
939        splitInfo.push_back(split);
940    }
941
942    // Get animation length
943    // Map frames -> seconds, this can be changed in speed of animation anyway
944
945
946
947    int numBones = msModel_GetBoneCount(pModel);
948    unsigned int frameTime;
949    float realTime;
950
951    std::vector<SplitAnimationStruct>::iterator animsIt;
952    for (animsIt = splitInfo.begin(); animsIt != splitInfo.end(); ++animsIt)
953    {
954        SplitAnimationStruct& currSplit = *animsIt;
955
956        // Create animation
957        frameTime = currSplit.end - currSplit.start;
958        realTime = frameTime / fps;
959                Ogre::StringUtil::StrStreamType msgStream;
960        msgStream << "Trying to create Animation object for animation "
961                        <<  currSplit.name << " For Frames " << currSplit.start << " to "
962            << currSplit.end << " inclusive. ";
963        logMgr.logMessage(msgStream.str());
964
965        msgStream.clear();
966                msgStream << "Frame time = "
967                        << frameTime << ", Seconds = " << realTime;
968        logMgr.logMessage(msg);
969
970        Ogre::Animation *ogreanim =
971            ogreskel->createAnimation(currSplit.name, realTime);
972        logMgr.logMessage("Animation object created.");
973
974        int i;
975        // Create all the animation tracks
976        for (i = 0; i < numBones; ++i)
977        {
978
979            msBone* bone = msModel_GetBoneAt(pModel, i);
980            Ogre::Bone* ogrebone = ogreskel->getBone(bone->szName);
981
982            // Create animation tracks
983                        msg = "Creating AnimationTrack for bone " + Ogre::StringConverter::toString(i);
984            logMgr.logMessage(msg);
985
986            Ogre::AnimationTrack *ogretrack = ogreanim->createTrack(i, ogrebone);
987            logMgr.logMessage("Animation track created.");
988
989            // OGRE uses keyframes which are both position and rotation
990            // Milkshape separates them, but never seems to use the ability to
991            // have a different # of pos & rot keys
992
993            int numKeys = msBone_GetRotationKeyCount(bone);
994
995            msg = "Number of keyframes: " + Ogre::StringConverter::toString(numKeys);
996            logMgr.logMessage(msg);
997
998            int currKeyIdx;
999            msPositionKey* currPosKey;
1000            msRotationKey* currRotKey;
1001            for (currKeyIdx = 0; currKeyIdx < numKeys; ++currKeyIdx )
1002            {
1003                currPosKey = msBone_GetPositionKeyAt(bone, currKeyIdx);
1004                currRotKey = msBone_GetRotationKeyAt(bone, currKeyIdx);
1005
1006                // Make sure keyframe is in current time frame (for splitting)
1007                if (currRotKey->fTime >= currSplit.start && currRotKey->fTime <= currSplit.end)
1008                {
1009
1010                    msg = "Creating KeyFrame #" + Ogre::StringConverter::toString(currKeyIdx)
1011                        + " for bone #" + Ogre::StringConverter::toString(i);
1012                    logMgr.logMessage(msg);
1013                    // Create keyframe
1014                    // Adjust for start time, and for the fact that frames are numbered from 1
1015                    frameTime = currRotKey->fTime - currSplit.start;
1016                    realTime = frameTime / fps;
1017                    Ogre::KeyFrame *ogrekey = ogretrack->createKeyFrame(realTime);
1018                    logMgr.logMessage("KeyFrame created");
1019
1020                    Ogre::Vector3 kfPos(currPosKey->Position[0], currPosKey->Position[1], currPosKey->Position[2]);
1021                    Ogre::Quaternion qx, qy, qz, kfQ;
1022
1023                    ogrekey->setTranslate(kfPos);
1024                    qx.FromAngleAxis(Ogre::Radian(currRotKey->Rotation[0]), Ogre::Vector3::UNIT_X);
1025                    qy.FromAngleAxis(Ogre::Radian(currRotKey->Rotation[1]), Ogre::Vector3::UNIT_Y);
1026                    qz.FromAngleAxis(Ogre::Radian(currRotKey->Rotation[2]), Ogre::Vector3::UNIT_Z);
1027                    kfQ = qz * qy * qx;
1028                    ogrekey->setRotation(kfQ);
1029
1030                                        Ogre::StringUtil::StrStreamType msgStream;
1031                    msgStream << "KeyFrame details: Adjusted Frame Time=" << frameTime
1032                                                << " Seconds: " << realTime << " Position=" << kfPos << " "
1033                                                << "Ms3d Rotation= {" << currRotKey->Rotation[0] << ", "
1034                                                << currRotKey->Rotation[1] << ", " << currRotKey->Rotation[2]
1035                                                << "} " << "Orientation=" << kfQ;
1036                    logMgr.logMessage(msgStream.str());
1037                } // keyframe creation
1038
1039            } // keys
1040        } //Bones
1041    } // Animations
1042
1043
1044
1045
1046}
1047
Note: See TracBrowser for help on using the repository browser.