Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/Tools/MilkshapeExport/src/MilkshapePlugin.cpp @ 6

Last change on this file since 6 was 6, checked in by anonymous, 17 years ago

=…

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