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

Revision 692, 37.4 KB checked in by mattausch, 18 years ago (diff)

adding ogre 1.2 and dependencies

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