This URL has Read-Only access.

Statistics
| Branch: | Tag: | Revision:

root / src / spin / ModelNode.cpp @ c38d81dd

History | View | Annotate | Download (21.3 kB)

1
// -----------------------------------------------------------------------------
2
// |    ___  ___  _  _ _     ___                                        _      |
3
// |   / __>| . \| || \ |   | __>_ _  ___ ._ _ _  ___  _ _ _  ___  _ _ | |__   |
4
// |   \__ \|  _/| ||   |   | _>| '_><_> || ' ' |/ ._>| | | |/ . \| '_>| / /   |
5
// |   <___/|_|  |_||_\_|   |_| |_|  <___||_|_|_|\___.|__/_/ \___/|_|  |_\_\   |
6
// |                                                                           |
7
// |---------------------------------------------------------------------------|
8
//
9
// http://spinframework.sourceforge.net
10
// Copyright (C) 2009 Mike Wozniewski, Zack Settel
11
//
12
// Developed/Maintained by:
13
//    Mike Wozniewski (http://www.mikewoz.com)
14
//    Zack Settel (http://www.sheefa.net/zack)
15
//
16
// Principle Partners:
17
//    Shared Reality Lab, McGill University (http://www.cim.mcgill.ca/sre)
18
//    La Societe des Arts Technologiques (http://www.sat.qc.ca)
19
//
20
// Funding by:
21
//    NSERC/Canada Council for the Arts - New Media Initiative
22
//    Heritage Canada
23
//    Ministere du Developpement economique, de l'Innovation et de l'Exportation
24
//
25
// -----------------------------------------------------------------------------
26
//  This file is part of the SPIN Framework.
27
//
28
//  SPIN Framework is free software: you can redistribute it and/or modify
29
//  it under the terms of the GNU Lesser General Public License as published by
30
//  the Free Software Foundation, either version 3 of the License, or
31
//  (at your option) any later version.
32
//
33
//  SPIN Framework is distributed in the hope that it will be useful,
34
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
35
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36
//  GNU Lesser General Public License for more details.
37
//
38
//  You should have received a copy of the GNU Lesser General Public License
39
//  along with SPIN Framework. If not, see <http://www.gnu.org/licenses/>.
40
// -----------------------------------------------------------------------------
41

    
42

    
43

    
44
#include <osgDB/ReadFile>
45
#include <osgDB/FileUtils>
46
#include <osgDB/FileNameUtils>
47
#include <osg/Group>
48
#include <osg/Billboard>
49
#include <osg/AutoTransform>
50
#include <osg/MatrixTransform>
51
#include <osg/Switch>
52
#include <osg/Sequence>
53

    
54
//#include <osgFX/Scribe>
55
//#include <osgFX/Cartoon>
56

    
57

    
58
#include <osgUtil/SmoothingVisitor>
59
#include <osg/BoundingBox>
60
#include <osg/ImageStream>
61

    
62
#include "ModelNode.h"
63
#include "osgUtil.h"
64
#include "SceneManager.h"
65
#include "spinApp.h"
66
#include "spinBaseContext.h"
67
#include "nodeVisitors.h"
68

    
69
#include "ImageTexture.h"
70
#include "VideoTexture.h"
71
#include "SharedVideoTexture.h"
72

    
73
using namespace std;
74

    
75
extern pthread_mutex_t sceneMutex;
76

    
77
namespace spin
78
{
79

    
80
// ===================================================================
81
// constructor:
82
ModelNode::ModelNode (SceneManager *sceneManager, char *initID) : GroupNode(sceneManager, initID)
83
{
84
        this->setName(string(id->s_name) + ".ModelNode");
85
        nodeType = "ModelNode";
86

    
87
    // Save a pointer to the current attachmentNode (we will attach the loaded
88
    // 3D mesh there:
89
    _modelAttachmentNode = this->getAttachmentNode();
90
    
91
    // Now, add a new 'centroid' node, and make that the new attachmentNode.
92
    // Children of this node will be attached there, and an offset will be added
93
    // depending whether _attachCentroid is enabled or not:
94
    _centroid = new osg::PositionAttitudeTransform();
95
    _centroid->setName(getID()+".CentroidOffset");
96
    this->getAttachmentNode()->addChild(_centroid.get());
97
    this->setAttachmentNode(_centroid.get());
98

    
99
    _attachCentroid = false;
100
    _registerStates = false;
101
        _statesetList.clear();
102
    _statesetList.push_back(stateset_);
103
    _renderBin = 10;
104
    _lightingOverride = 0;
105

    
106
        modelPath = "NULL";
107

    
108

    
109
}
110

    
111
// ===================================================================
112
// destructor
113
ModelNode::~ModelNode()
114
{
115
        //std::cout << "Destroying ModelNode: " << this->id->s_name << std::endl;
116
}
117

    
118

    
119
void ModelNode::updateNodePath(bool updateChildren)
120
{
121
        // call GroupNode's method, which will update all the way from the root, and
122
        // we just need to add the centroid node:
123
        GroupNode::updateNodePath(false);
124
        currentNodePath.push_back(_centroid.get());
125

    
126
    /*
127
    osg::NodePath::iterator iter;
128
    std::cout << "nodepath for " << id->s_name << ":" << std::endl;
129
    for (iter = currentNodePath.begin(); iter!=currentNodePath.end(); iter++)
130
    {
131
        std::cout << " > " << (*iter)->getName() << std::endl;
132
    }
133
    */
134
    
135
        // now update NodePaths for all children:
136
        updateChildNodePaths();
137
}
138

    
139

    
140
// ===================================================================
141
// ======================== SET METHODS: =============================
142
// ===================================================================
143

    
144
void ModelNode::setContext (const char *newvalue)
145
{
146
        if (contextString==string(newvalue)) return;
147

    
148
        // need to redraw after setContext() is called:
149
        ReferencedNode::setContext(newvalue);
150
        drawModel();
151
}
152

    
153
void ModelNode::setModelFromFile (const char* filename)
154
{
155
        string path = getRelativePath(string(filename));
156
        
157
        // don't do anything if the current model is already loaded:
158
        if (path==modelPath) return;
159

    
160
        modelPath = path;
161

    
162
        drawModel();
163

    
164
        BROADCAST(this, "ss", "setModelFromFile", getModelFromFile());
165
}
166

    
167
void ModelNode::setAttachCentroid (int i)
168
{
169
    _attachCentroid = (bool)i;
170

    
171
    // TODO: bound may change dynamically (eg, animations, switch nodes, etc)
172
    // so this should be done in the update callback whenever bound is made 
173
    // dirty(). Can we check for that?
174
    
175
    if (model.valid() && _attachCentroid)
176
    {        
177
        osg::BoundingSphere bound = model->computeBound();
178
        _centroid->setPosition(bound.center());
179
        osg::Vec3 c = bound.center();
180
        std::cout << "setting centroid for model: " <<c.x()<<","<<c.y()<< ","<<c.z() << std::endl;
181
    }
182
    else
183
    {
184
        std::cout << "setting centroid for model: 0,0,0" << std::endl;
185
        _centroid->setPosition(osg::Vec3(0.0,0.0,0.0));
186
    }
187
                    
188
    BROADCAST(this, "si", "setAttachCentroid", getAttachCentroid());
189
}
190

    
191
void ModelNode::makeCentered()
192
{
193
    if (model.valid())
194
    {
195
        osg::BoundingSphere bound = _modelAttachmentNode->computeBound();
196

    
197
        std::cout << "centering the model to centroid: ("<<bound.center().x()<<","<<bound.center().y()<<","<<bound.center().z()<<") length="<< bound.center().length() << std::endl;
198

    
199
        if (bound.center().length() > 0.00001)
200
        {
201
            _modelAttachmentNode->removeChild(model.get());
202

    
203
            osg::PositionAttitudeTransform *mpat = new osg::PositionAttitudeTransform();
204
            mpat->setName(getID()+".ModelAttachment");
205
            mpat->setPosition(-bound.center());
206

    
207
            mpat->addChild(model.get());
208
            _modelAttachmentNode->addChild(mpat);
209

    
210
            //mpat->addChild(model.get());
211
            //_modelAttachmentNode->replaceChild(model.get(), mpat);
212
        }
213

    
214
        _centroid->setPosition(osg::Vec3(0.0,0.0,0.0));
215

    
216
        bound = _modelAttachmentNode->computeBound();
217
        std::cout << "new centroid: ("<<bound.center().x()<<","<<bound.center().y()<<","<<bound.center().z()<<") length="<< bound.center().length() << std::endl;
218

    
219
    }
220

    
221

    
222
}
223

    
224
void ModelNode::setStateRegistration (int i)
225
{
226
        _registerStates = (bool)i;
227

    
228
        BROADCAST(this, "si", "setStateRegistration", getStateRegistration());
229
}
230

    
231
void ModelNode::setRenderBin (int i)
232
{
233
        _renderBin = i;
234

    
235
        if (model.valid())
236
        {
237
                osg::StateSet *ss = model->getOrCreateStateSet();
238
                ss->setRenderBinDetails( (int)_renderBin, "RenderBin");
239
        }
240

    
241
        BROADCAST(this, "si", "setRenderBin", _renderBin);
242
}
243

    
244
void ModelNode::setPlaying (int index, int playstate)
245
{
246
    _playState[index] = playstate;
247
    
248
    if (sequencer[index].valid())
249
    {
250
        osg::Sequence::SequenceMode mode = sequencer[index]->getMode();
251
        std:cout << "about to set mode to "<<_playState[index] << ", old="<<mode<<std::endl;
252
        
253
        sequencer[index]->setMode((osg::Sequence::SequenceMode)_playState[index]);
254
        
255
    
256
        std::cout << "osgSequence mode: " << sequencer[index]->getMode() << ", duration: " << sequencer[index]->getNumFrames()<< ", speed: " << sequencer[index]->getSpeed() << ", lastFrameTime: " << sequencer[index]->getLastFrameTime() <<std::endl;
257
    }
258
    else std::cout << "Warning: Model '" << this->getID() << "' has no Sequence for index " << index << std::endl;
259
    
260
    BROADCAST(this, "sii", "setPlaying", index, _playState[index]);
261
}
262
    
263
void ModelNode::setKeyframe (int index, float keyframe)
264
{
265
        _keyframe[index] = keyframe;
266

    
267
        if (switcher[index].valid())
268
        {
269
                for (unsigned j = 1; j < switcher[index]->getNumChildren(); j++) 
270
            switcher[index]->setValue(j, false);
271
                switcher[index]->setValue((int)(switcher[index]->getNumChildren()*_keyframe[index]), true);
272
        }
273

    
274
        else if (sequencer[index].valid())
275
        {
276
                sequencer[index]->setValue((int)(sequencer[index]->getNumChildren()*_keyframe[index]));
277
        }
278

    
279
        BROADCAST(this, "sif", "setKeyframe", index, _keyframe[index]);
280
}
281

    
282
void ModelNode::setStateSet (int i, const char *replacement)
283
{
284
    osg::ref_ptr<ReferencedStateSet> ssReplacement = sceneManager->getStateSet(replacement);
285

    
286
    if (i==0)
287
    {
288
        osg::ref_ptr<ReferencedStateSet> ss = dynamic_cast<ReferencedStateSet*>(stateset_->s_thing);
289
        if (mainTransform.valid() && ss.valid()) mainTransform->setStateSet( ss.get() );
290
    }
291
    else if ((i>0) && (i<_statesetList.size()))
292
    {
293
        osg::ref_ptr<ReferencedStateSet> ssOrig = dynamic_cast<ReferencedStateSet*>(_statesetList[i]->s_thing);
294

    
295
        if (ssOrig.valid() && ssReplacement.valid())
296
        {
297
            // We need to search through the drawables and nodes that originally had
298
            // statesets and replace ssOrig with ssReplacement:
299
            std::vector<osg::Drawable*>::iterator dItr;
300
            for (dItr=_ssDrawableList.begin(); dItr!=_ssDrawableList.end(); ++dItr)
301
            {
302
                if ((*dItr)->getStateSet() == ssOrig.get())
303
                {
304
                    (*dItr)->setStateSet(ssReplacement.get());
305
                }
306
            }
307
            std::vector<osg::Node*>::iterator nItr;
308
            for (nItr=_ssNodeList.begin(); nItr!=_ssNodeList.end(); ++nItr)
309
            {
310
                if ((*nItr)->getStateSet() == ssOrig.get())
311
                {
312
                    (*nItr)->setStateSet(ssReplacement.get());
313
                }
314
            }
315
        }
316
    }
317
    
318
    if (ssReplacement.valid())
319
    {
320
        _statesetList[i] = ssReplacement->id;
321
        BROADCAST(this, "sis", "setStateSet", i, _statesetList[i]->s_name);
322
    }
323
}
324

    
325
void ModelNode::updateStateSet()
326
{
327
    // updateStateSet should do nothing for ModelNode.
328
    this->setStateSet(0, stateset_->s_name);
329
}
330

    
331
void ModelNode::setLighting(int i)
332
{
333

    
334
    if (_lightingOverride==(bool)i) return;
335
    _lightingOverride = (bool)i;
336

    
337
    if (model.valid())// && !stateset->s_thing)
338
    {
339
        osg::StateSet *ss = model->getOrCreateStateSet();
340
        if (_lightingOverride) ss->setMode( GL_LIGHTING, osg::StateAttribute::ON );
341
        else ss->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
342
    }
343

    
344
    BROADCAST(this, "si", "setLighting", getLighting());
345

    
346
}
347

    
348
// ===================================================================
349
// ===================================================================
350
// ===================================================================
351
void ModelNode::drawModel()
352
{
353
        pthread_mutex_lock(&sceneMutex);
354
        
355
        if (model.valid())
356
        {
357

    
358
        _modelAttachmentNode->removeChild(model.get());
359
            
360
        model = NULL;
361
        _centroid->setPosition(osg::Vec3(0.0,0.0,0.0));
362
                _statesetList.clear();
363
        _statesetList.push_back(stateset_);
364
                _ssDrawableList.clear();
365
                _ssNodeList.clear();
366

    
367
                //if (sceneManager->sharedStateManager.valid()) sceneManager->sharedStateManager->prune();
368
                
369
                
370
                for (int i=0; i<MODELNODE_NUM_ANIM_CONTROLS; i++)
371
                {
372
                        // re-initialize:
373
                        switcher[i] = NULL;
374
                        sequencer[i] = NULL;
375
                        animationMode[i] = OFF;
376
                        _keyframe[i] = 0;
377
                }
378
        }
379

    
380
        bool ignoreOnThisHost = (not spinApp::Instance().getContext()->isServer() and (this->getContext()==getHostname()));
381
        
382
        if ((modelPath != string("NULL")) && !ignoreOnThisHost)
383
        {
384

    
385
        //osg::setNotifyLevel(osg::DEBUG_FP);
386
                model = (osg::Group*)(osgDB::readNodeFile( getAbsolutePath(modelPath).c_str() ));
387
        //osg::setNotifyLevel(osg::FATAL);
388
        
389
                if (model.valid())
390
                {
391
                        
392
                        
393
                        // *****************************************************************
394
                        
395

    
396
                        /*
397
                        // This is a better way to do this:
398
                        NodeList foundNodes;
399
                        NodeSearcher nodeSearcher(foundNodes);
400
                        nodeSearcher.search(model.get(), "OSG_Switch");
401
                        if (foundNodes.size())
402
                                std::cout << "found " << foundNodes.size() << " switch nodes" << std::endl;
403

    
404
                        int count = 0;
405
                        for (NodeList::iterator itr=foundNodes.begin(); itr!=foundNodes.end(); ++itr)
406
                        {
407

    
408

    
409
                        }
410
                        */
411

    
412
                        SearchVisitor searchVisitor;
413
                        char buf[16];
414
                        for (int i=0; i<MODELNODE_NUM_ANIM_CONTROLS; i++)
415
                        {
416

    
417
                                sprintf( buf, "%02d", i );
418

    
419
                                // Check if there are multiple states available from a osg::Switch
420
                                // note: from 3DS exporter, switch nodes are called: OSG_Switch01, etc.
421
                                searchVisitor.searchNode(model.get(), "OSG_Switch"+string(buf));
422

    
423
                                switcher[i] = searchVisitor.getSwitchNode();
424
                                if (switcher[i].valid())
425
                                {
426
                                        std::cout << "found OSG_Switch" << buf << " with " << switcher[i]->getNumChildren() << " frames" << std::endl;
427
                                        animationMode[i] = SWITCH;
428
                                        // initialize so only first frame is visible:
429
                                        switcher[i]->setValue(0, true);
430
                                        for (int j=1; j<switcher[i]->getNumChildren(); j++) switcher[i]->setValue(j, false);
431

    
432
                                }
433

    
434
                                // Check if there is an osg::Sequence node.
435
                                searchVisitor.searchNode(model.get(), "OSG_Sequence"+string(buf));
436
                                sequencer[i] = searchVisitor.getSequenceNode();
437
                                if (sequencer[i].valid())
438
                                {
439
                                        std::cout << "found OSG_Sequence" << buf << " with " << sequencer[i]->getNumChildren() << " frames" << std::endl;
440
                    sequencer[i]->setDataVariance(osg::Object::DYNAMIC);
441
                                        animationMode[i] = SEQUENCE;
442
                                        sequencer[i]->setValue(0);
443
                    //sequencer[i]->setMode(osg::Sequence::PAUSE);
444
                    //sequencer[i]->setMode(osg::Sequence::START);
445
                                }
446

    
447
                        }
448
                        
449
                        // *****************************************************************
450
                        // search for special "billboard" nodes
451

    
452
                        //optimizer.optimize(model.get(), osgUtil::Optimizer::ALL_OPTIMIZATIONS);
453
                        //optimizer.optimize(sceneManager->worldNode.get(), osgUtil::Optimizer::ALL_OPTIMIZATIONS);
454
                        optimizer.optimize(model.get());
455
                        /*
456
            optimizer.optimize(model.get(),
457
                osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS |
458
                osgUtil::Optimizer::REMOVE_REDUNDANT_NODES |
459
                osgUtil::Optimizer::REMOVE_LOADED_PROXY_NODES |
460
                osgUtil::Optimizer::COMBINE_ADJACENT_LODS |
461
                osgUtil::Optimizer::SHARE_DUPLICATE_STATE |
462
                osgUtil::Optimizer::MERGE_GEOMETRY |
463
                osgUtil::Optimizer::CHECK_GEOMETRY |
464
                osgUtil::Optimizer::SPATIALIZE_GROUPS | 
465
                osgUtil::Optimizer::COPY_SHARED_NODES | 
466
                osgUtil::Optimizer::TRISTRIP_GEOMETRY |
467
                osgUtil::Optimizer::TESSELLATE_GEOMETRY |
468
                osgUtil::Optimizer::OPTIMIZE_TEXTURE_SETTINGS |
469
                osgUtil::Optimizer::MERGE_GEODES |
470
                osgUtil::Optimizer::FLATTEN_BILLBOARDS |
471
                //osgUtil::Optimizer::TEXTURE_ATLAS_BUILDER |
472
                osgUtil::Optimizer::STATIC_OBJECT_DETECTION |
473
                osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS 
474
                        );
475
            */
476

    
477
                        StateSetList statesets;
478
                        TextureStateSetFinder f(statesets);
479
                        model->accept(f);
480
                        
481
                        // *****************************************************************
482
                        // Here, we search through all statesets and replace them with our
483
                        // own generated RegisteredStateSet objects. This way, we're able to
484
                        // control texture and shader information via OSC
485
                        if (_registerStates)
486
                        {
487
                                for (StateSetList::iterator itr=statesets.begin(); itr!=statesets.end(); ++itr)
488
                                {
489
                                        // check if this stateset has a special texture
490
                                        osg::StateAttribute *attr = (*itr)->getTextureAttribute(0,osg::StateAttribute::TEXTURE);
491
                                        if (attr)
492
                                        {
493

    
494
                                                std::string imageFile = attr->asTexture()->getImage(0)->getFileName();
495

    
496
                                                // If file came from other OS, we should fix it:
497
                                                imageFile = osgDB::convertFileNameToNativeStyle(imageFile);
498
                                                std::string imageFileLessExtension = imageFile.substr(0, imageFile.rfind("."));
499

    
500
                                                // in Linux, imageFile is relative, so check if it
501
                                                // exists and prepend the modelPath in case:
502
                                                if (!osgDB::fileExists(imageFile))
503
                                                {
504
                                                        imageFile = osgDB::concatPaths(osgDB::getFilePath(getAbsolutePath(modelPath)), imageFile);
505
                                                }
506

    
507
                                                /*
508
                                                std::string vidPath;
509
                                                if (imageFileLessExtension.substr(1)=="/")
510
                                                {
511
                                                        // absolute path, so don't change it
512
                                                        vidPath = imageFileLessExtension;
513
                                                }
514
                                                else {
515
                                                        // relative path:
516
                                                        vidPath = osgDB::concatPaths(osgDB::getFilePath(getAbsolutePath(modelPath)), imageFileLessExtension);
517
                                                }
518
                                                */
519

    
520
                                                // Now, in order to replace this stateset with another
521
                                                // in the future, we need to have pointers to all of the
522
                                                // drawables/nodes that originally used it. So before we
523
                                                // do anything, we store a big ugly array of all these
524
                                                // pointers:
525
                                                for (int i=0; i < (*itr)->getNumParents(); i++)
526
                                                {
527
                                                        // stateset can be shared by several parents within
528
                                                        // the model, but they must be of type osg::Drawable
529
                                                        // or osg::Node
530

    
531
                                                        osg::Drawable *drawable = dynamic_cast<osg::Drawable*>((*itr)->getParent(i));
532
                                                        osg::Node *node = dynamic_cast<osg::Node*>((*itr)->getParent(i));
533
                                                        if (drawable) _ssDrawableList.push_back(drawable);
534
                                                        if (node) _ssNodeList.push_back(node);
535
                                                }
536

    
537

    
538
                                                // NEW
539
                                                //osg::ref_ptr<ReferencedStateSet> ss = sceneManager->createStateSet(osgDB::getNameLessExtension(imageFile).c_str());
540
                                                osg::ref_ptr<ReferencedStateSet> ss = sceneManager->createStateSet(imageFile.c_str());
541
                                                if (ss.valid())
542
                                                {
543
                                                        ss->replace((*itr).get());
544
                                                        this->_statesetList.push_back(ss->id);
545
                                                        std::cout << "  Replaced placeholder texture with " << ss->classType << ": " << ss->id->s_name << std::endl;
546
                            
547
                            //
548
                            std::cout << "Now we have " << _statesetList.size() << " statesets:" << std::endl;
549
                            for (int i=0; i<_statesetList.size(); i++)
550
                            {
551
                                std::cout << " - " << _statesetList[i]->s_name << std::endl;
552
                            }
553

    
554
                                                }
555

    
556
                                        } // if texture attribute
557
                                } // stateset iterator
558
                        } // if _registerStates
559
                    
560
                        
561
                        // *****************************************************************
562

    
563
                        _modelAttachmentNode->addChild(model.get());
564
            model->setName(getID()+".file['"+modelPath+"']");
565
            //model->setName(string(id->s_name) + ".model['" + modelPath + "']");
566

    
567
                        std::cout << "Created model " << modelPath << std::endl;
568
                        osg::BoundingSphere bound = model->computeBound();
569
                        osg::Vec3 c = bound.center();
570
                        std::cout << "  center=" <<c.x()<<","<<c.y()<< ","<<c.z()<< "  radius=" << bound.radius() << "  numTextures=" << statesets.size() << std::endl;
571

    
572
            if (_attachCentroid)
573
                _centroid->setPosition(c);
574
            
575
                        //osg::StateSet *modelStateSet = new osg::StateSet();
576
                        //modelStateSet->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
577
                        //model->setStateSet(modelStateSet);
578

    
579

    
580
                        osg::StateSet *ss = model->getOrCreateStateSet();
581

    
582
            // Should we override our _renderBin value using ss->getBinNumber(),
583
            // or shoudld we apply our currently stored _renderBin to the model?
584
                        ss->setRenderBinDetails( (int)_renderBin, "RenderBin");
585

    
586
                        /*
587
                        if (sceneManager->sharedStateManager.valid())
588
                                sceneManager->sharedStateManager->share(model.get());
589
                        */
590
                        
591
                } else {
592
                        std::cout << "ERROR [ModelNode::drawModel]: Could not find \"" << modelPath << "\". Make sure file exists, and that it is a valid 3D model." << std::endl;
593
                }
594
        }
595
        
596
        pthread_mutex_unlock(&sceneMutex);
597
}
598

    
599
std::vector<lo_message> ModelNode::getState () const
600
{
601
        // inherit state from base class
602
        std::vector<lo_message> ret = GroupNode::getState();
603

    
604
        int i;
605
        lo_message msg;
606

    
607
        msg = lo_message_new();
608
        // note: this method MUST precede "setModelFromFile"
609
        lo_message_add(msg, "si", "setStateRegistration", getStateRegistration());
610
        ret.push_back(msg);
611

    
612
        msg = lo_message_new();
613
        lo_message_add(msg, "ss", "setModelFromFile", modelPath.c_str());
614
        ret.push_back(msg);
615

    
616
        msg = lo_message_new();
617
        lo_message_add(msg, "si", "setAttachCentroid", getAttachCentroid());
618
        ret.push_back(msg);
619

    
620
    msg = lo_message_new();
621
    lo_message_add(msg, "si", "setLighting", getLighting());
622
    ret.push_back(msg);
623
    
624
        for (i=0; i<MODELNODE_NUM_ANIM_CONTROLS; i++)
625
        {
626
                if (switcher[i].valid() || sequencer[i].valid())
627
                {
628
            msg = lo_message_new();
629
                        lo_message_add(msg, "sii", "setPlaying", i, _playState[i]);
630
                        ret.push_back(msg);
631

    
632
                        msg = lo_message_new();
633
                        lo_message_add(msg, "sif", "setKeyframe", i, _keyframe[i]);
634
                        ret.push_back(msg);
635
                }
636
        }
637

    
638
        msg = lo_message_new();
639
        lo_message_add(msg, "si", "setRenderBin", getRenderBin());
640
        ret.push_back(msg);
641

    
642
        for (i=1; i<_statesetList.size(); i++)
643
        {
644
                msg = lo_message_new();
645
        lo_message_add(msg, "sis", "setStateSet", i, _statesetList[i]->s_name);
646
        ret.push_back(msg);
647
        }
648

    
649
        return ret;
650
}
651

    
652
} // end of namespace spin
653