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 |
|
