source: OGRE/trunk/ogrenew/Tools/XMLConverter/src/main.cpp @ 657

Revision 657, 21.5 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
27#include "Ogre.h"
28#include "OgreXMLMeshSerializer.h"
29#include "OgreMeshSerializer.h"
30#include "OgreXMLSkeletonSerializer.h"
31#include "OgreSkeletonSerializer.h"
32#include "OgreXMLPrerequisites.h"
33#include "OgreDefaultHardwareBufferManager.h"
34#include <iostream>
35#include <sys/stat.h>
36
37using namespace std;
38using namespace Ogre;
39
40struct XmlOptions
41{
42    String source;
43    String dest;
44    String sourceExt;
45    String destExt;
46    bool interactiveMode;
47    unsigned short numLods;
48    Real lodDist;
49    Real lodPercent;
50    size_t lodFixed;
51    bool usePercent;
52    bool generateEdgeLists;
53    bool generateTangents;
54    bool reorganiseBuffers;
55        bool optimiseAnimations;
56};
57
58void help(void)
59{
60    // Print help message
61    cout << endl << "OgreXMLConvert: Converts data between XML and OGRE binary formats." << endl;
62    cout << "Provided for OGRE by Steve Streeting 2002" << endl << endl;
63    cout << "Usage: OgreXMLConverter [options] sourcefile [destfile] " << endl;
64        cout << endl << "Available options:" << endl;
65    cout << "-i             = interactive mode - prompt for options" << endl;
66    cout << "(The next 4 options are only applicable when converting XML to Mesh)" << endl;
67    cout << "-l lodlevels   = number of LOD levels" << endl;
68    cout << "-d loddist     = distance increment to reduce LOD" << endl;
69    cout << "-p lodpercent  = Percentage triangle reduction amount per LOD" << endl;
70    cout << "-f lodnumtris  = Fixed vertex reduction per LOD" << endl;
71    cout << "-e             = DON'T generate edge lists (for stencil shadows)" << endl;
72    cout << "-r             = DON'T reorganise vertex buffers to OGRE recommended format." << endl;
73    cout << "-t             = Generate tangents (for normal mapping)" << endl;
74    cout << "-o             = DON'T optimise out redundant tracks & keyframes" << endl;
75    cout << "sourcefile     = name of file to convert" << endl;
76    cout << "destfile       = optional name of file to write to. If you don't" << endl;
77    cout << "                 specify this OGRE works it out through the extension " << endl;
78    cout << "                 and the XML contents if the source is XML. For example" << endl;
79    cout << "                 test.mesh becomes test.xml, test.xml becomes test.mesh " << endl;
80    cout << "                 if the XML document root is <mesh> etc."  << endl;
81
82    cout << endl;
83}
84
85
86XmlOptions parseArgs(int numArgs, char **args)
87{
88    XmlOptions opts;
89
90    opts.interactiveMode = false;
91    opts.lodDist = 500;
92    opts.lodFixed = 0;
93    opts.lodPercent = 20;
94    opts.numLods = 0;
95    opts.usePercent = true;
96    opts.generateEdgeLists = true;
97    opts.generateTangents = false;
98    opts.reorganiseBuffers = true;
99        opts.optimiseAnimations = true;
100
101    // ignore program name
102    char* source = 0;
103    char* dest = 0;
104
105    // Set up options
106    UnaryOptionList unOpt;
107    BinaryOptionList binOpt;
108
109    unOpt["-i"] = false;
110    unOpt["-e"] = false;
111    unOpt["-r"] = false;
112    unOpt["-t"] = false;
113    unOpt["-o"] = false;
114    binOpt["-l"] = "";
115    binOpt["-d"] = "";
116    binOpt["-p"] = "";
117    binOpt["-f"] = "";
118
119    int startIndex = findCommandLineOpts(numArgs, args, unOpt, binOpt);
120    UnaryOptionList::iterator ui;
121    BinaryOptionList::iterator bi;
122
123    ui = unOpt.find("-i");
124    if (ui->second)
125    {
126        opts.interactiveMode = true;
127    }
128    else
129    {
130        ui = unOpt.find("-e");
131        if (ui->second)
132        {
133            opts.generateEdgeLists = false;
134        }
135   
136        ui = unOpt.find("-r");
137        if (ui->second)
138        {
139            opts.reorganiseBuffers = false;
140        }
141
142        ui = unOpt.find("-t");
143        if (ui->second)
144        {
145            opts.generateTangents = true;
146        }
147
148        ui = unOpt.find("-o");
149        if (ui->second)
150        {
151            opts.optimiseAnimations = false;
152        }
153
154                bi = binOpt.find("-l");
155        if (!bi->second.empty())
156        {
157            opts.numLods = StringConverter::parseInt(bi->second);
158        }
159
160        bi = binOpt.find("-d");
161        if (!bi->second.empty())
162        {
163            opts.lodDist = StringConverter::parseReal(bi->second);
164        }
165
166        bi = binOpt.find("-p");
167        if (!bi->second.empty())
168        {
169            opts.lodPercent = StringConverter::parseReal(bi->second);
170            opts.usePercent = true;
171        }
172
173
174        bi = binOpt.find("-f");
175        if (!bi->second.empty())
176        {
177            opts.lodFixed = StringConverter::parseInt(bi->second);
178            opts.usePercent = false;
179        }
180    }
181    // Source / dest
182    if (numArgs > startIndex)
183        source = args[startIndex];
184    if (numArgs > startIndex+1)
185        dest = args[startIndex+1];
186
187    if (!source)
188    {
189        cout << "Missing source file - abort. " << endl;
190        help();
191        exit(1);
192    }
193    // Work out what kind of conversion this is
194    opts.source = source;
195        std::vector<String> srcparts = StringUtil::split(opts.source, ".");
196    String& ext = srcparts.back();
197        StringUtil::toLowerCase(ext);
198    opts.sourceExt = ext;
199
200    if (!dest)
201    {
202        if (opts.sourceExt == "xml")
203        {
204            // dest is source minus .xml
205            opts.dest = opts.source.substr(0, opts.source.size() - 4);
206        }
207        else
208        {
209            // dest is source + .xml
210            opts.dest = opts.source;
211            opts.dest.append(".xml");
212        }
213
214    }
215    else
216    {
217        opts.dest = dest;
218    }
219        std::vector<String> dstparts = StringUtil::split(opts.dest, ".");
220    ext = dstparts.back();
221        StringUtil::toLowerCase(ext);
222    opts.destExt = ext;
223
224    cout << endl;
225    cout << "-- OPTIONS --" << endl;
226    cout << "source file      = " << opts.source << endl;
227    cout << "destination file = " << opts.dest << endl;
228    cout << "interactive mode = " << StringConverter::toString(opts.interactiveMode) << endl;
229    if (opts.numLods == 0)
230    {
231        cout << "lod levels       = none (or use existing)" << endl;
232    }
233    else
234    {
235        cout << "lod levels       = " << opts.numLods << endl;
236        cout << "lod distance     = " << opts.lodDist << endl;
237        if (opts.usePercent)
238        {
239            cout << "lod reduction    = " << opts.lodPercent << "%" << endl;
240        }
241        else
242        {
243            cout << "lod reduction    = " << opts.lodFixed << " verts" << endl;
244        }
245    }
246    cout << "Generate edge lists  = " << opts.generateEdgeLists << endl;
247    cout << "Generate tangents = " << opts.generateTangents << endl;
248    cout << "Reorganise vertex buffers = " << opts.reorganiseBuffers << endl;
249        cout << "Optimise animations = " << opts.optimiseAnimations << endl;
250       
251    cout << "-- END OPTIONS --" << endl;
252    cout << endl;
253
254
255    return opts;
256}
257
258// Crappy globals
259// NB some of these are not directly used, but are required to
260//   instantiate the singletons used in the dlls
261LogManager* logMgr;
262Math* mth;
263MaterialManager* matMgr;
264SkeletonManager* skelMgr;
265MeshSerializer* meshSerializer;
266XMLMeshSerializer* xmlMeshSerializer;
267SkeletonSerializer* skeletonSerializer;
268XMLSkeletonSerializer* xmlSkeletonSerializer;
269DefaultHardwareBufferManager *bufferManager;
270MeshManager* meshMgr;
271ResourceGroupManager* rgm;
272
273
274void meshToXML(XmlOptions opts)
275{
276    std::ifstream ifs;
277    ifs.open(opts.source.c_str(), std::ios_base::in | std::ios_base::binary);
278    // pass false for freeOnClose to FileStreamDataStream since ifs is created on stack
279    DataStreamPtr stream(new FileStreamDataStream(opts.source, &ifs, false));
280
281    MeshPtr mesh = MeshManager::getSingleton().create("conversion",
282        ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
283   
284
285    meshSerializer->importMesh(stream, mesh.getPointer());
286   
287    xmlMeshSerializer->exportMesh(mesh.getPointer(), opts.dest);
288
289}
290
291void XMLToBinary(XmlOptions opts)
292{
293    // Read root element and decide from there what type
294    String response;
295    TiXmlDocument* doc = new TiXmlDocument(opts.source);
296    // Some double-parsing here but never mind
297    if (!doc->LoadFile())
298    {
299        cout << "Unable to open file " << opts.source << " - fatal error." << endl;
300        exit (1);
301    }
302    TiXmlElement* root = doc->RootElement();
303    if (!stricmp(root->Value(), "mesh"))
304    {
305        delete doc;
306        MeshPtr newMesh = MeshManager::getSingleton().createManual("conversion",
307            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
308        xmlMeshSerializer->importMesh(opts.source, newMesh.getPointer());
309
310        // Re-jig the buffers?
311        if (opts.reorganiseBuffers)
312        {
313            logMgr->logMessage("Reorganising vertex buffers to automatic layout..");
314            // Shared geometry
315            if (newMesh->sharedVertexData)
316            {
317                // Automatic
318                VertexDeclaration* newDcl =
319                    newMesh->sharedVertexData->vertexDeclaration->getAutoOrganisedDeclaration(
320                        newMesh->hasSkeleton());
321                if (*newDcl != *(newMesh->sharedVertexData->vertexDeclaration))
322                {
323                    // Usages don't matter here since we're onlly exporting
324                    BufferUsageList bufferUsages;
325                    for (size_t u = 0; u <= newDcl->getMaxSource(); ++u)
326                        bufferUsages.push_back(HardwareBuffer::HBU_STATIC_WRITE_ONLY);
327                    newMesh->sharedVertexData->reorganiseBuffers(newDcl, bufferUsages);
328                }
329            }
330            // Dedicated geometry
331            Mesh::SubMeshIterator smIt = newMesh->getSubMeshIterator();
332            unsigned short idx = 0;
333            while (smIt.hasMoreElements())
334            {
335                SubMesh* sm = smIt.getNext();
336                if (!sm->useSharedVertices)
337                {
338                    // Automatic
339                    VertexDeclaration* newDcl =
340                        sm->vertexData->vertexDeclaration->getAutoOrganisedDeclaration(
341                            newMesh->hasSkeleton());
342                    if (*newDcl != *(sm->vertexData->vertexDeclaration))
343                    {
344                        // Usages don't matter here since we're onlly exporting
345                        BufferUsageList bufferUsages;
346                        for (size_t u = 0; u <= newDcl->getMaxSource(); ++u)
347                            bufferUsages.push_back(HardwareBuffer::HBU_STATIC_WRITE_ONLY);
348                        sm->vertexData->reorganiseBuffers(newDcl, bufferUsages);
349                    }
350                }
351            }
352
353        }
354
355        // Prompt for LOD generation?
356        bool genLod = false;
357        bool askLodDtls = false;
358        if (!opts.interactiveMode) // derive from params if in batch mode
359        {
360            askLodDtls = false;
361            if (opts.numLods == 0)
362            {
363                genLod = false;
364            }
365            else
366            {
367                genLod = true;
368            }
369        }
370        else if(opts.numLods == 0) // otherwise only ask if not specified on command line
371        {
372            if (newMesh->getNumLodLevels() > 1)
373            {
374                std::cout << "\nXML already contains level-of detail information.\n"
375                    "Do you want to: (u)se it, (r)eplace it, or (d)rop it?";
376                while (response == "")
377                {
378                    cin >> response;
379                                        StringUtil::toLowerCase(response);
380                    if (response == "u")
381                    {
382                        // Do nothing
383                    }
384                    else if (response == "d")
385                    {
386                        newMesh->removeLodLevels();
387                    }
388                    else if (response == "r")
389                    {
390                        genLod = true;
391                        askLodDtls = true;
392
393                    }
394                    else
395                    {
396                        response = "";
397                    }
398                }// while response == ""
399            }
400            else // no existing LOD
401            {
402                std::cout << "\nWould you like to generate LOD information? (y/n)";
403                while (response == "")
404                {
405                    cin >> response;
406                                        StringUtil::toLowerCase(response);
407                    if (response == "n")
408                    {
409                        // Do nothing
410                    }
411                    else if (response == "y")
412                    {
413                        genLod = true;
414                        askLodDtls = true;
415                    }
416                }
417            }
418        }
419
420        if (genLod)
421        {
422            unsigned short numLod;
423            ProgressiveMesh::VertexReductionQuota quota;
424            Real reduction;
425            Mesh::LodDistanceList distanceList;
426
427            if (askLodDtls)
428            {
429                cout << "\nHow many extra LOD levels would you like to generate?";
430                cin >> numLod;
431
432                cout << "\nWhat unit of reduction would you like to use:" <<
433                    "\n(f)ixed or (p)roportional?";
434                cin >> response;
435                                StringUtil::toLowerCase(response);
436                if (response == "f")
437                {
438                    quota = ProgressiveMesh::VRQ_CONSTANT;
439                    cout << "\nHow many vertices should be removed at each LOD?";
440                }
441                else
442                {
443                    quota = ProgressiveMesh::VRQ_PROPORTIONAL;
444                    cout << "\nWhat percentage of remaining vertices should be removed "
445                        "\at each LOD (e.g. 50)?";
446                }
447                cin >> reduction;
448                if (quota == ProgressiveMesh::VRQ_PROPORTIONAL)
449                {
450                    // Percentage -> parametric
451                    reduction = reduction * 0.01f;
452                }
453
454                cout << "\nEnter the distance for each LOD to come into effect.";
455
456                Real distance;
457                for (unsigned short iLod = 0; iLod < numLod; ++iLod)
458                {
459                    cout << "\nLOD Level " << (iLod+1) << ":";
460                    cin >> distance;
461                    distanceList.push_back(distance);
462                }
463            }
464            else
465            {
466                numLod = opts.numLods;
467                quota = opts.usePercent?
468                    ProgressiveMesh::VRQ_PROPORTIONAL : ProgressiveMesh::VRQ_CONSTANT;
469                if (opts.usePercent)
470                {
471                    reduction = opts.lodPercent * 0.01f;
472                }
473                else
474                {
475                    reduction = opts.lodFixed;
476                }
477                Real currDist = 0;
478                for (unsigned short iLod = 0; iLod < numLod; ++iLod)
479                {
480                    currDist += opts.lodDist;
481                    distanceList.push_back(currDist);
482                }
483
484            }
485
486            newMesh->generateLodLevels(distanceList, quota, reduction);
487        }
488
489        if (opts.interactiveMode)
490        {
491            std::cout << "\nWould you like to include edge lists to enable stencil shadows with this mesh? (y/n)";
492            while (response == "")
493            {
494                cin >> response;
495                StringUtil::toLowerCase(response);
496                if (response == "y")
497                {
498                    // Do nothing
499                }
500                else if (response == "n")
501                {
502                    opts.generateEdgeLists = false;
503                }
504                else
505                {
506                    response = "";
507                }
508            }
509
510
511            std::cout << "\nWould you like to generate tangents to enable normal mapping with this mesh? (y/n)";
512            while (response == "")
513            {
514                cin >> response;
515                StringUtil::toLowerCase(response);
516                if (response == "y")
517                {
518                    opts.generateTangents = true;
519                }
520                else if (response == "n")
521                {
522                    // Do nothing
523                }
524                else
525                {
526                    response = "";
527                }
528            }
529        }
530
531        if (opts.generateEdgeLists)
532        {
533            std::cout << "Generating edge lists...." << std::endl;
534            newMesh->buildEdgeList();
535        }
536
537        if (opts.generateTangents)
538        {
539            unsigned short srcTex, destTex;
540            bool existing = newMesh->suggestTangentVectorBuildParams(srcTex, destTex);
541            if (existing)
542            {
543                std::cout << "\nThis mesh appears to already have a set of 3D texture coordinates, " <<
544                    "which would suggest tangent vectors have already been calculated. Do you really " <<
545                    "want to generate new tangent vectors (may duplicate)? (y/n)";
546                while (response == "")
547                {
548                    cin >> response;
549                    StringUtil::toLowerCase(response);
550                    if (response == "y")
551                    {
552                        // Do nothing
553                    }
554                    else if (response == "n")
555                    {
556                        opts.generateTangents = false;
557                    }
558                    else
559                    {
560                        response = "";
561                    }
562                }
563
564            }
565            if (opts.generateTangents)
566            {
567                std::cout << "Generating tangent vectors...." << std::endl;
568                newMesh->buildTangentVectors(srcTex, destTex);
569            }
570        }
571
572
573        meshSerializer->exportMesh(newMesh.getPointer(), opts.dest);
574    }
575    else if (!stricmp(root->Value(), "skeleton"))
576    {
577        delete doc;
578        SkeletonPtr newSkel = SkeletonManager::getSingleton().create("conversion",
579            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
580        xmlSkeletonSerializer->importSkeleton(opts.source, newSkel.getPointer());
581                if (opts.optimiseAnimations)
582                {
583                        newSkel->optimiseAllAnimations();
584                }
585        skeletonSerializer->exportSkeleton(newSkel.getPointer(), opts.dest);
586    }
587
588
589}
590
591void skeletonToXML(XmlOptions opts)
592{
593
594    std::ifstream ifs;
595    ifs.open(opts.source.c_str(), std::ios_base::in | std::ios_base::binary);
596        if (!ifs)
597        {
598                cout << "Unable to load file " << opts.source << endl;
599                exit(1);
600        }
601
602    SkeletonPtr skel = SkeletonManager::getSingleton().create("conversion",
603        ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
604
605    // pass false for freeOnClose to FileStreamDataStream since ifs is created localy on stack
606    DataStreamPtr stream(new FileStreamDataStream(opts.source, &ifs, false));
607    skeletonSerializer->importSkeleton(stream, skel.getPointer());
608   
609    xmlSkeletonSerializer->exportSkeleton(skel.getPointer(), opts.dest);
610
611}
612
613int main(int numargs, char** args)
614{
615    if (numargs < 2)
616    {
617        help();
618        return -1;
619    }
620
621    logMgr = new LogManager();
622        logMgr->createLog("OgreXMLConverter.log");
623    rgm = new ResourceGroupManager();
624    mth = new Math();
625    meshMgr = new MeshManager();
626    matMgr = new MaterialManager();
627    matMgr->initialise();
628    skelMgr = new SkeletonManager();
629    meshSerializer = new MeshSerializer();
630    xmlMeshSerializer = new XMLMeshSerializer();
631    skeletonSerializer = new SkeletonSerializer();
632    xmlSkeletonSerializer = new XMLSkeletonSerializer();
633    bufferManager = new DefaultHardwareBufferManager(); // needed because we don't have a rendersystem
634
635
636
637    logMgr->createLog("OgreXMLConverter.log");
638
639    XmlOptions opts = parseArgs(numargs, args);
640
641    if (opts.sourceExt == "mesh")
642    {
643        meshToXML(opts);
644    }
645    else if (opts.sourceExt == "skeleton")
646    {
647        skeletonToXML(opts);
648    }
649    else if (opts.sourceExt == "xml")
650    {
651        XMLToBinary(opts);
652    }
653    else
654    {
655        cout << "Unknown input type.\n";
656        exit(1);
657    }
658
659   
660
661    delete xmlSkeletonSerializer;
662    delete skeletonSerializer;
663    delete xmlMeshSerializer;
664    delete meshSerializer;
665    delete skelMgr;
666    delete matMgr;
667    delete meshMgr;
668        delete bufferManager;
669    delete mth;
670    delete rgm;
671    delete logMgr;
672
673    return 0;
674
675}
676
Note: See TracBrowser for help on using the repository browser.