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

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