This URL has Read-Only access.

Statistics
| Branch: | Tag: | Revision:

root / src / spin / GroupNode.cpp @ 2426396a

History | View | Annotate | Download (48.6 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
#include <osg/ComputeBoundsVisitor>
43
#include <osgGA/GUIEventAdapter>
44
#include <osg/BlendFunc>
45
#include <osg/BlendColor>
46

    
47
#include <osgManipulator/TabBoxDragger>
48
#include <osgManipulator/TabBoxTrackballDragger>
49
#include <osgManipulator/TabPlaneDragger>
50
#include <osgManipulator/TabPlaneTrackballDragger>
51
//#include <osgManipulator/TrackballDragger>
52
#include <osgManipulator/Translate1DDragger>
53
#include <osgManipulator/Translate2DDragger>
54
//#include <osgManipulator/TranslateAxisDragger>
55
#include "DraggerWith3Axes.h"
56
#include "DraggerTrackball.h"
57

    
58
#include "GroupNode.h"
59
#include "SceneManager.h"
60
#include "spinApp.h"
61
#include "spinBaseContext.h"
62
#include "osgUtil.h"
63
#include "UserNode.h"
64
#include "spinApp.h"
65

    
66
extern pthread_mutex_t sceneMutex;
67

    
68
namespace spin
69
{
70

    
71
DraggerCallback::DraggerCallback(GroupNode* g) : DraggerTransformCallback(g->getTransform())
72
{
73
    groupNode = g;
74
}
75

    
76
bool DraggerCallback::receive(const osgManipulator::MotionCommand& command)
77
{
78
    if (!_transform) return false;
79
    
80
    bool success = false;
81
        
82
    switch (command.getStage())
83
    {
84
        case osgManipulator::MotionCommand::START:
85
        {
86
            //std::cout << "DraggerCallback START " << std::endl;
87
            
88
            // Save the current matrix
89
            _startMotionMatrix = _transform->getMatrix();
90
            
91
            // Get the LocalToWorld and WorldToLocal matrix for this node.
92
            osg::NodePath nodePathToRoot;
93
            osgManipulator::computeNodePathToRoot(*_transform,nodePathToRoot);
94
            _localToWorld = osg::computeLocalToWorld(nodePathToRoot);
95
            _worldToLocal = osg::Matrix::inverse(_localToWorld);
96
            
97
            success = true;
98
            break;
99
        }
100
        case osgManipulator::MotionCommand::MOVE:
101
        {
102
            osg::Matrix m = _localToWorld * command.getWorldToLocal()
103
                              * command.getMotionMatrix()
104
                              * command.getLocalToWorld()
105
                              * _worldToLocal
106
                              * _startMotionMatrix;
107
            
108
            /*
109
            std::cout << "MotionMatrix:" << std::endl;
110
            osg::Matrix mm = command.getMotionMatrix();
111
            std::cout << "  t  = " << stringify(mm.getTrans()) << std::endl;
112
            std::cout << "  q  = " << stringify(mm.getRotate()) << std::endl;
113
            std::cout << "  s  = " << stringify(mm.getScale()) << std::endl;
114
            
115
            std::cout << "DraggerCallback MOVE: " << std::endl;
116
            std::cout << "  t  = " << stringify(m.getTrans()) << std::endl;
117
            std::cout << "  q  = " << stringify(m.getRotate()) << std::endl;
118
            std::cout << "  s  = " << stringify(m.getScale()) << std::endl;
119
            */
120
        
121
                
122
            // Note: we don't know if the DraggerCallback is called on a client
123
            // machine or a server. For example, the event might originate with
124
            // mouse events in ViewerManipulator.
125
            
126
            // Each requires a different form of sending the update:
127

    
128
            
129
            
130
            // Use matrix::decompose and never matrix::getRotate when dealing
131
            // with scaled matrices:
132
            osg::Vec3 t;
133
            osg::Quat q;
134
            osg::Vec3 s;
135
            osg::Quat so;
136
            m.decompose(t, q, s, so);
137
            
138
            if (spinApp::Instance().getContext()->isServer())
139
            {
140
                groupNode->setTranslation(t.x(), t.y(), t.z());
141
                groupNode->setOrientationQuat(q.x(), q.y(), q.z(), q.w());
142
                groupNode->setScale(s.x(), s.y(), s.z());
143
            }
144
            else
145
            {
146
                spinApp::Instance().NodeMessage(groupNode->getID().c_str(), "sfff", "setTranslation", t.x(), t.y(), t.z(), SPIN_ARGS_END);
147
                spinApp::Instance().NodeMessage(groupNode->getID().c_str(), "sffff", "setOrientationQuat", q.x(), q.y(), q.z(), q.w(), SPIN_ARGS_END);
148
                spinApp::Instance().NodeMessage(groupNode->getID().c_str(), "sfff", "setScale", s.x(), s.y(), s.z(), SPIN_ARGS_END);
149
            }
150
            
151
            
152
            /*
153
            if (spinApp::Instance().getContext()->isServer())
154
            {
155
                groupNode->setManipulatorMatrix(
156
                    m(0,0),m(0,1),m(0,2),m(0,3),
157
                    m(1,0),m(1,1),m(1,2),m(1,3),
158
                    m(2,0),m(2,1),m(2,2),m(2,3),
159
                    m(3,0),m(3,1),m(3,2),m(3,3));
160
            }
161
            else
162
            {
163
                spinApp::Instance().NodeMessage(groupNode->getID().c_str(), "sffffffffffffffff", "setManipulatorMatrix",
164
                    m(0,0),m(0,1),m(0,2),m(0,3),
165
                    m(1,0),m(1,1),m(1,2),m(1,3),
166
                    m(2,0),m(2,1),m(2,2),m(2,3),
167
                    m(3,0),m(3,1),m(3,2),m(3,3),
168
                    SPIN_ARGS_END);
169
            }
170
            */
171
            
172
            success = true;
173
            break;
174
        }
175
        case osgManipulator::MotionCommand::FINISH:
176
        {
177
            groupNode->updateDraggerMatrix();
178
            success = true;
179
            break;
180
        }
181
        case osgManipulator::MotionCommand::NONE:
182
        default:
183
            success = false;
184
    }
185

    
186
    return success;
187
}
188
    
189
    
190
// ***********************************************************
191
// constructor:
192
GroupNode::GroupNode (SceneManager *sceneManager, char *initID) : ReferencedNode(sceneManager, initID)
193
{
194
    nodeType = "GroupNode";
195
    this->setName(getID() + ".GroupNode");
196

    
197
    //_reportGlobals = false;
198
    reportMode_ = GroupNode::NONE;
199
    interactionMode_ = GroupNode::STATIC;
200
    orientationMode_ = GroupNode::NORMAL;
201
    
202
    orientationTarget_ = gensym("NULL");
203
    manipulatorType_ = "NULL";
204
    manipulatorUpdateFlag_ = false;
205
    manipulatorShadowCopy_ = true;
206

    
207
    mainTransform_ = new osg::MatrixTransform();
208
    mainTransform_->setName(getID() + ".MainTransform");
209
    //mainTransform_->setAttitude(osg::Quat(0.0,0.0,0.0,0.0));
210
    //mainTransform_->setPosition(osg::Vec3(0.0,0.0,0.0));
211
    this->addChild(mainTransform_.get());
212

    
213
    computationMode_ = SERVER_SIDE;
214

    
215
    stateset_ = gensym("NULL");
216

    
217
    clipNode_ = new osg::ClipNode();
218
    clipNode_->setCullingActive(false);
219
    clipNode_->setName(getID() + ".ClipNode");
220
    mainTransform_->addChild(clipNode_.get());
221
    
222
    scale_ = osg::Vec3(1.0,1.0,1.0);
223
    velocity_ = osg::Vec3(0.0,0.0,0.0);
224
    velocityMode_ = GroupNode::TRANSLATE;
225
    spin_ = osg::Vec3(0.0,0.0,0.0);
226
    damping_ = 0.0;
227
    
228
    broadcastLock_ = false;
229
    
230
    updateMatrix();
231

    
232
    // When children are attached to this, they get added to the attachNode:
233
    // NOTE: by changing this, we MUST override the updateNodePath() method!
234
    setAttachmentNode(clipNode_.get());
235

    
236
    // keep a timer for velocity calculation:
237
    lastTick_ = osg::Timer::instance()->tick();
238
    lastUpdate_ = osg::Timer::instance()->tick();
239
}
240

    
241
// ***********************************************************
242
// destructor
243
GroupNode::~GroupNode()
244
{
245

    
246
}
247

    
248
#define EPSILON 0.0001
249

    
250
void GroupNode::callbackUpdate(osg::NodeVisitor* nv)
251
{
252
    ReferencedNode::callbackUpdate(nv);
253
    
254
    if (manipulatorUpdateFlag_)
255
    {
256
        drawManipulator();
257
        manipulatorUpdateFlag_ = false;
258
    }
259
    
260
    dumpGlobals(false); // never force globals here
261

    
262
    
263
    osg::Timer_t tick = osg::Timer::instance()->tick();
264
    
265
    float maxUpdateDelta = 0.05; // update when dt is at least 0.05s (ie 20hz)
266
    //float maxUpdateDelta = 0.1; // update when dt is at least 0.1s (ie 10hz)
267
  
268
    
269
    // Decide whether messages should be broadcasted during this update:
270
    if (spinApp::Instance().getContext()->isServer())
271
    {
272
        // if this node does computation on the client side, don't let the
273
        // server broadcast any messages:
274
        if (computationMode_==CLIENT_SIDE)
275
        {
276
            broadcastLock_ = true;
277
        }
278
        
279
        // on the server-side, we want to throttle network messages, so only
280
        // allow broadcasting of messages if a threshold amount of time has
281
        // passed:
282
        else if (osg::Timer::instance()->delta_s(lastUpdate_,tick) > maxUpdateDelta)
283
        {
284
            broadcastLock_ = false;
285
            lastUpdate_ = tick;
286
        }
287
        else 
288
        {
289
            broadcastLock_ = true;
290
        }
291
    }
292
    
293
     
294
    // We update state in the following situations:
295
    // 1) this is a server (always needs to be up-to-date for interaction)
296
    // 2) this is a client and the node is set to client-side computation.
297
    if (spinApp::Instance().getContext()->isServer() || (!spinApp::Instance().getContext()->isServer() && (computationMode_==CLIENT_SIDE)))
298
    {
299

    
300
        // Now we need to update translation/orientation based on the current
301
        // velocity/spin values. We find out how many seconds passed since the
302
        // last time this was called, and move by velocity_*dt (ie, m/s) and 
303
        // rotate by spin_*dt (ie, deg/sec)
304

    
305
        float dt = osg::Timer::instance()->delta_s(lastTick_,tick);
306

    
307

    
308
        if (motion_.valid())
309
        {
310
            motion_->update(dt);
311
            float motionIndex = motion_->getValue();
312
            osg::Vec3 newPos = motionStart_ + ((motionEnd_-motionStart_) * motionIndex);
313
            this->setTranslation( newPos.x(), newPos.y(), newPos.z() );
314
            
315
            if (motion_->getTime() >= motion_->getDuration())
316
            {
317
                BROADCAST(this, "sss", "event", "translateTo", "done");
318
                motion_ = NULL;
319
            }
320
        }
321

    
322

    
323
        if (velocity_.length() > EPSILON)
324
        {
325
            if (this->velocityMode_==GroupNode::TRANSLATE)
326
            {
327
                this->translate( velocity_.x()*dt, velocity_.y()*dt, velocity_.z()*dt );
328
            }
329
            else
330
            {
331
                this->move( velocity_.x()*dt, velocity_.y()*dt, velocity_.z()*dt );
332
            }
333
            if (damping_ > EPSILON)
334
            {
335
                double dv = 1 - (damping_*dt);
336
                this->setVelocity( velocity_.x()*dv, velocity_.y()*dv, velocity_.z()*dv );
337
            }
338
        }
339
        else velocity_ = osg::Vec3(0,0,0);
340

    
341
        if (spin_.length() > EPSILON)
342
        {
343
            if (this->velocityMode_==GroupNode::TRANSLATE)
344
                    this->rotate( spin_.x()*dt, spin_.y()*dt, spin_.z()*dt );
345
            else
346
                    this->addRotation( spin_.x()*dt, spin_.y()*dt, spin_.z()*dt );
347

    
348
            if (damping_ > EPSILON)
349
            {
350
                double ds = 1 - (damping_*dt);
351
                this->setSpin( spin_.x()*ds, spin_.y()*ds, spin_.z()*ds );
352
            }
353
        }
354
        else spin_ = osg::Vec3(0,0,0);
355
    }
356
    
357
    lastTick_ = tick;
358
    broadcastLock_ = false;
359
}
360

    
361
void GroupNode::updateNodePath(bool updateChildren)
362
{
363
    // get node path from parent class, but with the false flag so that children
364
    // are not updated yet:
365
        ReferencedNode::updateNodePath(false);
366

    
367
    currentNodePath.push_back(mainTransform_.get());    
368
    currentNodePath.push_back(clipNode_.get());
369
    
370
    // now update NodePaths for all children:
371
    if (updateChildren) updateChildNodePaths();
372
}
373

    
374
// *****************************************************************************
375

    
376
void GroupNode::mouseEvent (int /*event*/, int keyMask, int buttonMask, float x, float y)
377
{
378
    // TODO
379
    BROADCAST(this, "siiiff", "mouseEvent", keyMask, buttonMask, x, y);
380
}
381

    
382
void GroupNode::event (int event, const char* userString, float eData1, float eData2, float x, float y, float z)
383
{
384
    osg::ref_ptr<UserNode> user = dynamic_cast<UserNode*>(sceneManager->getNode(userString));
385
    if (!user.valid()) return;
386

    
387
    if (interactionMode_==DRAG || interactionMode_==THROW)
388
    {
389
        switch(event)
390
        {
391
            case(osgGA::GUIEventAdapter::PUSH):
392

    
393
                if (!this->owner_.valid())
394
                {
395
                    this->setVelocity(0,0,0);
396
                    this->setSpin(0,0,0);
397
                    trajectory_.clear();
398
                }
399

    
400
                break;
401

    
402
            case(osgGA::GUIEventAdapter::RELEASE):
403

    
404
                if (this->owner_ == user)
405
                {
406
                    if (interactionMode_==THROW)
407
                    {
408
                        // Take average of stored motion vectors, and set velocity in
409
                        // that direction. Note: damping_ should be set so that node
410
                        // gradually stops:
411

    
412
                        double currentTime = osg::Timer::instance()->time_m();
413

    
414
                        osg::Vec3 vel = osg::Vec3(0,0,0);
415
                        int count = 0;
416

    
417
                        std::vector<osg::Vec4>::iterator it;
418
                        for (it=trajectory_.begin(); it!=trajectory_.end(); ++it)
419
                        {
420
                            if (currentTime - (*it).w() < 500)
421
                            {
422
                                vel += osg::Vec3((*it).x(),(*it).y(),(*it).z());
423
                                count++;
424
                            }
425
                        }
426

    
427
                        if (count)
428
                        {
429
                            vel /= count;
430
                            vel *= 10; // motion vectors were typically small, so scale
431

    
432
                            this->setVelocity(vel.x(), vel.y(), vel.z());
433
                        }
434
                    }
435
                }
436
                break;
437

    
438
            case(osgGA::GUIEventAdapter::DRAG):
439

    
440
                if (this->owner_ == user)
441
                {
442

    
443
                    // if this node is owned by the event's user, then we apply the
444
                    // motion relative to the user's current position/orientation:
445

    
446
                    const osg::Matrix targMatrix = this->getGlobalMatrix();
447
                    const osg::Matrix userMatrix = user->getGlobalMatrix();
448

    
449
                    float distance = (targMatrix.getTrans() - userMatrix.getTrans()).length();
450

    
451
                    // perspective projection:
452
                    //float f = 29.1489;
453
                    float f = 3.9;  // why this number?
454
                    float dx = distance * -eData1 / f;
455
                    float dy = distance * -eData2 / f;
456

    
457
                    osg::Vec3 motionVec = userMatrix.getRotate() * osg::Vec3(dx,0,dy);
458
                    osg::Vec3 newPos = getTranslation() + motionVec;
459
                    this->setTranslation(newPos.x(), newPos.y(), newPos.z());
460

    
461
                    // save last N motion vectors, so we can setVelocity on RELEASE:
462
                    // (note, we use a vec4, and store a time in ms as the 4th value
463
                    trajectory_.insert(trajectory_.begin(), osg::Vec4(motionVec, osg::Timer::instance()->time_m()));
464
                    if (trajectory_.size() > 5) {
465
                        trajectory_.pop_back();
466
                    }
467
                }
468

    
469
                break;
470
        }
471

    
472
        // broadcast event
473
        // NO! can't do this, because we will duplicate move/rotate/etc actions
474

    
475
    }
476

    
477
    else if (interactionMode_==DRAW)
478
    {
479
        if ((event==osgGA::GUIEventAdapter::PUSH) || (event==osgGA::GUIEventAdapter::RELEASE))
480
        {
481
            if ((int)eData2==osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON) drawMod_ = 1;
482
            else if ((int)eData2==osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON) drawMod_ = 2;
483
            else drawMod_ = 0;
484

    
485
            BROADCAST(this, "ssifff", "draw", userString, drawMod_, x, y, z);
486
        }
487
        else if (event==osgGA::GUIEventAdapter::DRAG)
488
        {
489
            BROADCAST(this, "ssifff", "draw", userString, drawMod_, x, y, z);
490
        }
491
    }
492

    
493
    // after all is done, we update ownership (if this was a PUSH or RELEASE)
494
    if (interactionMode_==SELECT || interactionMode_==DRAG || interactionMode_==THROW)
495
    {
496
        switch(event)
497
        {
498
            case(osgGA::GUIEventAdapter::PUSH):
499

    
500
                // on push, we check if the current node is owned by anyone,
501
                // and if not, we give ownnerhip to the event's user:
502
                if (!this->owner_.valid())
503
                {
504
                    this->owner_ = user.get();
505
                    //std::cout << "setting owner of " << id->s_name << " to " << owner_->id->s_name << std::endl;
506
                    BROADCAST(this, "ss", "setOwner", owner_->id->s_name);
507
                    BROADCAST(this, "ssi", "select", userString, 1);
508
                }
509
                break;
510

    
511
            case(osgGA::GUIEventAdapter::RELEASE):
512

    
513
                // on release, the user gives up ownership of the node (assuming
514
                // that he had ownership in the first place):
515
                if (this->owner_ == user)
516
                {
517
                    //std::cout << "setting owner of " << id->s_name << " to NULL" << std::endl;
518
                    this->owner_ = NULL;
519
                    BROADCAST(this, "ss", "setOwner", "NULL");
520
                    BROADCAST(this, "ssi", "select", userString, 0);
521
               }
522
                break;
523
            case(osgGA::GUIEventAdapter::DOUBLECLICK):
524
                BROADCAST(this, "ss", "doubleclick", userString);
525
                break;
526
        }
527

    
528
    }
529
}
530

    
531
void GroupNode::setLock(const char* userString, int lock)
532
{
533
    osg::ref_ptr<UserNode> user = dynamic_cast<UserNode*>(sceneManager->getNode(userString));
534
    if (!user.valid()) return;
535

    
536
    if (lock)
537
    {
538
        if (owner_.valid())
539
        {
540
            this->owner_ = user.get();
541
            //std::cout << "setting owner of " << id->s_name << " to " << owner_->id->s_name << std::endl;
542
            BROADCAST(this, "ss", "setOwner", owner_->id->s_name);
543
        }
544
    }
545
    else
546
    {
547
        if (this->owner_ == user)
548
        {
549
            //std::cout << "setting owner of " << id->s_name << " to NULL" << std::endl;
550
            this->owner_ = NULL;
551
            BROADCAST(this, "ss", "setOwner", "NULL");
552
        }
553
    }
554
}
555

    
556
void GroupNode::debug()
557
{
558
    ReferencedNode::debug();
559

    
560
    std::cout << "   Manipulator: " << manipulatorType_ << std::endl;
561
    if (owner_.valid()) std::cout << "   owner: " << owner_->id->s_name << std::endl;
562
    else std::cout << "   owner: NULL" << std::endl;
563
    
564
    std::cout << "   mainX pos: " << stringify(this->getTranslation()) << std::endl;
565
    std::cout << "   mainX rot: " << stringify(this->getOrientationQuat()) << std::endl;
566
    std::cout << "   mainX scl: " << stringify(this->getScale()) << std::endl;
567
}
568

    
569

    
570
// -----------------------------------------------------------------------------
571

    
572
void GroupNode::setComputationMode (ComputationMode mode)
573
{
574

    
575
    if (this->computationMode_ != mode)
576
    {
577
        this->computationMode_ = mode;
578
        lastUpdate_ = lastTick_;
579
        BROADCAST(this, "si", "setComputationMode", getComputationMode());
580
    }
581
}
582

    
583
void GroupNode::setReportMode (globalsReportMode mode)
584
{
585
    if (this->reportMode_ != mode)
586
    {
587
        this->reportMode_ = mode;
588
        BROADCAST(this, "si", "setReportMode", getReportMode());
589
    }
590
}
591

    
592
void GroupNode::setInteractionMode (InteractionMode mode)
593
{
594
    if (this->interactionMode_ != mode)
595
    {
596

    
597
        // if this node previously had an owner, it will be lost with the change
598
        // of mode
599
        if (this->owner_.valid())
600
        {
601
            this->owner_ = NULL;
602
            BROADCAST(this, "ss", "owner", "NULL");
603
        } else if (interactionMode_ == DRAW)
604
        {
605
            BROADCAST(this, "ssifff", "draw", "NULL", 0, 0.0f, 0.0f, 0.0f);
606
        }
607

    
608
        this->interactionMode_ = mode;
609

    
610

    
611

    
612
        // set the node mask so that an intersection visitor will only test
613
        // for interactive nodes (note: nodemask info in spinUtil.h)
614
        if ((int)interactionMode_ > 0)
615
            this->setNodeMask(INTERACTIVE_NODE_MASK);
616
        else
617
            this->setNodeMask(GEOMETRIC_NODE_MASK);
618

    
619
        BROADCAST(this, "si", "setInteractionMode", getInteractionMode());
620
        
621
        // now we need to travel up the scenegraph and set all parents to have
622
        // an InteractionMode of PASSTHRU, otherwise an intersection traversal
623
        // will never find this node:
624
        GroupNode *parent = dynamic_cast<GroupNode*>(this->getParentNode());
625
        while (parent)
626
        {
627
            if (!parent->getInteractionMode())
628
            {
629
                parent->setInteractionMode(PASSTHRU);
630
                continue; // rest of parents will be recursively called
631
            }
632
            else parent = dynamic_cast<GroupNode*>(parent->getParentNode());
633
        }
634
        
635
    }
636
}
637

    
638
// -----------------------------------------------------------------------------
639

    
640
void GroupNode::setStateSetFromFile(const char* filename)
641
{
642
        osg::ref_ptr<ReferencedStateSet> ss = sceneManager->createStateSet(filename);
643
        if (ss.valid())
644
        {
645
                if (ss->id == stateset_) return; // we're already using that stateset
646
                stateset_ = ss->id;
647
                updateStateSet();
648
                BROADCAST(this, "ss", "setStateSet", getStateSetSymbol()->s_name);
649
        }
650
}
651

    
652
void GroupNode::setStateSet (const char* s)
653
{
654
        if (gensym(s)==stateset_) return;
655

    
656
        osg::ref_ptr<ReferencedStateSet> ss = sceneManager->getStateSet(s);
657
        if (ss.valid())
658
        {
659
                stateset_ = ss->id;
660
                updateStateSet();
661
                BROADCAST(this, "ss", "setStateSet", getStateSetSymbol()->s_name);
662
        }
663
}
664

    
665
void GroupNode::updateStateSet()
666
{
667
        osg::ref_ptr<ReferencedStateSet> ss = dynamic_cast<ReferencedStateSet*>(stateset_->s_thing);
668
        if (mainTransform_.valid() && ss.valid()) mainTransform_->setStateSet( ss.get() );
669
}
670

    
671

    
672

    
673
// -----------------------------------------------------------------------------
674

    
675
void GroupNode::setClipping(float x, float y, float z)
676
{
677
    clipping_ = osg::Vec3d(x,y,z);
678

    
679
    // remove existing clipping planes:
680
    while (clipNode_->getNumClipPlanes()) clipNode_->removeClipPlane((unsigned int)0);
681

    
682
    // add this one:
683
    if ( (clipping_.x()>0) && (clipping_.y()>0) && (clipping_.z()>0) )
684
    {
685
        osg::BoundingBox bb(-clipping_.x(),-clipping_.y(),-clipping_.z(),clipping_.x(),clipping_.y(),clipping_.z());
686
        clipNode_->createClipBox(bb);
687
    }
688

    
689
    BROADCAST(this, "sfff", "setClipping", clipping_.x(), clipping_.y(), clipping_.z());
690
}
691

    
692
void GroupNode::updateQuat()
693
{
694
    quat_ = osg::Quat( osg::DegreesToRadians(orientation_.x()), osg::Vec3d(1,0,0),
695
                       osg::DegreesToRadians(orientation_.y()), osg::Vec3d(0,1,0), 
696
                       osg::DegreesToRadians(orientation_.z()), osg::Vec3d(0,0,1));
697
            
698
    // fix numerical imprecision:
699
    float epsilon = 0.0000001;
700
    if (fabs(quat_.x())<epsilon) quat_ = osg::Quat(0.0,quat_.y(),quat_.z(),quat_.w());
701
    if (fabs(quat_.y())<epsilon) quat_ = osg::Quat(quat_.x(),0.0,quat_.z(),quat_.w());
702
    if (fabs(quat_.z())<epsilon) quat_ = osg::Quat(quat_.x(),quat_.y(),0.0,quat_.w());
703
    if (fabs(quat_.w())<epsilon) quat_ = osg::Quat(quat_.x(),quat_.y(),quat_.z(),0.0);
704

    
705
    updateMatrix();
706
}
707

    
708
void GroupNode::updateMatrix()
709
{
710
    osg::Matrix matrix;
711
    /*
712
    // from osg::PositionAttitudeTransform (RELATIVE_RF)
713
    matrix.preMultTranslate(translation_);
714
    matrix.preMultRotate(quat_);
715
    matrix.preMultScale(scale_);
716
    */
717
    
718
    // from osg::PositionAttitudeTransform (ABSOLUTE)
719
    matrix.makeRotate(quat_);
720
    matrix.postMultTranslate(translation_);
721
    matrix.preMultScale(scale_);
722
    
723
    /*
724
    matrix = osg::Matrix::translate(translation_)
725
            * osg::Matrix::scale(scale_)
726
            * osg::Matrix::rotate(quat_);
727
    */
728
    mainTransform_->setMatrix(matrix);
729
 
730
}
731

    
732
void GroupNode::updateDraggerMatrix()
733
{
734
    osg::Matrix matrix;
735
    matrix.preMultTranslate(translation_);
736
    matrix.preMultScale(dragger_->getMatrix().getScale());
737
    dragger_->setMatrix(matrix);
738
}
739

    
740
void GroupNode::setTranslation (float x, float y, float z)
741
{
742
    osg::Vec3 newTranslation = osg::Vec3(x,y,z);
743

    
744
    if (newTranslation != translation_)
745
    {
746
        translation_ = newTranslation;
747
        updateMatrix();
748

    
749
        //if (dragger_.valid()) updateDraggerMatrix();
750
        
751
        if (!broadcastLock_)
752
            BROADCAST(this, "sfff", "setTranslation", x, y, z);
753
    }
754
    
755
    // if we've moved and the orientationMode requires us to point to a target
756
    // node or the origin, we should apply that orientation change too:
757
    if (orientationMode_!=NORMAL)
758
    {
759
        applyOrientationMode();
760
    }
761
}
762

    
763
void GroupNode::setOrientationQuat (float x, float y, float z, float w)
764
{
765
    if ((orientationMode_==NORMAL)||(!spinApp::Instance().getContext()->isServer()))
766
    {
767
        osg::Quat newQuat = osg::Quat(x,y,z,w);
768

    
769
        if (newQuat != quat_)
770
        {
771
            quat_ = newQuat;
772
            updateMatrix();
773
            orientation_ = Vec3inDegrees(QuatToEuler(newQuat));
774

    
775
            if (!broadcastLock_)
776
                BROADCAST(this, "sffff", "setOrientationQuat", x, y, z, w);
777
        }
778
    }
779
}
780

    
781
void GroupNode::setOrientation (float p, float r, float y)
782
{
783
    // Only allow orientation changes if OrientationMode is NORMAL, or if this
784
    // is a client. The server cannot apply orientations for other modes, but
785
    // clients do need to be notified if the server sends a constrained 
786
    // orientation
787
    if ((orientationMode_==NORMAL)||(!spinApp::Instance().getContext()->isServer()))
788
    {
789
        osg::Vec3 newOrientation = osg::Vec3(p, r, y);
790

    
791
        if (newOrientation != orientation_)
792
        {
793
            orientation_ = newOrientation;
794
            updateQuat();
795
            if (!broadcastLock_)
796
                BROADCAST(this, "sfff", "setOrientation", p, r, y);
797
        }
798
    }
799
}
800

    
801
void GroupNode::setOrientationMode (OrientationMode m)
802
{
803
        this->orientationMode_ = m;
804
        BROADCAST(this, "si", "setOrientationMode", (int)orientationMode_);
805
    if (orientationMode_!=NORMAL)
806
    {
807
        applyOrientationMode();
808
    }
809
}
810

    
811
void GroupNode::setOrientationTarget (const char* target)
812
{
813
    orientationTarget_ = gensym(target);
814
    if (orientationMode_!=NORMAL)
815
    {
816
        applyOrientationMode();
817
    }
818
    BROADCAST(this, "ss", "setOrientationTarget", getOrientationTarget());
819
}
820

    
821
void GroupNode::applyOrientationMode()
822
{
823
    if (!spinApp::Instance().getContext()->isServer()) return;
824

    
825
    osg::Vec3 targetPos;
826
    osg::Matrix thisMatrix = osg::computeLocalToWorld(this->currentNodePath);
827
    
828
    if ((orientationMode_==POINT_TO_TARGET)||(orientationMode_==POINT_TO_TARGET_CENTROID))
829
    {
830
        ReferencedNode *target = dynamic_cast<ReferencedNode*>(orientationTarget_->s_thing);
831
        if (target)
832
        {
833
            // TODO: verify if we really need to recompute the matrices and bound every time.
834
            // Perhaps the matrix is already stored somewhere in certain cases?
835
            
836
            if (orientationMode_==POINT_TO_TARGET_CENTROID)
837
            {
838
                targetPos = target->computeBound().center();
839
            }
840
            else
841
            {
842
                osg::Matrixd targetMatrix = osg::computeLocalToWorld(target->currentNodePath);
843
                targetPos = targetMatrix.getTrans();
844
            }
845
        }
846
    }
847
    else if (orientationMode_==POINT_TO_ORIGIN)
848
    {
849
        targetPos = osg::Vec3(0.0,0.0,0.0);
850
    }
851
    
852
    osg::Vec3 aed = cartesianToSpherical(targetPos - thisMatrix.getTrans());
853
    
854
    // can't do this, because that function only does something for NORMAL mode
855
    //setOrientation(osg::RadiansToDegrees(aed.y()), 0.0, osg::RadiansToDegrees(aed.x()));
856

    
857
    orientation_ = osg::Vec3(osg::RadiansToDegrees(aed.y()), 0.0, osg::RadiansToDegrees(aed.x()));
858
    quat_ = osg::Quat( osg::DegreesToRadians(orientation_.x()), osg::Vec3d(1,0,0),
859
                             osg::DegreesToRadians(orientation_.y()), osg::Vec3d(0,1,0),
860
                             osg::DegreesToRadians(orientation_.z()), osg::Vec3d(0,0,1));
861
    updateMatrix();
862
    
863
    if (!broadcastLock_)
864
        BROADCAST(this, "sfff", "setOrientation", orientation_.x(), orientation_.y(),  orientation_.z());
865
}
866

    
867

    
868
/*
869
void GroupNode::setOrientationQuat (float x, float y, float z, float w)
870
{
871
    osg::Quat = osg::Quat(x,y,z,w);
872
    orientation_ = QuatToEuler(q);
873
    mainTransform_->setAttitude(q);
874

    
875
    BROADCAST(this, "sfff", "setOrientation", _orientatoin.x(), orientation_.y(), orientation_.z());
876
}
877
*/
878

    
879
void GroupNode::setScale (float x, float y, float z)
880
{
881
    osg::Vec3 newScale = osg::Vec3(x,y,z);
882

    
883
    if (newScale != scale_)
884
    {
885
        scale_ = newScale;
886
        updateMatrix();
887
        
888
        if (!broadcastLock_)
889
            BROADCAST(this, "sfff", "setScale", x, y, z);
890
    }
891
}
892

    
893
void GroupNode::setVelocity (float dx, float dy, float dz)
894
{
895
    osg::Vec3 newVelocity = osg::Vec3(dx,dy,dz);
896

    
897
    if (newVelocity != velocity_)
898
    {
899
        velocity_ = newVelocity;
900
        if (!broadcastLock_)
901
            BROADCAST(this, "sfff", "setVelocity", dx, dy, dz);
902
            
903
        // if this is an object that was updated on the client side, and the
904
        // velocity is stopped, let's do a server-side push of the latest state
905
        // to ensure the client state is perfectly synchronized:
906
        if ((velocity_.length()==0) && (computationMode_==CLIENT_SIDE))
907
        {
908
            BROADCAST(this, "sfff", "setTranslation", translation_.x(), translation_.y(), translation_.z());
909
            BROADCAST(this, "sfff", "setOrientation", orientation_.x(), orientation_.y(), orientation_.z());
910
        }
911
    }
912
}
913

    
914
void GroupNode::setVelocityMode (velocityMode mode)
915
{
916
    if (this->velocityMode_ != (int)mode)
917
    {
918
        this->velocityMode_ = mode;
919
        BROADCAST(this, "si", "setVelocityMode", (int) this->velocityMode_);
920
    }
921
}
922

    
923
void GroupNode::setSpin (float dp, float dr, float dy)
924
{
925
    osg::Vec3 newSpin = osg::Vec3(dp,dr,dy);
926

    
927
    if (newSpin != spin_)
928
    {
929
        spin_ = newSpin;
930
        if (!broadcastLock_)
931
            BROADCAST(this, "sfff", "setSpin", dp, dr, dy);
932
            
933
        // if this is an object that was updated on the client side, and the
934
        // spin is stopped, let's do a server-side push of the latest state
935
        // to ensure the client state is perfectly synchronized:
936
        if ((spin_.length()==0) && (computationMode_==CLIENT_SIDE))
937
        {
938
            BROADCAST(this, "sfff", "setTranslation", translation_.x(), translation_.y(), translation_.z());
939
            BROADCAST(this, "sfff", "setOrientation", orientation_.x(), orientation_.y(), orientation_.z());
940
        }
941

    
942
    }
943
}
944

    
945
void GroupNode::setDamping (float d)
946
{
947
    if (d != damping_)
948
    {
949
        damping_ = d;
950
        BROADCAST(this, "sf", "setDamping", damping_);
951
    }
952
}
953

    
954
void GroupNode::translate (float x, float y, float z)
955
{
956
    // simple move relative to the parent
957
    osg::Vec3 newPos = getTranslation() + osg::Vec3(x,y,z);
958
    setTranslation(newPos.x(), newPos.y(), newPos.z());
959
}
960

    
961
void GroupNode::move (float x, float y, float z)
962
{
963
    // take the orientation into account, and move along that vector:
964
    osg::Vec3 newPos = getTranslation() + ( getOrientationQuat() * osg::Vec3(x,y,z) );
965
    setTranslation(newPos.x(), newPos.y(), newPos.z());
966
}
967

    
968
void GroupNode::rotate (float dPitch, float dRoll, float dYaw)
969
{
970
    this->setOrientation(orientation_.x()+dPitch, orientation_.y()+dRoll, orientation_.z()+dYaw);
971
}
972

    
973
void GroupNode::addRotation (float dPitch, float dRoll, float dYaw)
974
{
975
    osg::Quat newQuat = EulerToQuat(dPitch,dRoll,dYaw).inverse() * getOrientationQuat();
976
    this->setOrientationQuat(newQuat.x(), newQuat.y(), newQuat.z(), newQuat.w());
977
}
978

    
979
// *****************************************************************************
980

    
981

    
982

    
983
void GroupNode::translateTo (float x, float y, float z, float duration, const char *motion)
984
{
985

    
986
    if (motion_.valid())
987
    {
988
        motion_ = NULL;
989
    }
990

    
991
    motionStart_ = getTranslation();
992
    motionEnd_ = osg::Vec3(x,y,z);
993
    
994
    std::string motionType = std::string(motion);
995
    
996
    osgAnimation::Motion::TimeBehaviour tb = osgAnimation::Motion::CLAMP;
997
    float init = 0.0f;
998
    float d = 1.0f;
999

    
1000
    if (!motionType.compare("OutQuadMotion"))
1001
        motion_ = new osgAnimation::OutQuadMotion(init, duration, d, tb);
1002
    else if (!motionType.compare("InQuadMotion"))
1003
        motion_ = new osgAnimation::InQuadMotion(init, duration, d, tb);
1004
    else if (!motionType.compare("InOutQuadMotion"))
1005
        motion_ = new osgAnimation::InOutQuadMotion(init, duration, d, tb);
1006
    else if (!motionType.compare("OutCubicMotion"))
1007
        motion_ = new osgAnimation::OutCubicMotion(init, duration, d, tb);
1008
    else if (!motionType.compare("InCubicMotion"))
1009
        motion_ = new osgAnimation::InCubicMotion(init, duration, d, tb);
1010
    else if (!motionType.compare("InOutCubicMotion"))
1011
        motion_ = new osgAnimation::InOutCubicMotion(init, duration, d, tb);
1012
    else if (!motionType.compare("OutQuartMotion"))
1013
        motion_ = new osgAnimation::OutQuartMotion(init, duration, d, tb);
1014
    else if (!motionType.compare("InQuartMotion"))
1015
        motion_ = new osgAnimation::InQuartMotion(init, duration, d, tb);
1016
    else if (!motionType.compare("InOutQuartMotion"))
1017
        motion_ = new osgAnimation::InOutQuartMotion(init, duration, d, tb);
1018
    else if (!motionType.compare("OutBounceMotion"))
1019
        motion_ = new osgAnimation::OutBounceMotion(init, duration, d, tb);
1020
    else if (!motionType.compare("InBounceMotion"))
1021
        motion_ = new osgAnimation::InBounceMotion(init, duration, d, tb);
1022
    else if (!motionType.compare("InOutBounceMotion"))
1023
        motion_ = new osgAnimation::InOutBounceMotion(init, duration, d, tb);
1024
    else if (!motionType.compare("OutElasticMotion"))
1025
        motion_ = new osgAnimation::OutElasticMotion(init, duration, d, tb);
1026
    else if (!motionType.compare("InElasticMotion"))
1027
        motion_ = new osgAnimation::InElasticMotion(init, duration, d, tb);
1028
    else if (!motionType.compare("InOutElasticMotion"))
1029
        motion_ = new osgAnimation::InOutElasticMotion(init, duration, d, tb);
1030
    else if (!motionType.compare("OutSineMotion"))
1031
        motion_ = new osgAnimation::OutSineMotion(init, duration, d, tb);
1032
    else if (!motionType.compare("InSineMotion"))
1033
        motion_ = new osgAnimation::InSineMotion(init, duration, d, tb);
1034
    else if (!motionType.compare("InOutSineMotion"))
1035
        motion_ = new osgAnimation::InOutSineMotion(init, duration, d, tb);
1036
    else if (!motionType.compare("OutBackMotion"))
1037
        motion_ = new osgAnimation::OutBackMotion(init, duration, d, tb);
1038
    else if (!motionType.compare("InBackMotion"))
1039
        motion_ = new osgAnimation::InBackMotion(init, duration, d, tb);
1040
    else if (!motionType.compare("InOutBackMotion"))
1041
        motion_ = new osgAnimation::InOutBackMotion(init, duration, d, tb);
1042
    else if (!motionType.compare("OutCircMotion"))
1043
        motion_ = new osgAnimation::OutCircMotion(init, duration, d, tb);
1044
    else if (!motionType.compare("InCircMotion"))
1045
        motion_ = new osgAnimation::InCircMotion(init, duration, d, tb);
1046
    else if (!motionType.compare("InOutCircMotion"))
1047
        motion_ = new osgAnimation::InOutCircMotion(init, duration, d, tb);
1048
    else if (!motionType.compare("OutExpoMotion"))
1049
        motion_ = new osgAnimation::OutExpoMotion(init, duration, d, tb);
1050
    else if (!motionType.compare("InExpoMotion"))
1051
        motion_ = new osgAnimation::InExpoMotion(init, duration, d, tb);
1052
    else if (!motionType.compare("InOutExpoMotion"))
1053
        motion_ = new osgAnimation::InOutExpoMotion(init, duration, d, tb);
1054
    else
1055
        motion_ = new osgAnimation::LinearMotion(init, duration, d, tb);
1056

    
1057
    if (computationMode_==CLIENT_SIDE)
1058
    {
1059
        BROADCAST(this, "sffffs", "translateTo", x, y, z, duration, motion);
1060
    }
1061
}
1062

    
1063
// *****************************************************************************
1064

    
1065
    
1066
osgManipulator::Dragger* createDragger(const std::string& name, float size, osg::Vec3 offset, osg::Vec3 scale)
1067
{
1068
    osgManipulator::Dragger* dragger = 0;
1069
    float scaleFactor = 1.0;
1070
    if ("TabPlaneDragger" == name)
1071
    {
1072
        osgManipulator::TabPlaneDragger* d = new osgManipulator::TabPlaneDragger();
1073
        d->setupDefaultGeometry();
1074
        dragger = d;
1075
        scaleFactor = 2.0;
1076
    }
1077
    else if ("TabPlaneTrackballDragger" == name)
1078
    {
1079
        osgManipulator::TabPlaneTrackballDragger* d = new osgManipulator::TabPlaneTrackballDragger();
1080
        d->setupDefaultGeometry();
1081
        dragger = d;
1082
        scaleFactor = 2.5;
1083
    }
1084
    else if ("TabBoxTrackballDragger" == name)
1085
    {
1086
        osgManipulator::TabBoxTrackballDragger* d = new osgManipulator::TabBoxTrackballDragger();
1087
        d->setupDefaultGeometry();
1088
        dragger = d;
1089
        scaleFactor = 2.5;
1090
    }
1091
    else if ("TrackballDragger" == name)
1092
    {
1093
        //osgManipulator::TrackballDragger* d = new osgManipulator::TrackballDragger();
1094
        osgManipulator::DraggerTrackball* d = new osgManipulator::DraggerTrackball();
1095
        d->setupDefaultGeometry();
1096
        dragger = d;
1097
        scaleFactor = 1.0;
1098
    }
1099
    else if ("Translate1DDragger" == name)
1100
    {
1101
        osgManipulator::Translate1DDragger* d = new osgManipulator::Translate1DDragger();
1102
        d->setupDefaultGeometry();
1103
        dragger = d;
1104
        scaleFactor = 2.0;
1105
    }
1106
    else if ("Translate2DDragger" == name)
1107
    {
1108
        osgManipulator::Translate2DDragger* d = new osgManipulator::Translate2DDragger();
1109
        d->setupDefaultGeometry();
1110
        dragger = d;
1111
        scaleFactor = 2.5;
1112
    }
1113
    else if ("TranslateAxisDragger" == name)
1114
    {
1115
        //osgManipulator::TranslateAxisDragger* d = new osgManipulator::TranslateAxisDragger();
1116
        osgManipulator::DraggerWith3Axes* d = new osgManipulator::DraggerWith3Axes();
1117
        d->setupDefaultGeometry();
1118
        dragger = d;
1119
        scaleFactor = 1.4;
1120
    }
1121
    else if ("TabBoxDragger" == name)
1122
    {
1123
        osgManipulator::TabBoxDragger* d = new osgManipulator::TabBoxDragger();
1124
        d->setupDefaultGeometry();
1125
        dragger = d;
1126
        scaleFactor = 1.25; //1.6;
1127
    }
1128
    
1129
    if (dragger)
1130
    {
1131
        dragger->setName(name);
1132
        
1133
        dragger->setMatrix(osg::Matrix::scale(size*scaleFactor, size*scaleFactor, size*scaleFactor) * osg::Matrix::translate(offset));
1134
        //dragger->setMatrix(osg::Matrix::scale(scale.x()*scaleFactor, scale.y()*scaleFactor, scale.z()*scaleFactor) * osg::Matrix::translate(offset));
1135
        
1136
        // turn off lighing on dragger
1137
        osg::StateSet *ss = dragger->getOrCreateStateSet();
1138
        ss->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
1139
    }
1140
    
1141
    return dragger;
1142
}
1143

    
1144
void GroupNode::setManipulatorMatrix(float a00, float a01, float a02, float a03,
1145
                                     float a10, float a11, float a12, float a13,
1146
                                     float a20, float a21, float a22, float a23,
1147
                                     float a30, float a31, float a32, float a33)
1148
{
1149
    
1150
    osg::Matrix m = osg::Matrix(a00,a01,a02,a03,
1151
                                a10,a11,a12,a13,
1152
                                a20,a21,a22,a23,
1153
                                a30,a31,a32,a33);
1154

    
1155
    mainTransform_->setMatrix(m);
1156
    
1157
    /*
1158
    // Can't just broadcast the matrix, because it won't call the clients'
1159
    // setTranslation, setOrientation and setScale method... thus, collisions
1160
    // and any other constraints won't be handled (important if this instance
1161
    // overrides those methods and does extra stuff).
1162
    BROADCAST(this, "sffffffffffffffff", "setManipulatorMatrix",
1163
                    a00,a01,a02,a03,
1164
                    a10,a11,a12,a13,
1165
                    a20,a21,a22,a23,
1166
                    a30,a31,a32,a33);
1167
    */
1168
    
1169
    
1170
    // So we compute the independent translation, rotation, & scale components
1171
    // from the matrix:
1172
    
1173
    osg::Vec3 t;
1174
    osg::Quat q;
1175
    osg::Vec3 s;
1176
    
1177
    // IMPORTANT: getRotate() doesn't take scale into account! watch out!
1178
    /*
1179
    t = m.getTrans();
1180
    q = m.getRotate();
1181
    s = m.getScale();
1182
    */
1183
    
1184
    // Use matrix::decompose() instead (and note: so is 'scale orientation')
1185
    osg::Quat so;
1186
    m.decompose(t, q, s, so);
1187

    
1188
    /*
1189
    std::cout << "decomp trans: " << stringify(t) << std::endl;
1190
    std::cout << "decomp orien: " << stringify(q) << std::endl;
1191
    std::cout << "decomp scale: " << stringify(s) << std::endl;
1192
    std::cout << "decomp scOri: " << stringify(so) << std::endl;
1193
    */
1194

    
1195

    
1196
    // But, we can't call setTranslation etc here because those are assumed to
1197
    // be manual method and cause the dragger matrix to be updated. ie, the 
1198
    // updateDraggerMatrix() method...
1199
    
1200
    this->setTranslation(t.x(), t.y(), t.z());
1201
    this->setOrientationQuat(q.x(), q.y(), q.z(), q.w());
1202
    this->setScale(s.x(), s.y(), s.z());
1203
    
1204
    
1205
    // So... what to do?
1206
    
1207
    /*
1208
    translation_ = t;
1209
    quat_ = q;
1210
    scale_ = s;
1211
    orientation_ = Vec3inDegrees(QuatToEuler(quat_));
1212

    
1213
    
1214
    // can't do this yet, because a message will result!
1215
    if (orientationMode_!=NORMAL)
1216
    {
1217
       // applyOrientationMode();
1218
    }
1219
    
1220
    BROADCAST(this, "sfff", "setTranslation", translation_.x(), translation_.y(), translation_.z());
1221
    BROADCAST(this, "sffff", "setOrientationQuat", quat_.x(), quat_.y(), quat_.z(), quat_.w());
1222
    BROADCAST(this, "sfff", "setScale", scale_.x(), scale_.y(), scale_.z());
1223
    */
1224
    
1225
}
1226
    
1227
void GroupNode::setManipulator(const char *manipulatorType)
1228
{
1229
    //if (this->owner_.valid()) return;
1230
    if (std::string(manipulatorType)==manipulatorType_) return;
1231
    
1232
    manipulatorType_ = std::string(manipulatorType);
1233
    BROADCAST(this, "ss", "setManipulator", getManipulator());
1234

    
1235
    // set the flag, which is checked during the updateCallback, and calls the
1236
    // drawManipulator method below
1237
    manipulatorUpdateFlag_ = true;
1238
}
1239

    
1240
void GroupNode::drawManipulator()
1241
{
1242
    // remove the previously attached dragger (if one exists):
1243
    if (dragger_.valid())
1244
    {
1245
        this->removeChild(dragger_.get());
1246
        dragger_ = 0;
1247
    }
1248

    
1249
    dragger_ = createDragger(manipulatorType_, this->getBound().radius(), this->getTranslation(), this->getScale());
1250
    
1251
    if (dragger_.valid())
1252
    {
1253
        this->addChild(dragger_.get());
1254
        
1255
        dragger_->addDraggerCallback(new DraggerCallback(this));
1256

    
1257
        // we want the dragger to handle it's own events automatically
1258
        dragger_->setHandleEvents(true);
1259
        
1260
        // if we don't set an activation key or mod mask then any mouse click on
1261
        // the dragger will activate it, however if do define either of ActivationModKeyMask or
1262
        // and ActivationKeyEvent then you'll have to press either than mod key or the specified key to
1263
        // be able to activate the dragger when you mouse click on it.  Please note the follow allows
1264
        // activation if either the ctrl key or the 'a' key is pressed and held down.
1265
        dragger_->setActivationModKeyMask(osgGA::GUIEventAdapter::MODKEY_CTRL);
1266
        dragger_->setActivationKeyEvent('a');
1267

    
1268
    }
1269
      
1270
    // note: setAttachmentNode will call updateNodePath
1271
    //this->setAttachmentNode(clipNode_.get());
1272
    //this->updateNodePath();
1273
}
1274

    
1275
    
1276
// *****************************************************************************
1277

    
1278
osg::Matrix GroupNode::getGlobalMatrix()
1279
{
1280
    if (!this->reportMode_)
1281
    {
1282
        // might not be up-to-date, so force an update:
1283
        globalMatrix_ = osg::computeLocalToWorld(this->currentNodePath);
1284
    }
1285

    
1286
    return globalMatrix_;
1287
}
1288

    
1289
osg::Vec3 GroupNode::getCenter() const
1290
{
1291
    const osg::BoundingSphere& bs = this->getBound();
1292
    //osg::BoundingSphere& bs = this->computeBound();
1293
    return bs.center();
1294
}
1295

    
1296

    
1297
bool GroupNode::dumpGlobals(bool forced)
1298
{
1299
    // The "forced" parameter means that we do the dump even if there has been
1300
    // no change. The "forced" flag should NEVER be used in the updateCallback,
1301
    // which occurs very frequently. The stateDump() method will however force
1302
    // an update of the current global parameters
1303

    
1304
    //if (this->_reportGlobals)
1305
    if (this->reportMode_)
1306
    {
1307

    
1308
        // position & rotation: (should we get position from centre of boundingsphere?
1309
        osg::Matrix myMatrix = osg::computeLocalToWorld(this->currentNodePath);
1310
        if ((myMatrix != this->globalMatrix_) || forced)
1311
        {
1312
            this->globalMatrix_ = myMatrix;
1313
            
1314
            //TODO: use decompose!
1315
            //osg::Vec3 myPos = myMatrix.getTrans();
1316
            //osg::Vec3 myRot = Vec3inDegrees(QuatToEuler(myMatrix.getRotate()));
1317

    
1318
            osg::Vec3 myPos;
1319
            osg::Quat myQuat;
1320
            osg::Vec3 myScale;
1321
            osg::Quat myScaleOrientation;
1322
            this->globalMatrix_.decompose(myPos, myQuat, myScale, myScaleOrientation);
1323
            osg::Vec3 myRot = Vec3inDegrees(QuatToEuler(myQuat));
1324
            
1325

    
1326
            BROADCAST(this, "sffffff", "global6DOF", myPos.x(), myPos.y(), myPos.z(), myRot.x(), myRot.y(), myRot.z());
1327

    
1328
            if (this->reportMode_ == GLOBAL_ALL)
1329
            {
1330
                osg::ComputeBoundsVisitor *vst = new osg::ComputeBoundsVisitor;
1331
                this->accept(*vst);
1332
                osg::BoundingBox bb = vst->getBoundingBox();
1333
                osg::Vec3 myScale = osg::Vec3(bb.xMax()-bb.xMin(), bb.yMax()-bb.yMin(), bb.zMax()-bb.zMin());
1334
                if ((myScale != this->_globalScale) || forced)
1335
                {
1336
                    this->_globalScale = myScale;
1337
                    BROADCAST(this, "sfff", "globalScale", _globalScale.x(), _globalScale.y(), _globalScale.z());
1338
                }
1339
            }
1340
        }
1341

    
1342

    
1343
        if (this->reportMode_ == GLOBAL_ALL)
1344
        {
1345
            const osg::BoundingSphere& bs = this->getBound();
1346
            if ((bs.radius() != globalRadius_) || forced)
1347
            {
1348
                globalRadius_ = bs.radius();
1349
                BROADCAST(this, "sf", "globalRadius", globalRadius_);
1350
            }
1351
        }
1352

    
1353
        return true;
1354

    
1355
    }
1356
    return false;
1357
}
1358

    
1359
void GroupNode::stateDump ()
1360
{
1361
    ReferencedNode::stateDump();
1362
    dumpGlobals(true);
1363
}
1364

    
1365
// *****************************************************************************
1366

    
1367

    
1368
std::vector<lo_message> GroupNode::getState () const
1369
{
1370
    // inherit state from base class
1371
    std::vector<lo_message> ret = ReferencedNode::getState();
1372

    
1373
    lo_message msg;
1374
    osg::Vec3 v;
1375

    
1376
        msg = lo_message_new();
1377
        lo_message_add(msg,  "ss", "setStateSet", getStateSetSymbol()->s_name);
1378
        ret.push_back(msg);
1379

    
1380
    msg = lo_message_new();
1381
    lo_message_add(msg, "si", "setComputationMode", getComputationMode());
1382
    ret.push_back(msg);
1383

    
1384
    msg = lo_message_new();
1385
    lo_message_add(msg, "si", "setReportMode", getReportMode());
1386
    ret.push_back(msg);
1387

    
1388
    msg = lo_message_new();
1389
    lo_message_add(msg, "si", "setInteractionMode", getInteractionMode());
1390
    ret.push_back(msg);
1391

    
1392
    msg = lo_message_new();
1393
    v = getOrientation();
1394
    lo_message_add(msg, "si", "setOrientationMode", getOrientationMode());
1395
    ret.push_back(msg);
1396
    
1397
    msg = lo_message_new();
1398
    v = getOrientation();
1399
    lo_message_add(msg, "ss", "setOrientationTarget", getOrientationTarget());
1400
    ret.push_back(msg);
1401

    
1402
    msg = lo_message_new();
1403
    lo_message_add(msg, "sfff", "setClipping", clipping_.x(), clipping_.y(), clipping_.z());
1404
    ret.push_back(msg);
1405

    
1406
    msg = lo_message_new();
1407
    v = getTranslation();
1408
    lo_message_add(msg, "sfff", "setTranslation", v.x(), v.y(), v.z());
1409
    ret.push_back(msg);
1410

    
1411
    msg = lo_message_new();
1412
    v = getOrientation();
1413
    lo_message_add(msg, "sfff", "setOrientation", v.x(), v.y(), v.z());
1414
    ret.push_back(msg);
1415

    
1416
    msg = lo_message_new();
1417
    v = getScale();
1418
    lo_message_add(msg, "sfff", "setScale", v.x(), v.y(), v.z());
1419
    ret.push_back(msg);
1420

    
1421
    msg = lo_message_new();
1422
    v = getVelocity();
1423
    lo_message_add(msg, "sfff", "setVelocity", v.x(), v.y(), v.z());
1424
    ret.push_back(msg);
1425

    
1426
    msg = lo_message_new();
1427
    lo_message_add(msg, "si", "setVelocityMode", getVelocityMode());
1428
    ret.push_back(msg);
1429

    
1430
    msg = lo_message_new();
1431
    lo_message_add(msg, "sf", "setDamping", getDamping());
1432
    ret.push_back(msg);
1433

    
1434

    
1435
    //lo_message_free(msg);
1436

    
1437
    return ret;
1438
}
1439

    
1440
} // end of namespace spin
1441

    
1442