Coverage Report

Created: 2026-04-12 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/PlugIns/DotScene/src/DotSceneLoader.cpp
Line
Count
Source
1
#include <Ogre.h>
2
#include <OgreDotSceneLoader.h>
3
#include <OgreComponents.h>
4
5
#ifdef OGRE_BUILD_COMPONENT_TERRAIN
6
#include <OgreTerrain.h>
7
#include <OgreTerrainGroup.h>
8
#endif
9
10
#include <pugixml.hpp>
11
12
using namespace Ogre;
13
14
#ifndef OGRE_BUILD_COMPONENT_TERRAIN
15
namespace Ogre {
16
    class TerrainGroup {}; // appease unique_ptr destructor
17
}
18
#endif
19
20
namespace
21
{
22
String getAttrib(const pugi::xml_node& XMLNode, const String& attrib, const String& defaultValue = "")
23
0
{
24
0
    if (auto anode = XMLNode.attribute(attrib.c_str()))
25
0
        return anode.value();
26
0
    else
27
0
        return defaultValue;
28
0
}
29
30
Real getAttribReal(const pugi::xml_node& XMLNode, const String& attrib, Real defaultValue = 0)
31
0
{
32
0
    if (auto anode = XMLNode.attribute(attrib.c_str()))
33
0
        return StringConverter::parseReal(anode.value());
34
0
    else
35
0
        return defaultValue;
36
0
}
37
38
bool getAttribBool(const pugi::xml_node& XMLNode, const String& attrib, bool defaultValue = false)
39
0
{
40
0
    if (auto anode = XMLNode.attribute(attrib.c_str()))
41
0
        return anode.as_bool();
42
0
    else
43
0
        return defaultValue;
44
45
0
    return false;
46
0
}
47
48
Vector3 parseVector3(const pugi::xml_node& XMLNode)
49
0
{
50
0
    return Vector3(StringConverter::parseReal(XMLNode.attribute("x").value()),
51
0
                   StringConverter::parseReal(XMLNode.attribute("y").value()),
52
0
                   StringConverter::parseReal(XMLNode.attribute("z").value()));
53
0
}
54
55
Quaternion parseQuaternion(const pugi::xml_node& XMLNode)
56
0
{
57
    //! @todo Fix this crap!
58
59
0
    Quaternion orientation;
60
61
0
    if (XMLNode.attribute("qw"))
62
0
    {
63
0
        orientation.w = StringConverter::parseReal(XMLNode.attribute("qw").value());
64
0
        orientation.x = StringConverter::parseReal(XMLNode.attribute("qx").value());
65
0
        orientation.y = StringConverter::parseReal(XMLNode.attribute("qy").value());
66
0
        orientation.z = StringConverter::parseReal(XMLNode.attribute("qz").value());
67
0
    }
68
0
    else if (XMLNode.attribute("axisX"))
69
0
    {
70
0
        Vector3 axis;
71
0
        axis.x = StringConverter::parseReal(XMLNode.attribute("axisX").value());
72
0
        axis.y = StringConverter::parseReal(XMLNode.attribute("axisY").value());
73
0
        axis.z = StringConverter::parseReal(XMLNode.attribute("axisZ").value());
74
0
        Real angle = StringConverter::parseReal(XMLNode.attribute("angle").value());
75
76
0
        orientation.FromAngleAxis(Radian(angle), axis);
77
0
    }
78
0
    else if (XMLNode.attribute("angleX"))
79
0
    {
80
0
        Matrix3 rot;
81
0
        rot.FromEulerAnglesXYZ(StringConverter::parseAngle(XMLNode.attribute("angleX").value()),
82
0
                               StringConverter::parseAngle(XMLNode.attribute("angleY").value()),
83
0
                               StringConverter::parseAngle(XMLNode.attribute("angleZ").value()));
84
0
        orientation.FromRotationMatrix(rot);
85
0
    }
86
0
    else if (XMLNode.attribute("x"))
87
0
    {
88
0
        orientation.x = StringConverter::parseReal(XMLNode.attribute("x").value());
89
0
        orientation.y = StringConverter::parseReal(XMLNode.attribute("y").value());
90
0
        orientation.z = StringConverter::parseReal(XMLNode.attribute("z").value());
91
0
        orientation.w = StringConverter::parseReal(XMLNode.attribute("w").value());
92
0
    }
93
0
    else if (XMLNode.attribute("w"))
94
0
    {
95
0
        orientation.w = StringConverter::parseReal(XMLNode.attribute("w").value());
96
0
        orientation.x = StringConverter::parseReal(XMLNode.attribute("x").value());
97
0
        orientation.y = StringConverter::parseReal(XMLNode.attribute("y").value());
98
0
        orientation.z = StringConverter::parseReal(XMLNode.attribute("z").value());
99
0
    }
100
101
0
    return orientation;
102
0
}
103
104
ColourValue parseColour(pugi::xml_node& XMLNode)
105
0
{
106
0
    return ColourValue(StringConverter::parseReal(XMLNode.attribute("r").value()),
107
0
                       StringConverter::parseReal(XMLNode.attribute("g").value()),
108
0
                       StringConverter::parseReal(XMLNode.attribute("b").value()),
109
0
                       XMLNode.attribute("a") != NULL ? StringConverter::parseReal(XMLNode.attribute("a").value()) : 1);
110
0
}
111
112
struct DotSceneCodec : public Codec
113
{
114
0
    String magicNumberToFileExt(const char* magicNumberPtr, size_t maxbytes) const override { return ""; }
115
1
    String getType() const override { return "scene"; }
116
    void decode(const DataStreamPtr& stream, const Any& output) const override
117
0
    {
118
0
        DataStreamPtr _stream(stream);
119
0
        DotSceneLoader loader;
120
0
        loader.load(_stream, ResourceGroupManager::getSingleton().getWorldResourceGroupName(),
121
0
                    any_cast<SceneNode*>(output));
122
0
    }
123
124
    void encodeToFile(const Any& input, const String& outFileName) const override
125
0
    {
126
0
        DotSceneLoader loader;
127
0
        loader.exportScene(any_cast<SceneNode*>(input), outFileName);
128
0
    }
129
};
130
131
} // namespace
132
133
0
DotSceneLoader::DotSceneLoader() : mSceneMgr(0), mBackgroundColour(ColourValue::Black), mItemPrefix("") {}
134
135
0
DotSceneLoader::~DotSceneLoader() {}
136
137
void DotSceneLoader::load(DataStreamPtr& stream, const String& groupName, SceneNode* rootNode)
138
0
{
139
0
    m_sGroupName = groupName;
140
0
    mSceneMgr = rootNode->getCreator();
141
142
0
    pugi::xml_document XMLDoc; // character type defaults to char
143
144
0
    auto optsAny = rootNode->getUserObjectBindings().getUserAny("_DotSceneLoaderOptions");
145
0
    if (optsAny.has_value())
146
0
    {
147
0
        std::map<Ogre::String, Ogre::Any> options = any_cast<std::map<Ogre::String, Ogre::Any>>(optsAny);
148
0
        if (options.find("namePrefix") != options.end())
149
0
        {
150
0
            Ogre::Any optionValue = options["namePrefix"];
151
0
            if (optionValue.has_value())
152
0
                mItemPrefix = Ogre::any_cast<Ogre::String>(optionValue);
153
0
        }
154
0
    }
155
156
0
    auto result = XMLDoc.load_buffer(stream->getAsString().c_str(), stream->size());
157
0
    if (!result)
158
0
    {
159
0
        LogManager::getSingleton().logError("DotSceneLoader - " + String(result.description()));
160
0
        return;
161
0
    }
162
163
    // Grab the scene node
164
0
    auto XMLRoot = XMLDoc.child("scene");
165
166
    // Validate the File
167
0
    if (!XMLRoot.attribute("formatVersion"))
168
0
    {
169
0
        LogManager::getSingleton().logError("DotSceneLoader - Invalid .scene File. Missing <scene formatVersion='x.y' >");
170
0
        return;
171
0
    }
172
173
    // figure out where to attach any nodes we create
174
0
    mAttachNode = rootNode;
175
176
    // Process the scene
177
0
    processScene(XMLRoot);
178
0
}
179
180
void DotSceneLoader::processScene(pugi::xml_node& XMLRoot)
181
0
{
182
    // Process the scene parameters
183
0
    String version = getAttrib(XMLRoot, "formatVersion", "unknown");
184
185
0
    String message = "[DotSceneLoader] Parsing dotScene file with version " + version;
186
0
    if (XMLRoot.attribute("sceneManager"))
187
0
        message += ", scene manager " + String(XMLRoot.attribute("sceneManager").value());
188
0
    if (XMLRoot.attribute("minOgreVersion"))
189
0
        message += ", min. Ogre version " + String(XMLRoot.attribute("minOgreVersion").value());
190
0
    if (XMLRoot.attribute("author"))
191
0
        message += ", author " + String(XMLRoot.attribute("author").value());
192
193
0
    LogManager::getSingleton().logMessage(message);
194
195
    // Process environment (?)
196
0
    if (auto pElement = XMLRoot.child("environment"))
197
0
        processEnvironment(pElement);
198
199
    // Process nodes (?)
200
0
    if (auto pElement = XMLRoot.child("nodes"))
201
0
        processNodes(pElement);
202
203
    // Process externals (?)
204
0
    if (auto pElement = XMLRoot.child("externals"))
205
0
        processExternals(pElement);
206
207
    // Process userDataReference (?)
208
0
    if (auto pElement = XMLRoot.child("userData"))
209
0
        processUserData(pElement, mAttachNode->getUserObjectBindings());
210
211
    // Process light (?)
212
0
    if (auto pElement = XMLRoot.child("light"))
213
0
        processLight(pElement);
214
215
    // Process camera (?)
216
0
    if (auto pElement = XMLRoot.child("camera"))
217
0
        processCamera(pElement);
218
219
    // Process terrain (?)
220
0
    if (auto pElement = XMLRoot.child("terrainGroup"))
221
0
        processTerrainGroup(pElement);
222
0
}
223
224
void DotSceneLoader::processNodes(pugi::xml_node& XMLNode)
225
0
{
226
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Nodes...", LML_TRIVIAL);
227
228
    // Process node (*)
229
0
    for (auto pElement : XMLNode.children("node"))
230
0
    {
231
0
        processNode(pElement);
232
0
    }
233
234
    // Process position (?)
235
0
    if (auto pElement = XMLNode.child("position"))
236
0
    {
237
0
        mAttachNode->setPosition(parseVector3(pElement));
238
0
    }
239
240
    // Process rotation (?)
241
0
    if (auto pElement = XMLNode.child("rotation"))
242
0
    {
243
0
        mAttachNode->setOrientation(parseQuaternion(pElement));
244
0
    }
245
246
    // Process scale (?)
247
0
    if (auto pElement = XMLNode.child("scale"))
248
0
    {
249
0
        mAttachNode->setScale(parseVector3(pElement));
250
0
    }
251
0
}
252
253
void DotSceneLoader::processExternals(pugi::xml_node& XMLNode)
254
0
{
255
    //! @todo Implement this
256
0
}
257
258
void DotSceneLoader::processEnvironment(pugi::xml_node& XMLNode)
259
0
{
260
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Environment...", LML_TRIVIAL);
261
262
    // Process camera (?)
263
0
    if (auto pElement = XMLNode.child("camera"))
264
0
        processCamera(pElement);
265
266
    // Process fog (?)
267
0
    if (auto pElement = XMLNode.child("fog"))
268
0
        processFog(pElement);
269
270
    // Process skyBox (?)
271
0
    if (auto pElement = XMLNode.child("skyBox"))
272
0
        processSkyBox(pElement);
273
274
    // Process skyDome (?)
275
0
    if (auto pElement = XMLNode.child("skyDome"))
276
0
        processSkyDome(pElement);
277
278
    // Process skyPlane (?)
279
0
    if (auto pElement = XMLNode.child("skyPlane"))
280
0
        processSkyPlane(pElement);
281
282
    // Process colourAmbient (?)
283
0
    if (auto pElement = XMLNode.child("colourAmbient"))
284
0
        mSceneMgr->setAmbientLight(parseColour(pElement));
285
286
    // Process colourBackground (?)
287
0
    if (auto pElement = XMLNode.child("colourBackground"))
288
0
        mBackgroundColour = parseColour(pElement);
289
0
}
290
291
void DotSceneLoader::processTerrainGroup(pugi::xml_node& XMLNode)
292
0
{
293
0
#ifdef OGRE_BUILD_COMPONENT_TERRAIN
294
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Terrain Group...", LML_TRIVIAL);
295
296
0
    Real worldSize = getAttribReal(XMLNode, "worldSize");
297
0
    int mapSize = StringConverter::parseInt(XMLNode.attribute("size").value());
298
0
    int compositeMapDistance = StringConverter::parseInt(XMLNode.attribute("tuningCompositeMapDistance").value());
299
0
    int maxPixelError = StringConverter::parseInt(XMLNode.attribute("tuningMaxPixelError").value());
300
301
0
    auto terrainGlobalOptions = TerrainGlobalOptions::getSingletonPtr();
302
0
    OgreAssert(terrainGlobalOptions, "TerrainGlobalOptions not available");
303
304
0
    terrainGlobalOptions->setMaxPixelError((Real)maxPixelError);
305
0
    terrainGlobalOptions->setCompositeMapDistance((Real)compositeMapDistance);
306
307
0
    auto terrainGroup = std::make_shared<TerrainGroup>(mSceneMgr, Terrain::ALIGN_X_Z, mapSize, worldSize);
308
0
    terrainGroup->setOrigin(Vector3::ZERO);
309
0
    terrainGroup->setResourceGroup(m_sGroupName);
310
311
    // Process terrain pages (*)
312
0
    for (auto pPageElement : XMLNode.children("terrain"))
313
0
    {
314
0
        int pageX = StringConverter::parseInt(pPageElement.attribute("x").value());
315
0
        int pageY = StringConverter::parseInt(pPageElement.attribute("y").value());
316
317
0
        terrainGroup->defineTerrain(pageX, pageY, pPageElement.attribute("dataFile").value());
318
0
    }
319
0
    terrainGroup->loadAllTerrains(true);
320
321
0
    terrainGroup->freeTemporaryResources();
322
323
0
    mAttachNode->getUserObjectBindings().setUserAny("TerrainGroup", terrainGroup);
324
#else
325
    OGRE_EXCEPT(Exception::ERR_INVALID_CALL, "recompile with Terrain component");
326
#endif
327
0
}
328
329
void DotSceneLoader::processLight(pugi::xml_node& XMLNode, SceneNode* pParent)
330
0
{
331
    // Process attributes
332
0
    String name = getAttrib(XMLNode, "name");
333
334
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Light: " + name, LML_TRIVIAL);
335
336
    // Create the light
337
0
    Light* pLight = mSceneMgr->createLight(name);
338
0
    if (pParent)
339
0
        pParent->attachObject(pLight);
340
341
0
    String sValue = getAttrib(XMLNode, "type");
342
0
    if (sValue == "point")
343
0
        pLight->setType(Light::LT_POINT);
344
0
    else if (sValue == "directional")
345
0
        pLight->setType(Light::LT_DIRECTIONAL);
346
0
    else if (sValue == "spot")
347
0
        pLight->setType(Light::LT_SPOTLIGHT);
348
0
    else if (sValue == "radPoint")
349
0
        pLight->setType(Light::LT_POINT);
350
0
    else if (sValue == "rect")
351
0
        pLight->setType(Light::LT_RECTLIGHT);
352
353
0
    pLight->setVisible(getAttribBool(XMLNode, "visible", true));
354
0
    pLight->setCastShadows(getAttribBool(XMLNode, "castShadows", true));
355
0
    pLight->setPowerScale(getAttribReal(XMLNode, "powerScale", 1.0));
356
357
    // Process colourDiffuse (?)
358
0
    if (auto pElement = XMLNode.child("colourDiffuse"))
359
0
        pLight->setDiffuseColour(parseColour(pElement));
360
361
    // Process colourSpecular (?)
362
0
    if (auto pElement = XMLNode.child("colourSpecular"))
363
0
        pLight->setSpecularColour(parseColour(pElement));
364
365
0
    if (sValue != "directional")
366
0
    {
367
        // Process lightRange (?)
368
0
        if (auto pElement = XMLNode.child("lightRange"))
369
0
            processLightRange(pElement, pLight);
370
371
        // Process lightAttenuation (?)
372
0
        if (auto pElement = XMLNode.child("lightAttenuation"))
373
0
            processLightAttenuation(pElement, pLight);
374
0
    }
375
376
0
    if (sValue == "rect")
377
0
    {
378
        // Process lightSourceSize (?)
379
0
        if (auto pElement = XMLNode.child("lightSourceSize"))
380
0
            processLightSourceSize(pElement, pLight);
381
0
  }
382
383
    // Process userDataReference (?)
384
0
    if (auto pElement = XMLNode.child("userData"))
385
0
        processUserData(pElement, pLight->getUserObjectBindings());
386
0
}
387
388
void DotSceneLoader::processCamera(pugi::xml_node& XMLNode, SceneNode* pParent)
389
0
{
390
    // Process attributes
391
0
    String name = getAttrib(XMLNode, "name");
392
393
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Camera: " + name, LML_TRIVIAL);
394
395
    // Real fov = getAttribReal(XMLNode, "fov", 45);
396
0
    Real aspectRatio = getAttribReal(XMLNode, "aspectRatio", 1.3333);
397
0
    String projectionType = getAttrib(XMLNode, "projectionType", "perspective");
398
399
    // Create the camera
400
0
    Camera* pCamera = mSceneMgr->createCamera(name);
401
402
    // construct a scenenode is no parent
403
0
    if (!pParent)
404
0
        pParent = mAttachNode->createChildSceneNode(name);
405
406
0
    pParent->attachObject(pCamera);
407
408
    // Set the field-of-view
409
    //! @todo Is this always in degrees?
410
    // pCamera->setFOVy(Degree(fov));
411
412
    // Set the aspect ratio
413
0
    pCamera->setAspectRatio(aspectRatio);
414
415
    // Set the projection type
416
0
    if (projectionType == "perspective")
417
0
        pCamera->setProjectionType(PT_PERSPECTIVE);
418
0
    else if (projectionType == "orthographic")
419
0
        pCamera->setProjectionType(PT_ORTHOGRAPHIC);
420
421
    // Process clipping (?)
422
0
    if (auto pElement = XMLNode.child("clipping"))
423
0
    {
424
0
        Real nearDist = getAttribReal(pElement, "near");
425
0
        pCamera->setNearClipDistance(nearDist);
426
427
0
        Real farDist = getAttribReal(pElement, "far");
428
0
        pCamera->setFarClipDistance(farDist);
429
0
    }
430
431
    // Process userDataReference (?)
432
0
    if (auto pElement = XMLNode.child("userData"))
433
0
        processUserData(pElement, static_cast<MovableObject*>(pCamera)->getUserObjectBindings());
434
0
}
435
436
void DotSceneLoader::processNode(pugi::xml_node& XMLNode, SceneNode* pParent)
437
0
{
438
    // Construct the node's name
439
0
    String name = getAttrib(XMLNode, "name");
440
441
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Node: " + name, LML_TRIVIAL);
442
443
    // Create the scene node
444
0
    SceneNode* pNode;
445
0
    if (name.empty())
446
0
    {
447
        // Let Ogre choose the name
448
0
        if (pParent)
449
0
            pNode = pParent->createChildSceneNode();
450
0
        else
451
0
            pNode = mAttachNode->createChildSceneNode();
452
0
    }
453
0
    else
454
0
    {
455
        // Provide the name
456
0
        if (pParent)
457
0
            pNode = pParent->createChildSceneNode(mItemPrefix + name);
458
0
        else
459
0
            pNode = mAttachNode->createChildSceneNode(mItemPrefix + name);
460
0
    }
461
462
    // Process other attributes
463
464
    // Process position (?)
465
0
    if (auto pElement = XMLNode.child("position"))
466
0
    {
467
0
        pNode->setPosition(parseVector3(pElement));
468
0
    }
469
470
    // Process rotation (?)
471
0
    if (auto pElement = XMLNode.child("rotation"))
472
0
    {
473
0
        pNode->setOrientation(parseQuaternion(pElement));
474
0
    }
475
476
    // Process scale (?)
477
0
    if (auto pElement = XMLNode.child("scale"))
478
0
    {
479
0
        pNode->setScale(parseVector3(pElement));
480
0
    }
481
482
    // Process lookTarget (?)
483
0
    if (auto pElement = XMLNode.child("lookTarget"))
484
0
        processLookTarget(pElement, pNode);
485
486
    // Process trackTarget (?)
487
0
    if (auto pElement = XMLNode.child("trackTarget"))
488
0
        processTrackTarget(pElement, pNode);
489
490
    // Process node (*)
491
0
    for (auto pElement : XMLNode.children("node"))
492
0
    {
493
0
        processNode(pElement, pNode);
494
0
    }
495
496
    // Process entity (*)
497
0
    for (auto pElement : XMLNode.children("entity"))
498
0
    {
499
0
        processEntity(pElement, pNode);
500
0
    }
501
502
    // Process light (*)
503
0
    for (auto pElement : XMLNode.children("light"))
504
0
    {
505
0
        processLight(pElement, pNode);
506
0
    }
507
508
    // Process camera (*)
509
0
    for (auto pElement : XMLNode.children("camera"))
510
0
    {
511
0
        processCamera(pElement, pNode);
512
0
    }
513
514
    // Process particleSystem (*)
515
0
    for (auto pElement : XMLNode.children("particleSystem"))
516
0
    {
517
0
        processParticleSystem(pElement, pNode);
518
0
    }
519
520
    // Process billboardSet (*)
521
0
    for (auto pElement : XMLNode.children("billboardSet"))
522
0
    {
523
0
        processBillboardSet(pElement, pNode);
524
0
    }
525
526
    // Process plane (*)
527
0
    for (auto pElement : XMLNode.children("plane"))
528
0
    {
529
0
        processPlane(pElement, pNode);
530
0
    }
531
532
    // Process userDataReference (?)
533
0
    if (auto pElement = XMLNode.child("userData"))
534
0
        processUserData(pElement, pNode->getUserObjectBindings());
535
536
    // Process node animations (?)
537
0
    if (auto pElement = XMLNode.child("animations"))
538
0
        processNodeAnimations(pElement, pNode);
539
0
}
540
541
void DotSceneLoader::processLookTarget(pugi::xml_node& XMLNode, SceneNode* pParent)
542
0
{
543
    //! @todo Is this correct? Cause I don't have a clue actually
544
545
    // Process attributes
546
0
    String nodeName = getAttrib(XMLNode, "nodeName");
547
548
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Look Target, nodeName: " + nodeName, LML_TRIVIAL);
549
550
0
    Node::TransformSpace relativeTo = Node::TS_PARENT;
551
0
    String sValue = getAttrib(XMLNode, "relativeTo");
552
0
    if (sValue == "local")
553
0
        relativeTo = Node::TS_LOCAL;
554
0
    else if (sValue == "parent")
555
0
        relativeTo = Node::TS_PARENT;
556
0
    else if (sValue == "world")
557
0
        relativeTo = Node::TS_WORLD;
558
559
    // Process position (?)
560
0
    Vector3 position;
561
0
    if (auto pElement = XMLNode.child("position"))
562
0
        position = parseVector3(pElement);
563
564
    // Process localDirection (?)
565
0
    Vector3 localDirection = Vector3::NEGATIVE_UNIT_Z;
566
0
    if (auto pElement = XMLNode.child("localDirection"))
567
0
        localDirection = parseVector3(pElement);
568
569
    // Setup the look target
570
0
    try
571
0
    {
572
0
        if (!nodeName.empty())
573
0
        {
574
0
            SceneNode* pLookNode = mSceneMgr->getSceneNode(nodeName);
575
0
            position = pLookNode->_getDerivedPosition();
576
0
        }
577
578
0
        pParent->lookAt(position, relativeTo, localDirection);
579
0
    }
580
0
    catch (const Exception& e)
581
0
    {
582
0
        LogManager::getSingleton().logError("DotSceneLoader - " + e.getDescription());
583
0
    }
584
0
}
585
586
void DotSceneLoader::processTrackTarget(pugi::xml_node& XMLNode, SceneNode* pParent)
587
0
{
588
    // Process attributes
589
0
    String nodeName = getAttrib(XMLNode, "nodeName");
590
591
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Track Target, nodeName: " + nodeName, LML_TRIVIAL);
592
593
    // Process localDirection (?)
594
0
    Vector3 localDirection = Vector3::NEGATIVE_UNIT_Z;
595
0
    if (auto pElement = XMLNode.child("localDirection"))
596
0
        localDirection = parseVector3(pElement);
597
598
    // Process offset (?)
599
0
    Vector3 offset = Vector3::ZERO;
600
0
    if (auto pElement = XMLNode.child("offset"))
601
0
        offset = parseVector3(pElement);
602
603
    // Setup the track target
604
0
    try
605
0
    {
606
0
        SceneNode* pTrackNode = mSceneMgr->getSceneNode(nodeName);
607
0
        pParent->setAutoTracking(true, pTrackNode, localDirection, offset);
608
0
    }
609
0
    catch (const Exception& e)
610
0
    {
611
0
        LogManager::getSingleton().logError("DotSceneLoader - " + e.getDescription());
612
0
    }
613
0
}
614
615
void DotSceneLoader::processEntity(pugi::xml_node& XMLNode, SceneNode* pParent)
616
0
{
617
    // Process attributes
618
0
    String name = getAttrib(XMLNode, "name");
619
620
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Entity: " + mItemPrefix + name, LML_TRIVIAL);
621
622
0
    String meshFile = getAttrib(XMLNode, "meshFile");
623
0
    String staticGeometry = getAttrib(XMLNode, "static");
624
0
    String instancedManager = getAttrib(XMLNode, "instanced");
625
0
    String material = getAttrib(XMLNode, "material");
626
0
    bool castShadows = getAttribBool(XMLNode, "castShadows", true);
627
0
    bool visible = getAttribBool(XMLNode, "visible", true);
628
629
    // Create the entity
630
0
    MovableObject* pEntity = 0;
631
632
0
    try
633
0
    {
634
        // If the Entity is instanced then the creation path is different
635
0
        if (!instancedManager.empty())
636
0
        {
637
0
            LogManager::getSingleton().logMessage(
638
0
                "[DotSceneLoader] Adding entity: " + name + " to Instance Manager: " + instancedManager, LML_TRIVIAL);
639
640
            // Load the Mesh to get the material name of the first submesh
641
0
            Ogre::MeshPtr mesh = MeshManager::getSingletonPtr()->load(meshFile, m_sGroupName);
642
643
            // Get the material name of the entity
644
0
            if (!material.empty())
645
0
                pEntity = mSceneMgr->createInstancedEntity(material, instancedManager);
646
0
            else
647
0
                pEntity = mSceneMgr->createInstancedEntity(mesh->getSubMesh(0)->getMaterialName(), instancedManager);
648
649
0
            pParent->attachObject(static_cast<InstancedEntity*>(pEntity));
650
0
        }
651
0
        else
652
0
        {
653
0
            pEntity = mSceneMgr->createEntity(mItemPrefix + name, meshFile, m_sGroupName);
654
655
0
            static_cast<Entity*>(pEntity)->setCastShadows(castShadows);
656
0
            static_cast<Entity*>(pEntity)->setVisible(visible);
657
658
0
            if (!material.empty())
659
0
                static_cast<Entity*>(pEntity)->setMaterialName(material);
660
661
            // If the Entity belongs to a Static Geometry group then it doesn't get attached to a node
662
            // * TODO * : Clean up nodes without attached entities or children nodes? (should be done afterwards
663
            // if the hierarchy is being processed)
664
0
            if (!staticGeometry.empty())
665
0
            {
666
0
                LogManager::getSingleton().logMessage(
667
0
                    "[DotSceneLoader] Adding entity: " + name + " to Static Group: " + staticGeometry, LML_TRIVIAL);
668
0
                mSceneMgr->getStaticGeometry(staticGeometry)
669
0
                    ->addEntity(static_cast<Entity*>(pEntity), pParent->_getDerivedPosition(),
670
0
                                pParent->_getDerivedOrientation(), pParent->_getDerivedScale());
671
0
            }
672
0
            else
673
0
            {
674
0
                LogManager::getSingleton().logMessage("[DotSceneLoader] pParent->attachObject(): " + name, LML_TRIVIAL);
675
0
                pParent->attachObject(static_cast<Entity*>(pEntity));
676
0
            }
677
0
        }
678
0
    }
679
0
    catch (const Exception& e)
680
0
    {
681
0
        LogManager::getSingleton().logError("DotSceneLoader - " + e.getDescription());
682
0
        return;
683
0
    }
684
685
    // Process userDataReference (?)
686
0
    if (auto pElement = XMLNode.child("userData"))
687
0
        processUserData(pElement, pEntity->getUserObjectBindings());
688
0
}
689
690
void DotSceneLoader::processParticleSystem(pugi::xml_node& XMLNode, SceneNode* pParent)
691
0
{
692
    // Process attributes
693
0
    String name = getAttrib(XMLNode, "name");
694
695
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Particle System: " + name, LML_TRIVIAL);
696
697
0
    String templateName = getAttrib(XMLNode, "template");
698
699
0
    if (templateName.empty())
700
0
        templateName = getAttrib(XMLNode, "file"); // compatibility with old scenes
701
702
    // Create the particle system
703
0
    try
704
0
    {
705
0
        ParticleSystem* pParticles = mSceneMgr->createParticleSystem(name, templateName);
706
0
        pParent->attachObject(pParticles);
707
0
    }
708
0
    catch (const Exception& e)
709
0
    {
710
0
        LogManager::getSingleton().logError("DotSceneLoader - " + e.getDescription());
711
0
    }
712
0
}
713
714
void DotSceneLoader::processBillboardSet(pugi::xml_node& XMLNode, SceneNode* pParent)
715
0
{
716
    //! @todo Implement this
717
0
}
718
719
void DotSceneLoader::processPlane(pugi::xml_node& XMLNode, SceneNode* pParent)
720
0
{
721
0
    String name = getAttrib(XMLNode, "name");
722
723
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Plane: " + name, LML_TRIVIAL);
724
725
0
    Real distance = getAttribReal(XMLNode, "distance");
726
0
    Real width = getAttribReal(XMLNode, "width");
727
0
    Real height = getAttribReal(XMLNode, "height");
728
0
    int xSegments = StringConverter::parseInt(getAttrib(XMLNode, "xSegments"));
729
0
    int ySegments = StringConverter::parseInt(getAttrib(XMLNode, "ySegments"));
730
0
    int numTexCoordSets = StringConverter::parseInt(getAttrib(XMLNode, "numTexCoordSets"));
731
0
    Real uTile = getAttribReal(XMLNode, "uTile");
732
0
    Real vTile = getAttribReal(XMLNode, "vTile");
733
0
    String material = getAttrib(XMLNode, "material");
734
0
    bool hasNormals = getAttribBool(XMLNode, "hasNormals");
735
0
    Vector3 normal = parseVector3(XMLNode.child("normal"));
736
0
    Vector3 up = parseVector3(XMLNode.child("upVector"));
737
738
0
    Plane plane(normal, distance);
739
0
    MeshPtr res =
740
0
        MeshManager::getSingletonPtr()->createPlane(name + "mesh", m_sGroupName, plane, width, height, xSegments,
741
0
                                                    ySegments, hasNormals, numTexCoordSets, uTile, vTile, up);
742
0
    Entity* ent = mSceneMgr->createEntity(name, name + "mesh");
743
744
0
    ent->setMaterialName(material);
745
746
0
    pParent->attachObject(ent);
747
0
}
748
749
void DotSceneLoader::processFog(pugi::xml_node& XMLNode)
750
0
{
751
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Fog...", LML_TRIVIAL);
752
753
    // Process attributes
754
0
    Real expDensity = getAttribReal(XMLNode, "density", 0.001);
755
0
    Real linearStart = getAttribReal(XMLNode, "start", 0.0);
756
0
    Real linearEnd = getAttribReal(XMLNode, "end", 1.0);
757
758
0
    FogMode mode = FOG_NONE;
759
0
    String sMode = getAttrib(XMLNode, "mode");
760
0
    if (sMode == "none")
761
0
        mode = FOG_NONE;
762
0
    else if (sMode == "exp")
763
0
        mode = FOG_EXP;
764
0
    else if (sMode == "exp2")
765
0
        mode = FOG_EXP2;
766
0
    else if (sMode == "linear")
767
0
        mode = FOG_LINEAR;
768
0
    else
769
0
        mode = (FogMode)StringConverter::parseInt(sMode);
770
771
    // Process colourDiffuse (?)
772
0
    ColourValue colourDiffuse = ColourValue::White;
773
774
0
    if (auto pElement = XMLNode.child("colour"))
775
0
        colourDiffuse = parseColour(pElement);
776
777
    // Setup the fog
778
0
    mSceneMgr->setFog(mode, colourDiffuse, expDensity, linearStart, linearEnd);
779
0
}
780
781
void DotSceneLoader::processSkyBox(pugi::xml_node& XMLNode)
782
0
{
783
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing SkyBox...", LML_TRIVIAL);
784
785
    // Process attributes
786
0
    String material = getAttrib(XMLNode, "material", "BaseWhite");
787
0
    Real distance = getAttribReal(XMLNode, "distance", 5000);
788
0
    bool drawFirst = getAttribBool(XMLNode, "drawFirst", true);
789
790
    // Process rotation (?)
791
0
    Quaternion rotation = Quaternion::IDENTITY;
792
793
0
    if (auto pElement = XMLNode.child("rotation"))
794
0
        rotation = parseQuaternion(pElement);
795
796
    // Setup the sky box
797
0
    mSceneMgr->setSkyBox(true, material, distance, drawFirst, rotation, m_sGroupName);
798
0
}
799
800
void DotSceneLoader::processSkyDome(pugi::xml_node& XMLNode)
801
0
{
802
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing SkyDome...", LML_TRIVIAL);
803
804
    // Process attributes
805
0
    String material = XMLNode.attribute("material").value();
806
0
    Real curvature = getAttribReal(XMLNode, "curvature", 10);
807
0
    Real tiling = getAttribReal(XMLNode, "tiling", 8);
808
0
    Real distance = getAttribReal(XMLNode, "distance", 4000);
809
0
    bool drawFirst = getAttribBool(XMLNode, "drawFirst", true);
810
0
    bool active = getAttribBool(XMLNode, "active", false);
811
0
    if (!active)
812
0
        return;
813
814
    // Process rotation (?)
815
0
    Quaternion rotation = Quaternion::IDENTITY;
816
0
    if (auto pElement = XMLNode.child("rotation"))
817
0
        rotation = parseQuaternion(pElement);
818
819
    // Setup the sky dome
820
0
    mSceneMgr->setSkyDome(true, material, curvature, tiling, distance, drawFirst, rotation, 16, 16, -1, m_sGroupName);
821
0
}
822
823
void DotSceneLoader::processSkyPlane(pugi::xml_node& XMLNode)
824
0
{
825
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing SkyPlane...", LML_TRIVIAL);
826
827
    // Process attributes
828
0
    String material = getAttrib(XMLNode, "material");
829
0
    Real planeX = getAttribReal(XMLNode, "planeX", 0);
830
0
    Real planeY = getAttribReal(XMLNode, "planeY", -1);
831
0
    Real planeZ = getAttribReal(XMLNode, "planeZ", 0);
832
0
    Real planeD = getAttribReal(XMLNode, "planeD", 5000);
833
0
    Real scale = getAttribReal(XMLNode, "scale", 1000);
834
0
    Real bow = getAttribReal(XMLNode, "bow", 0);
835
0
    Real tiling = getAttribReal(XMLNode, "tiling", 10);
836
0
    bool drawFirst = getAttribBool(XMLNode, "drawFirst", true);
837
838
    // Setup the sky plane
839
0
    Plane plane;
840
0
    plane.normal = Vector3(planeX, planeY, planeZ);
841
0
    plane.d = planeD;
842
0
    mSceneMgr->setSkyPlane(true, plane, material, scale, tiling, drawFirst, bow, 1, 1, m_sGroupName);
843
0
}
844
845
void DotSceneLoader::processLightRange(pugi::xml_node& XMLNode, Light* pLight)
846
0
{
847
    // Process attributes
848
0
    Real inner = getAttribReal(XMLNode, "inner");
849
0
    Real outer = getAttribReal(XMLNode, "outer");
850
0
    Real falloff = getAttribReal(XMLNode, "falloff", 1.0);
851
852
    // Setup the light range
853
0
    pLight->setSpotlightRange(Radian(inner), Radian(outer), falloff);
854
0
}
855
856
void DotSceneLoader::processLightAttenuation(pugi::xml_node& XMLNode, Light* pLight)
857
0
{
858
    // Process attributes
859
0
    Real range = getAttribReal(XMLNode, "range");
860
0
    Real constant = getAttribReal(XMLNode, "constant");
861
0
    Real linear = getAttribReal(XMLNode, "linear");
862
0
    Real quadratic = getAttribReal(XMLNode, "quadratic");
863
864
    // Setup the light attenuation
865
0
    pLight->setAttenuation(range, constant, linear, quadratic);
866
0
}
867
868
void DotSceneLoader::processLightSourceSize(pugi::xml_node& XMLNode, Light* pLight)
869
0
{
870
    // Process attributes
871
0
    Real width = getAttribReal(XMLNode, "width");
872
0
    Real height = getAttribReal(XMLNode, "height");
873
874
    // Setup the light range
875
0
    pLight->setSourceSize(width, height);
876
0
}
877
878
void DotSceneLoader::processUserData(pugi::xml_node& XMLNode, UserObjectBindings& userData)
879
0
{
880
    // Process node (*)
881
0
    for (auto pElement : XMLNode.children("property"))
882
0
    {
883
0
        String name = getAttrib(pElement, "name");
884
0
        String type = getAttrib(pElement, "type");
885
0
        String data = getAttrib(pElement, "data");
886
887
0
        Any value;
888
0
        if (type == "bool")
889
0
            value = StringConverter::parseBool(data);
890
0
        else if (type == "float")
891
0
            value = StringConverter::parseReal(data);
892
0
        else if (type == "int")
893
0
            value = StringConverter::parseInt(data);
894
0
        else
895
0
            value = data;
896
897
0
        userData.setUserAny(name, value);
898
0
    }
899
0
}
900
901
void DotSceneLoader::processNodeAnimations(pugi::xml_node& XMLNode, SceneNode* pParent)
902
0
{
903
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Node Animations for SceneNode: " + pParent->getName(), LML_TRIVIAL);
904
905
    // Process node animations (*)
906
0
    for (auto pElement : XMLNode.children("animation"))
907
0
    {
908
0
        processNodeAnimation(pElement, pParent);
909
0
    }
910
0
}
911
912
void DotSceneLoader::processNodeAnimation(pugi::xml_node& XMLNode, SceneNode* pParent)
913
0
{
914
    // Process node animation (*)
915
916
    // Construct the animation name
917
0
    String name = getAttrib(XMLNode, "name");
918
919
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Node Animation: " + name, LML_TRIVIAL);
920
921
0
    Real length = getAttribReal(XMLNode, "length");
922
923
0
    Animation* anim = mSceneMgr->createAnimation(name, length);
924
925
0
    bool enable = getAttribBool(XMLNode, "enable", false);
926
0
    bool loop = getAttribBool(XMLNode, "loop", false);
927
928
0
    String interpolationMode = getAttrib(XMLNode, "interpolationMode");
929
930
0
    if (interpolationMode == "linear")
931
0
        anim->setInterpolationMode(Animation::IM_LINEAR);
932
0
    else if (interpolationMode == "spline")
933
0
        anim->setInterpolationMode(Animation::IM_SPLINE);
934
0
    else
935
0
        LogManager::getSingleton().logError("DotSceneLoader - Invalid interpolationMode: " + interpolationMode);
936
937
0
    String rotationInterpolationMode = getAttrib(XMLNode, "rotationInterpolationMode");
938
939
0
    if (rotationInterpolationMode == "linear")
940
0
        anim->setRotationInterpolationMode(Animation::RIM_LINEAR);
941
0
    else if (rotationInterpolationMode == "spherical")
942
0
        anim->setRotationInterpolationMode(Animation::RIM_SPHERICAL);
943
0
    else
944
0
        LogManager::getSingleton().logError("DotSceneLoader - Invalid rotationInterpolationMode: " + rotationInterpolationMode);
945
946
    // create a track to animate the camera's node
947
0
    NodeAnimationTrack* track = anim->createNodeTrack(0, pParent);
948
0
    pParent->setInitialState();
949
950
    // Process keyframes (*)
951
0
    for (auto pElement : XMLNode.children("keyframe"))
952
0
    {
953
0
        processKeyframe(pElement, track);
954
0
    }
955
956
    // create a new animation state to track this
957
0
    auto animState = mSceneMgr->createAnimationState(name);
958
0
    animState->setEnabled(enable);
959
0
    animState->setLoop(loop);
960
0
}
961
962
void DotSceneLoader::processKeyframe(pugi::xml_node& XMLNode, NodeAnimationTrack* pTrack)
963
0
{
964
    // Process node animation keyframe (*)
965
0
    Real time = getAttribReal(XMLNode, "time");
966
967
0
    LogManager::getSingleton().logMessage("[DotSceneLoader] Processing Keyframe: " + StringConverter::toString(time), LML_TRIVIAL);
968
969
0
    auto keyframe = pTrack->createNodeKeyFrame(time);
970
971
    // Process translation (?)
972
0
    if (auto pElement = XMLNode.child("position")) {
973
0
        Vector3 translation = parseVector3(pElement);
974
0
        keyframe->setTranslate(translation);
975
0
    }
976
977
    // Process rotation (?)
978
    //Quaternion rotation = Quaternion::IDENTITY;
979
0
    if (auto pElement = XMLNode.child("rotation")) {
980
0
        Quaternion rotation = parseQuaternion(pElement);
981
0
        keyframe->setRotation(rotation);
982
0
    }
983
984
    // Process scale (?)
985
    //Vector3 scale = parseVector3(XMLNode.child("scale"));
986
0
    if (auto pElement = XMLNode.child("scale")) {
987
0
        Vector3 scale = parseVector3(pElement);
988
0
        keyframe->setScale(scale);
989
0
    }
990
0
}
991
992
void DotSceneLoader::exportScene(SceneNode* rootNode, const String& outFileName)
993
0
{
994
0
    pugi::xml_document XMLDoc; // character type defaults to char
995
0
    auto comment = XMLDoc.append_child(pugi::node_comment);
996
0
    comment.set_value(StringUtil::format(" exporter: Plugin_DotScene %d.%d.%d ", OGRE_VERSION_MAJOR,
997
0
                                         OGRE_VERSION_MINOR, OGRE_VERSION_PATCH)
998
0
                          .c_str());
999
0
    auto scene = XMLDoc.append_child("scene");
1000
0
    scene.append_attribute("formatVersion") = "1.1";
1001
0
    scene.append_attribute("sceneManager") = rootNode->getCreator()->getTypeName().c_str();
1002
1003
0
    auto nodes = scene.append_child("nodes");
1004
1005
0
    for(auto c : rootNode->getChildren())
1006
0
        writeNode(nodes, static_cast<SceneNode*>(c));
1007
1008
    //writeNode(nodes, rootNode);
1009
1010
0
    XMLDoc.save_file(outFileName.c_str());
1011
0
}
1012
1013
static void write(pugi::xml_node& node, const Vector3& v)
1014
0
{
1015
0
    node.append_attribute("x") = StringConverter::toString(v.x).c_str();
1016
0
    node.append_attribute("y") = StringConverter::toString(v.y).c_str();
1017
0
    node.append_attribute("z") = StringConverter::toString(v.z).c_str();
1018
0
}
1019
1020
static void write(pugi::xml_node& node, const ColourValue& c)
1021
0
{
1022
0
    node.append_attribute("r") = StringConverter::toString(c.r).c_str();
1023
0
    node.append_attribute("g") = StringConverter::toString(c.g).c_str();
1024
0
    node.append_attribute("b") = StringConverter::toString(c.b).c_str();
1025
0
    node.append_attribute("a") = StringConverter::toString(c.a).c_str();
1026
0
}
1027
1028
void DotSceneLoader::writeNode(pugi::xml_node& parentXML, const SceneNode* n)
1029
0
{
1030
0
    auto nodeXML = parentXML.append_child("node");
1031
0
    if(!n->getName().empty())
1032
0
        nodeXML.append_attribute("name") = n->getName().c_str();
1033
1034
0
    auto pos = nodeXML.append_child("position");
1035
0
    write(pos, n->getPosition());
1036
1037
0
    auto scale = nodeXML.append_child("scale");
1038
0
    write(scale, n->getScale());
1039
1040
0
    auto rot = nodeXML.append_child("rotation");
1041
0
    rot.append_attribute("qw") = StringConverter::toString(n->getOrientation().w).c_str();
1042
0
    rot.append_attribute("qx") = StringConverter::toString(n->getOrientation().x).c_str();
1043
0
    rot.append_attribute("qy") = StringConverter::toString(n->getOrientation().y).c_str();
1044
0
    rot.append_attribute("qz") = StringConverter::toString(n->getOrientation().z).c_str();
1045
1046
0
    for(auto mo : n->getAttachedObjects())
1047
0
    {
1048
0
        if(auto c = dynamic_cast<Camera*>(mo))
1049
0
        {
1050
0
            auto camera = nodeXML.append_child("camera");
1051
0
            camera.append_attribute("name") = c->getName().c_str();
1052
0
            auto clipping = camera.append_child("clipping");
1053
0
            clipping.append_attribute("near") = StringConverter::toString(c->getNearClipDistance()).c_str();
1054
0
            clipping.append_attribute("far") = StringConverter::toString(c->getFarClipDistance()).c_str();
1055
0
            continue;
1056
0
        }
1057
1058
0
        if (auto l = dynamic_cast<Light*>(mo))
1059
0
        {
1060
0
            auto light = nodeXML.append_child("light");
1061
0
            light.append_attribute("name") = l->getName().c_str();
1062
0
            light.append_attribute("castShadows") = StringConverter::toString(l->getCastShadows()).c_str();
1063
1064
0
            if(!l->isVisible())
1065
0
                light.append_attribute("visible") = "false";
1066
1067
0
            auto diffuse = light.append_child("colourDiffuse");
1068
0
            write(diffuse, l->getDiffuseColour());
1069
0
            auto specular = light.append_child("colourSpecular");
1070
0
            write(specular, l->getSpecularColour());
1071
0
            switch (l->getType())
1072
0
            {
1073
0
            case Light::LT_POINT:
1074
0
                light.append_attribute("type") = "point";
1075
0
                break;
1076
0
            case Light::LT_DIRECTIONAL:
1077
0
                light.append_attribute("type") = "directional";
1078
0
                break;
1079
0
            case Light::LT_SPOTLIGHT:
1080
0
                light.append_attribute("type") = "spot";
1081
0
                break;
1082
0
            case Light::LT_RECTLIGHT:
1083
0
                light.append_attribute("type") = "rect";
1084
0
                break;
1085
0
            }
1086
1087
0
            if(l->getType() != Light::LT_DIRECTIONAL)
1088
0
            {
1089
0
                auto range = light.append_child("lightRange");
1090
0
                range.append_attribute("inner") =
1091
0
                    StringConverter::toString(l->getSpotlightInnerAngle()).c_str();
1092
0
                range.append_attribute("outer") =
1093
0
                    StringConverter::toString(l->getSpotlightOuterAngle()).c_str();
1094
0
                range.append_attribute("falloff") =
1095
0
                    StringConverter::toString(l->getSpotlightFalloff()).c_str();
1096
0
                auto atten = light.append_child("lightAttenuation");
1097
0
                atten.append_attribute("range") =
1098
0
                    StringConverter::toString(l->getAttenuationRange()).c_str();
1099
0
                atten.append_attribute("constant") =
1100
0
                    StringConverter::toString(l->getAttenuationConstant()).c_str();
1101
0
                atten.append_attribute("linear") =
1102
0
                    StringConverter::toString(l->getAttenuationLinear()).c_str();
1103
0
                atten.append_attribute("quadratic") =
1104
0
                    StringConverter::toString(l->getAttenuationQuadric()).c_str();
1105
0
            }
1106
1107
0
            continue;
1108
0
        }
1109
1110
0
        if(auto e = dynamic_cast<Entity*>(mo))
1111
0
        {
1112
0
            auto entity = nodeXML.append_child("entity");
1113
0
            entity.append_attribute("name") = e->getName().c_str();
1114
0
            entity.append_attribute("meshFile") = e->getMesh()->getName().c_str();
1115
1116
0
            if(!e->isVisible())
1117
0
                entity.append_attribute("visible") = "false";
1118
1119
            // Heuristic: assume first submesh is representative
1120
0
            auto sub0mat = e->getSubEntity(0)->getMaterial();
1121
0
            if(sub0mat != e->getMesh()->getSubMesh(0)->getMaterial())
1122
0
                entity.append_attribute("material") = sub0mat->getName().c_str();
1123
0
            continue;
1124
0
        }
1125
1126
0
        LogManager::getSingleton().logWarning("DotSceneLoader - unsupported MovableType " +
1127
0
                                            mo->getMovableType());
1128
0
    }
1129
1130
    // recurse
1131
0
    for(auto c : n->getChildren())
1132
0
        writeNode(nodeXML, static_cast<SceneNode*>(c));
1133
0
}
1134
1135
1
const Ogre::String& DotScenePlugin::getName() const {
1136
1
    static Ogre::String name = "DotScene Loader";
1137
1
    return name;
1138
1
}
1139
1140
void DotScenePlugin::install()
1141
1
{
1142
1
    mCodec = new DotSceneCodec();
1143
1
    Codec::registerCodec(mCodec);
1144
1
}
1145
1146
0
void DotScenePlugin::shutdown() {
1147
0
    Codec::unregisterCodec(mCodec);
1148
0
    delete mCodec;
1149
0
}
1150
1151
#ifndef OGRE_STATIC_LIB
1152
    extern "C" _OgreDotScenePluginExport void dllStartPlugin();
1153
    extern "C" _OgreDotScenePluginExport void dllStopPlugin();
1154
1155
    static DotScenePlugin plugin;
1156
1157
    extern "C" _OgreDotScenePluginExport void dllStartPlugin()
1158
    {
1159
        Ogre::Root::getSingleton().installPlugin(&plugin);
1160
    }
1161
    extern "C" _OgreDotScenePluginExport void dllStopPlugin()
1162
    {
1163
        Ogre::Root::getSingleton().uninstallPlugin(&plugin);
1164
    }
1165
#endif