1 /* 2 ----------------------------------------------------------------------------- 3 This source file is part of OGRE 4 (Object-oriented Graphics Rendering Engine) 5 For the latest info, see http://www.ogre3d.org/ 6 7 Copyright (c) 2000-2013 Torus Knot Software Ltd 8 9 Permission is hereby granted, free of charge, to any person obtaining a copy 10 of this software and associated documentation files (the "Software"), to deal 11 in the Software without restriction, including without limitation the rights 12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 copies of the Software, and to permit persons to whom the Software is 14 furnished to do so, subject to the following conditions: 15 16 The above copyright notice and this permission notice shall be included in 17 all copies or substantial portions of the Software. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 THE SOFTWARE. 26 ----------------------------------------------------------------------------- 27 */ 28 #include "OgreStableHeaders.h" 29 #include "OgreRibbonTrail.h" 30 #include "OgreMath.h" 31 #include "OgreException.h" 32 #include "OgreSceneNode.h" 33 #include "OgreStringConverter.h" 34 35 namespace Ogre 36 { 37 namespace 38 { 39 /** Controller value for pass frame time to RibbonTrail 40 */ 41 class _OgrePrivate TimeControllerValue : public ControllerValue<Real> 42 { 43 protected: 44 RibbonTrail* mTrail; 45 public: TimeControllerValue(RibbonTrail * r)46 TimeControllerValue(RibbonTrail* r) { mTrail = r; } 47 getValue(void) const48 Real getValue(void) const { return 0; }// not a source setValue(Real value)49 void setValue(Real value) { mTrail->_timeUpdate(value); } 50 }; 51 } 52 //----------------------------------------------------------------------- 53 //----------------------------------------------------------------------- RibbonTrail(const String & name,size_t maxElements,size_t numberOfChains,bool useTextureCoords,bool useColours)54 RibbonTrail::RibbonTrail(const String& name, size_t maxElements, 55 size_t numberOfChains, bool useTextureCoords, bool useColours) 56 :BillboardChain(name, maxElements, 0, useTextureCoords, useColours, true), 57 mFadeController(0) 58 { 59 setTrailLength(100); 60 setNumberOfChains(numberOfChains); 61 mTimeControllerValue = ControllerValueRealPtr(OGRE_NEW TimeControllerValue(this)); 62 63 // use V as varying texture coord, so we can use 1D textures to 'smear' 64 setTextureCoordDirection(TCD_V); 65 66 67 } 68 //----------------------------------------------------------------------- ~RibbonTrail()69 RibbonTrail::~RibbonTrail() 70 { 71 // Detach listeners 72 for (NodeList::iterator i = mNodeList.begin(); i != mNodeList.end(); ++i) 73 { 74 (*i)->setListener(0); 75 } 76 77 if (mFadeController) 78 { 79 // destroy controller 80 ControllerManager::getSingleton().destroyController(mFadeController); 81 } 82 83 } 84 //----------------------------------------------------------------------- addNode(Node * n)85 void RibbonTrail::addNode(Node* n) 86 { 87 if (mNodeList.size() == mChainCount) 88 { 89 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 90 mName + " cannot monitor any more nodes, chain count exceeded", 91 "RibbonTrail::addNode"); 92 } 93 if (n->getListener()) 94 { 95 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 96 mName + " cannot monitor node " + n->getName() + " since it already has a listener.", 97 "RibbonTrail::addNode"); 98 } 99 100 // get chain index 101 size_t chainIndex = mFreeChains.back(); 102 mFreeChains.pop_back(); 103 mNodeToChainSegment.push_back(chainIndex); 104 mNodeToSegMap[n] = chainIndex; 105 106 // initialise the chain 107 resetTrail(chainIndex, n); 108 109 mNodeList.push_back(n); 110 n->setListener(this); 111 112 } 113 //----------------------------------------------------------------------- getChainIndexForNode(const Node * n)114 size_t RibbonTrail::getChainIndexForNode(const Node* n) 115 { 116 NodeToChainSegmentMap::const_iterator i = mNodeToSegMap.find(n); 117 if (i == mNodeToSegMap.end()) 118 { 119 OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, 120 "This node is not being tracked", "RibbonTrail::getChainIndexForNode"); 121 } 122 return i->second; 123 } 124 //----------------------------------------------------------------------- removeNode(Node * n)125 void RibbonTrail::removeNode(Node* n) 126 { 127 NodeList::iterator i = std::find(mNodeList.begin(), mNodeList.end(), n); 128 if (i != mNodeList.end()) 129 { 130 // also get matching chain segment 131 size_t index = std::distance(mNodeList.begin(), i); 132 IndexVector::iterator mi = mNodeToChainSegment.begin(); 133 std::advance(mi, index); 134 size_t chainIndex = *mi; 135 BillboardChain::clearChain(chainIndex); 136 // mark as free now 137 mFreeChains.push_back(chainIndex); 138 n->setListener(0); 139 mNodeList.erase(i); 140 mNodeToChainSegment.erase(mi); 141 mNodeToSegMap.erase(mNodeToSegMap.find(n)); 142 143 } 144 } 145 //----------------------------------------------------------------------- 146 RibbonTrail::NodeIterator getNodeIterator(void) const147 RibbonTrail::getNodeIterator(void) const 148 { 149 return NodeIterator(mNodeList.begin(), mNodeList.end()); 150 } 151 //----------------------------------------------------------------------- setTrailLength(Real len)152 void RibbonTrail::setTrailLength(Real len) 153 { 154 mTrailLength = len; 155 mElemLength = mTrailLength / mMaxElementsPerChain; 156 mSquaredElemLength = mElemLength * mElemLength; 157 } 158 //----------------------------------------------------------------------- setMaxChainElements(size_t maxElements)159 void RibbonTrail::setMaxChainElements(size_t maxElements) 160 { 161 BillboardChain::setMaxChainElements(maxElements); 162 mElemLength = mTrailLength / mMaxElementsPerChain; 163 mSquaredElemLength = mElemLength * mElemLength; 164 165 resetAllTrails(); 166 } 167 //----------------------------------------------------------------------- setNumberOfChains(size_t numChains)168 void RibbonTrail::setNumberOfChains(size_t numChains) 169 { 170 if (numChains < mNodeList.size()) 171 { 172 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 173 "Can't shrink the number of chains less than number of tracking nodes", 174 "RibbonTrail::setNumberOfChains"); 175 } 176 177 size_t oldChains = getNumberOfChains(); 178 179 BillboardChain::setNumberOfChains(numChains); 180 181 mInitialColour.resize(numChains, ColourValue::White); 182 mDeltaColour.resize(numChains, ColourValue::ZERO); 183 mInitialWidth.resize(numChains, 10); 184 mDeltaWidth.resize(numChains, 0); 185 186 if (oldChains > numChains) 187 { 188 // remove free chains 189 for (IndexVector::iterator i = mFreeChains.begin(); i != mFreeChains.end();) 190 { 191 if (*i >= numChains) 192 i = mFreeChains.erase(i); 193 else 194 ++i; 195 } 196 } 197 else if (oldChains < numChains) 198 { 199 // add new chains, at front to preserve previous ordering (pop_back) 200 for (size_t i = oldChains; i < numChains; ++i) 201 mFreeChains.insert(mFreeChains.begin(), i); 202 } 203 resetAllTrails(); 204 } 205 //----------------------------------------------------------------------- clearChain(size_t chainIndex)206 void RibbonTrail::clearChain(size_t chainIndex) 207 { 208 BillboardChain::clearChain(chainIndex); 209 210 // Reset if we are tracking for this chain 211 IndexVector::iterator i = std::find(mNodeToChainSegment.begin(), mNodeToChainSegment.end(), chainIndex); 212 if (i != mNodeToChainSegment.end()) 213 { 214 size_t nodeIndex = std::distance(mNodeToChainSegment.begin(), i); 215 resetTrail(*i, mNodeList[nodeIndex]); 216 } 217 } 218 //----------------------------------------------------------------------- setInitialColour(size_t chainIndex,const ColourValue & col)219 void RibbonTrail::setInitialColour(size_t chainIndex, const ColourValue& col) 220 { 221 setInitialColour(chainIndex, col.r, col.g, col.b, col.a); 222 } 223 //----------------------------------------------------------------------- setInitialColour(size_t chainIndex,Real r,Real g,Real b,Real a)224 void RibbonTrail::setInitialColour(size_t chainIndex, Real r, Real g, Real b, Real a) 225 { 226 if (chainIndex >= mChainCount) 227 { 228 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 229 "chainIndex out of bounds", "RibbonTrail::setInitialColour"); 230 } 231 mInitialColour[chainIndex].r = r; 232 mInitialColour[chainIndex].g = g; 233 mInitialColour[chainIndex].b = b; 234 mInitialColour[chainIndex].a = a; 235 } 236 //----------------------------------------------------------------------- getInitialColour(size_t chainIndex) const237 const ColourValue& RibbonTrail::getInitialColour(size_t chainIndex) const 238 { 239 if (chainIndex >= mChainCount) 240 { 241 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 242 "chainIndex out of bounds", "RibbonTrail::getInitialColour"); 243 } 244 return mInitialColour[chainIndex]; 245 } 246 //----------------------------------------------------------------------- setInitialWidth(size_t chainIndex,Real width)247 void RibbonTrail::setInitialWidth(size_t chainIndex, Real width) 248 { 249 if (chainIndex >= mChainCount) 250 { 251 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 252 "chainIndex out of bounds", "RibbonTrail::setInitialWidth"); 253 } 254 mInitialWidth[chainIndex] = width; 255 } 256 //----------------------------------------------------------------------- getInitialWidth(size_t chainIndex) const257 Real RibbonTrail::getInitialWidth(size_t chainIndex) const 258 { 259 if (chainIndex >= mChainCount) 260 { 261 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 262 "chainIndex out of bounds", "RibbonTrail::getInitialWidth"); 263 } 264 return mInitialWidth[chainIndex]; 265 } 266 //----------------------------------------------------------------------- setColourChange(size_t chainIndex,const ColourValue & valuePerSecond)267 void RibbonTrail::setColourChange(size_t chainIndex, const ColourValue& valuePerSecond) 268 { 269 setColourChange(chainIndex, 270 valuePerSecond.r, valuePerSecond.g, valuePerSecond.b, valuePerSecond.a); 271 } 272 //----------------------------------------------------------------------- setColourChange(size_t chainIndex,Real r,Real g,Real b,Real a)273 void RibbonTrail::setColourChange(size_t chainIndex, Real r, Real g, Real b, Real a) 274 { 275 if (chainIndex >= mChainCount) 276 { 277 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 278 "chainIndex out of bounds", "RibbonTrail::setColourChange"); 279 } 280 mDeltaColour[chainIndex].r = r; 281 mDeltaColour[chainIndex].g = g; 282 mDeltaColour[chainIndex].b = b; 283 mDeltaColour[chainIndex].a = a; 284 285 manageController(); 286 287 } 288 //----------------------------------------------------------------------- getColourChange(size_t chainIndex) const289 const ColourValue& RibbonTrail::getColourChange(size_t chainIndex) const 290 { 291 if (chainIndex >= mChainCount) 292 { 293 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 294 "chainIndex out of bounds", "RibbonTrail::getColourChange"); 295 } 296 return mDeltaColour[chainIndex]; 297 } 298 //----------------------------------------------------------------------- setWidthChange(size_t chainIndex,Real widthDeltaPerSecond)299 void RibbonTrail::setWidthChange(size_t chainIndex, Real widthDeltaPerSecond) 300 { 301 if (chainIndex >= mChainCount) 302 { 303 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 304 "chainIndex out of bounds", "RibbonTrail::setWidthChange"); 305 } 306 mDeltaWidth[chainIndex] = widthDeltaPerSecond; 307 manageController(); 308 } 309 //----------------------------------------------------------------------- getWidthChange(size_t chainIndex) const310 Real RibbonTrail::getWidthChange(size_t chainIndex) const 311 { 312 if (chainIndex >= mChainCount) 313 { 314 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 315 "chainIndex out of bounds", "RibbonTrail::getWidthChange"); 316 } 317 return mDeltaWidth[chainIndex]; 318 319 } 320 //----------------------------------------------------------------------- manageController(void)321 void RibbonTrail::manageController(void) 322 { 323 bool needController = false; 324 for (size_t i = 0; i < mChainCount; ++i) 325 { 326 if (mDeltaWidth[i] != 0 || mDeltaColour[i] != ColourValue::ZERO) 327 { 328 needController = true; 329 break; 330 } 331 } 332 if (!mFadeController && needController) 333 { 334 // Set up fading via frame time controller 335 ControllerManager& mgr = ControllerManager::getSingleton(); 336 mFadeController = mgr.createFrameTimePassthroughController(mTimeControllerValue); 337 } 338 else if (mFadeController && !needController) 339 { 340 // destroy controller 341 ControllerManager::getSingleton().destroyController(mFadeController); 342 mFadeController = 0; 343 } 344 345 } 346 //----------------------------------------------------------------------- nodeUpdated(const Node * node)347 void RibbonTrail::nodeUpdated(const Node* node) 348 { 349 size_t chainIndex = getChainIndexForNode(node); 350 updateTrail(chainIndex, node); 351 } 352 //----------------------------------------------------------------------- nodeDestroyed(const Node * node)353 void RibbonTrail::nodeDestroyed(const Node* node) 354 { 355 removeNode(const_cast<Node*>(node)); 356 357 } 358 //----------------------------------------------------------------------- updateTrail(size_t index,const Node * node)359 void RibbonTrail::updateTrail(size_t index, const Node* node) 360 { 361 // Repeat this entire process if chain is stretched beyond its natural length 362 bool done = false; 363 while (!done) 364 { 365 // Node has changed somehow, we're only interested in the derived position 366 ChainSegment& seg = mChainSegmentList[index]; 367 Element& headElem = mChainElementList[seg.start + seg.head]; 368 size_t nextElemIdx = seg.head + 1; 369 // wrap 370 if (nextElemIdx == mMaxElementsPerChain) 371 nextElemIdx = 0; 372 Element& nextElem = mChainElementList[seg.start + nextElemIdx]; 373 374 // Vary the head elem, but bake new version if that exceeds element len 375 Vector3 newPos = node->_getDerivedPosition(); 376 if (mParentNode) 377 { 378 // Transform position to ourself space 379 newPos = mParentNode->_getDerivedOrientation().UnitInverse() * 380 (newPos - mParentNode->_getDerivedPosition()) / mParentNode->_getDerivedScale(); 381 } 382 Vector3 diff = newPos - nextElem.position; 383 Real sqlen = diff.squaredLength(); 384 if (sqlen >= mSquaredElemLength) 385 { 386 // Move existing head to mElemLength 387 Vector3 scaledDiff = diff * (mElemLength / Math::Sqrt(sqlen)); 388 headElem.position = nextElem.position + scaledDiff; 389 // Add a new element to be the new head 390 Element newElem( newPos, mInitialWidth[index], 0.0f, 391 mInitialColour[index], node->_getDerivedOrientation() ); 392 addChainElement(index, newElem); 393 // alter diff to represent new head size 394 diff = newPos - headElem.position; 395 // check whether another step is needed or not 396 if (diff.squaredLength() <= mSquaredElemLength) 397 done = true; 398 399 } 400 else 401 { 402 // Extend existing head 403 headElem.position = newPos; 404 done = true; 405 } 406 407 // Is this segment full? 408 if ((seg.tail + 1) % mMaxElementsPerChain == seg.head) 409 { 410 // If so, shrink tail gradually to match head extension 411 Element& tailElem = mChainElementList[seg.start + seg.tail]; 412 size_t preTailIdx; 413 if (seg.tail == 0) 414 preTailIdx = mMaxElementsPerChain - 1; 415 else 416 preTailIdx = seg.tail - 1; 417 Element& preTailElem = mChainElementList[seg.start + preTailIdx]; 418 419 // Measure tail diff from pretail to tail 420 Vector3 taildiff = tailElem.position - preTailElem.position; 421 Real taillen = taildiff.length(); 422 if (taillen > 1e-06) 423 { 424 Real tailsize = mElemLength - diff.length(); 425 taildiff *= tailsize / taillen; 426 tailElem.position = preTailElem.position + taildiff; 427 } 428 429 } 430 } // end while 431 432 433 mBoundsDirty = true; 434 // Need to dirty the parent node, but can't do it using needUpdate() here 435 // since we're in the middle of the scene graph update (node listener), 436 // so re-entrant calls don't work. Queue. 437 if (mParentNode) 438 { 439 Node::queueNeedUpdate(getParentSceneNode()); 440 } 441 442 } 443 //----------------------------------------------------------------------- _timeUpdate(Real time)444 void RibbonTrail::_timeUpdate(Real time) 445 { 446 // Apply all segment effects 447 for (size_t s = 0; s < mChainSegmentList.size(); ++s) 448 { 449 ChainSegment& seg = mChainSegmentList[s]; 450 if (seg.head != SEGMENT_EMPTY && seg.head != seg.tail) 451 { 452 453 for(size_t e = seg.head + 1;; ++e) // until break 454 { 455 e = e % mMaxElementsPerChain; 456 457 Element& elem = mChainElementList[seg.start + e]; 458 elem.width = elem.width - (time * mDeltaWidth[s]); 459 elem.width = std::max(Real(0.0f), elem.width); 460 elem.colour = elem.colour - (mDeltaColour[s] * time); 461 elem.colour.saturate(); 462 463 if (e == seg.tail) 464 break; 465 } 466 } 467 } 468 mVertexContentDirty = true; 469 } 470 //----------------------------------------------------------------------- resetTrail(size_t index,const Node * node)471 void RibbonTrail::resetTrail(size_t index, const Node* node) 472 { 473 assert(index < mChainCount); 474 475 ChainSegment& seg = mChainSegmentList[index]; 476 // set up this segment 477 seg.head = seg.tail = SEGMENT_EMPTY; 478 // Create new element, v coord is always 0.0f 479 // need to convert to take parent node's position into account 480 Vector3 position = node->_getDerivedPosition(); 481 if (mParentNode) 482 { 483 position = mParentNode->_getDerivedOrientation().Inverse() 484 * (position - mParentNode->_getDerivedPosition()) 485 / mParentNode->_getDerivedScale(); 486 } 487 Element e(position, 488 mInitialWidth[index], 0.0f, mInitialColour[index], node->_getDerivedOrientation()); 489 // Add the start position 490 addChainElement(index, e); 491 // Add another on the same spot, this will extend 492 addChainElement(index, e); 493 } 494 //----------------------------------------------------------------------- resetAllTrails(void)495 void RibbonTrail::resetAllTrails(void) 496 { 497 for (size_t i = 0; i < mNodeList.size(); ++i) 498 { 499 resetTrail(i, mNodeList[i]); 500 } 501 } 502 //----------------------------------------------------------------------- getMovableType(void) const503 const String& RibbonTrail::getMovableType(void) const 504 { 505 return RibbonTrailFactory::FACTORY_TYPE_NAME; 506 } 507 //----------------------------------------------------------------------- 508 //----------------------------------------------------------------------- 509 String RibbonTrailFactory::FACTORY_TYPE_NAME = "RibbonTrail"; 510 //----------------------------------------------------------------------- getType(void) const511 const String& RibbonTrailFactory::getType(void) const 512 { 513 return FACTORY_TYPE_NAME; 514 } 515 //----------------------------------------------------------------------- createInstanceImpl(const String & name,const NameValuePairList * params)516 MovableObject* RibbonTrailFactory::createInstanceImpl( const String& name, 517 const NameValuePairList* params) 518 { 519 size_t maxElements = 20; 520 size_t numberOfChains = 1; 521 bool useTex = true; 522 bool useCol = true; 523 // optional params 524 if (params != 0) 525 { 526 NameValuePairList::const_iterator ni = params->find("maxElements"); 527 if (ni != params->end()) 528 { 529 maxElements = StringConverter::parseUnsignedLong(ni->second); 530 } 531 ni = params->find("numberOfChains"); 532 if (ni != params->end()) 533 { 534 numberOfChains = StringConverter::parseUnsignedLong(ni->second); 535 } 536 ni = params->find("useTextureCoords"); 537 if (ni != params->end()) 538 { 539 useTex = StringConverter::parseBool(ni->second); 540 } 541 ni = params->find("useVertexColours"); 542 if (ni != params->end()) 543 { 544 useCol = StringConverter::parseBool(ni->second); 545 } 546 547 } 548 549 return OGRE_NEW RibbonTrail(name, maxElements, numberOfChains, useTex, useCol); 550 551 } 552 //----------------------------------------------------------------------- destroyInstance(MovableObject * obj)553 void RibbonTrailFactory::destroyInstance( MovableObject* obj) 554 { 555 OGRE_DELETE obj; 556 } 557 558 559 560 561 } 562 563