Patch 2793067: fix trunk with OGRE_THREAD_SUPPORT=1 on non-Windows platforms (don...
[ogre3d.git] / OgreMain / src / OgreRibbonTrail.cpp
blob8879eecf1d513bde35baf53e674afc048b01bfd0
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/
7 Copyright (c) 2000-2006 Torus Knot Software Ltd
8 Also see acknowledgements in Readme.html
10 This program is free software; you can redistribute it and/or modify it under
11 the terms of the GNU Lesser General Public License as published by the Free Software
12 Foundation; either version 2 of the License, or (at your option) any later
13 version.
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License along with
20 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21 Place - Suite 330, Boston, MA 02111-1307, USA, or go to
22 http://www.gnu.org/copyleft/lesser.txt.
24 You may alternatively use this source under the terms of a specific version of
25 the OGRE Unrestricted License provided you have obtained such a license from
26 Torus Knot Software Ltd.
27 -----------------------------------------------------------------------------
29 #include "OgreStableHeaders.h"
30 #include "OgreRibbonTrail.h"
31 #include "OgreMath.h"
32 #include "OgreException.h"
33 #include "OgreSceneNode.h"
34 #include "OgreStringConverter.h"
36 namespace Ogre
38 namespace
40 /** Controller value for pass frame time to RibbonTrail
42 class _OgrePrivate TimeControllerValue : public ControllerValue<Real>
44 protected:
45 RibbonTrail* mTrail;
46 public:
47 TimeControllerValue(RibbonTrail* r) { mTrail = r; }
49 Real getValue(void) const { return 0; }// not a source
50 void setValue(Real value) { mTrail->_timeUpdate(value); }
53 //-----------------------------------------------------------------------
54 //-----------------------------------------------------------------------
55 RibbonTrail::RibbonTrail(const String& name, size_t maxElements,
56 size_t numberOfChains, bool useTextureCoords, bool useColours)
57 :BillboardChain(name, maxElements, 0, useTextureCoords, useColours, true),
58 mFadeController(0)
60 setTrailLength(100);
61 setNumberOfChains(numberOfChains);
62 mTimeControllerValue = ControllerValueRealPtr(OGRE_NEW TimeControllerValue(this));
64 // use V as varying texture coord, so we can use 1D textures to 'smear'
65 setTextureCoordDirection(TCD_V);
69 //-----------------------------------------------------------------------
70 RibbonTrail::~RibbonTrail()
72 // Detach listeners
73 for (NodeList::iterator i = mNodeList.begin(); i != mNodeList.end(); ++i)
75 (*i)->setListener(0);
78 if (mFadeController)
80 // destroy controller
81 ControllerManager::getSingleton().destroyController(mFadeController);
85 //-----------------------------------------------------------------------
86 void RibbonTrail::addNode(Node* n)
88 if (mNodeList.size() == mChainCount)
90 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
91 mName + " cannot monitor any more nodes, chain count exceeded",
92 "RibbonTrail::addNode");
94 if (n->getListener())
96 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
97 mName + " cannot monitor node " + n->getName() + " since it already has a listener.",
98 "RibbonTrail::addNode");
101 // get chain index
102 size_t chainIndex = mFreeChains.back();
103 mFreeChains.pop_back();
104 mNodeToChainSegment.push_back(chainIndex);
105 mNodeToSegMap[n] = chainIndex;
107 // initialise the chain
108 resetTrail(chainIndex, n);
110 mNodeList.push_back(n);
111 n->setListener(this);
114 //-----------------------------------------------------------------------
115 size_t RibbonTrail::getChainIndexForNode(const Node* n)
117 NodeToChainSegmentMap::const_iterator i = mNodeToSegMap.find(n);
118 if (i == mNodeToSegMap.end())
120 OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
121 "This node is not being tracked", "RibbonTrail::getChainIndexForNode");
123 return i->second;
125 //-----------------------------------------------------------------------
126 void RibbonTrail::removeNode(Node* n)
128 NodeList::iterator i = std::find(mNodeList.begin(), mNodeList.end(), n);
129 if (i != mNodeList.end())
131 // also get matching chain segment
132 size_t index = std::distance(mNodeList.begin(), i);
133 IndexVector::iterator mi = mNodeToChainSegment.begin();
134 std::advance(mi, index);
135 size_t chainIndex = *mi;
136 BillboardChain::clearChain(chainIndex);
137 // mark as free now
138 mFreeChains.push_back(chainIndex);
139 n->setListener(0);
140 mNodeList.erase(i);
141 mNodeToChainSegment.erase(mi);
142 mNodeToSegMap.erase(mNodeToSegMap.find(n));
146 //-----------------------------------------------------------------------
147 RibbonTrail::NodeIterator
148 RibbonTrail::getNodeIterator(void) const
150 return NodeIterator(mNodeList.begin(), mNodeList.end());
152 //-----------------------------------------------------------------------
153 void RibbonTrail::setTrailLength(Real len)
155 mTrailLength = len;
156 mElemLength = mTrailLength / mMaxElementsPerChain;
157 mSquaredElemLength = mElemLength * mElemLength;
159 //-----------------------------------------------------------------------
160 void RibbonTrail::setMaxChainElements(size_t maxElements)
162 BillboardChain::setMaxChainElements(maxElements);
163 mElemLength = mTrailLength / mMaxElementsPerChain;
164 mSquaredElemLength = mElemLength * mElemLength;
166 resetAllTrails();
168 //-----------------------------------------------------------------------
169 void RibbonTrail::setNumberOfChains(size_t numChains)
171 if (numChains < mNodeList.size())
173 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
174 "Can't shrink the number of chains less than number of tracking nodes",
175 "RibbonTrail::setNumberOfChains");
178 size_t oldChains = getNumberOfChains();
180 BillboardChain::setNumberOfChains(numChains);
182 mInitialColour.resize(numChains, ColourValue::White);
183 mDeltaColour.resize(numChains, ColourValue::ZERO);
184 mInitialWidth.resize(numChains, 10);
185 mDeltaWidth.resize(numChains, 0);
187 if (oldChains > numChains)
189 // remove free chains
190 for (IndexVector::iterator i = mFreeChains.begin(); i != mFreeChains.end();)
192 if (*i >= numChains)
193 i = mFreeChains.erase(i);
194 else
195 ++i;
198 else if (oldChains < numChains)
200 // add new chains, in reverse order to preserve previous ordering (pop_back)
201 int count = static_cast<int>(numChains - oldChains);
202 for (size_t i = numChains - 1; count > 0; --i, --count)
203 mFreeChains.push_back(i);
205 resetAllTrails();
207 //-----------------------------------------------------------------------
208 void RibbonTrail::clearChain(size_t chainIndex)
210 BillboardChain::clearChain(chainIndex);
212 // Reset if we are tracking for this chain
213 IndexVector::iterator i = std::find(mNodeToChainSegment.begin(), mNodeToChainSegment.end(), chainIndex);
214 if (i != mNodeToChainSegment.end())
216 size_t nodeIndex = std::distance(mNodeToChainSegment.begin(), i);
217 resetTrail(*i, mNodeList[nodeIndex]);
220 //-----------------------------------------------------------------------
221 void RibbonTrail::setInitialColour(size_t chainIndex, const ColourValue& col)
223 setInitialColour(chainIndex, col.r, col.g, col.b, col.a);
225 //-----------------------------------------------------------------------
226 void RibbonTrail::setInitialColour(size_t chainIndex, Real r, Real g, Real b, Real a)
228 if (chainIndex >= mChainCount)
230 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
231 "chainIndex out of bounds", "RibbonTrail::setInitialColour");
233 mInitialColour[chainIndex].r = r;
234 mInitialColour[chainIndex].g = g;
235 mInitialColour[chainIndex].b = b;
236 mInitialColour[chainIndex].a = a;
238 //-----------------------------------------------------------------------
239 const ColourValue& RibbonTrail::getInitialColour(size_t chainIndex) const
241 if (chainIndex >= mChainCount)
243 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
244 "chainIndex out of bounds", "RibbonTrail::getInitialColour");
246 return mInitialColour[chainIndex];
248 //-----------------------------------------------------------------------
249 void RibbonTrail::setInitialWidth(size_t chainIndex, Real width)
251 if (chainIndex >= mChainCount)
253 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
254 "chainIndex out of bounds", "RibbonTrail::setInitialWidth");
256 mInitialWidth[chainIndex] = width;
258 //-----------------------------------------------------------------------
259 Real RibbonTrail::getInitialWidth(size_t chainIndex) const
261 if (chainIndex >= mChainCount)
263 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
264 "chainIndex out of bounds", "RibbonTrail::getInitialWidth");
266 return mInitialWidth[chainIndex];
268 //-----------------------------------------------------------------------
269 void RibbonTrail::setColourChange(size_t chainIndex, const ColourValue& valuePerSecond)
271 setColourChange(chainIndex,
272 valuePerSecond.r, valuePerSecond.g, valuePerSecond.b, valuePerSecond.a);
274 //-----------------------------------------------------------------------
275 void RibbonTrail::setColourChange(size_t chainIndex, Real r, Real g, Real b, Real a)
277 if (chainIndex >= mChainCount)
279 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
280 "chainIndex out of bounds", "RibbonTrail::setColourChange");
282 mDeltaColour[chainIndex].r = r;
283 mDeltaColour[chainIndex].g = g;
284 mDeltaColour[chainIndex].b = b;
285 mDeltaColour[chainIndex].a = a;
287 manageController();
290 //-----------------------------------------------------------------------
291 const ColourValue& RibbonTrail::getColourChange(size_t chainIndex) const
293 if (chainIndex >= mChainCount)
295 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
296 "chainIndex out of bounds", "RibbonTrail::getColourChange");
298 return mDeltaColour[chainIndex];
300 //-----------------------------------------------------------------------
301 void RibbonTrail::setWidthChange(size_t chainIndex, Real widthDeltaPerSecond)
303 if (chainIndex >= mChainCount)
305 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
306 "chainIndex out of bounds", "RibbonTrail::setWidthChange");
308 mDeltaWidth[chainIndex] = widthDeltaPerSecond;
309 manageController();
311 //-----------------------------------------------------------------------
312 Real RibbonTrail::getWidthChange(size_t chainIndex) const
314 if (chainIndex >= mChainCount)
316 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
317 "chainIndex out of bounds", "RibbonTrail::getWidthChange");
319 return mDeltaWidth[chainIndex];
322 //-----------------------------------------------------------------------
323 void RibbonTrail::manageController(void)
325 bool needController = false;
326 for (size_t i = 0; i < mChainCount; ++i)
328 if (mDeltaWidth[i] != 0 || mDeltaColour[i] != ColourValue::ZERO)
330 needController = true;
331 break;
334 if (!mFadeController && needController)
336 // Set up fading via frame time controller
337 ControllerManager& mgr = ControllerManager::getSingleton();
338 mFadeController = mgr.createFrameTimePassthroughController(mTimeControllerValue);
340 else if (mFadeController && !needController)
342 // destroy controller
343 ControllerManager::getSingleton().destroyController(mFadeController);
344 mFadeController = 0;
348 //-----------------------------------------------------------------------
349 void RibbonTrail::nodeUpdated(const Node* node)
351 size_t chainIndex = getChainIndexForNode(node);
352 updateTrail(chainIndex, node);
354 //-----------------------------------------------------------------------
355 void RibbonTrail::nodeDestroyed(const Node* node)
357 removeNode(const_cast<Node*>(node));
360 //-----------------------------------------------------------------------
361 void RibbonTrail::updateTrail(size_t index, const Node* node)
363 // Repeat this entire process if chain is stretched beyond its natural length
364 bool done = false;
365 while (!done)
367 // Node has changed somehow, we're only interested in the derived position
368 ChainSegment& seg = mChainSegmentList[index];
369 Element& headElem = mChainElementList[seg.start + seg.head];
370 size_t nextElemIdx = seg.head + 1;
371 // wrap
372 if (nextElemIdx == mMaxElementsPerChain)
373 nextElemIdx = 0;
374 Element& nextElem = mChainElementList[seg.start + nextElemIdx];
376 // Vary the head elem, but bake new version if that exceeds element len
377 Vector3 newPos = node->_getDerivedPosition();
378 if (mParentNode)
380 // Transform position to ourself space
381 newPos = mParentNode->_getDerivedOrientation().UnitInverse() *
382 (newPos - mParentNode->_getDerivedPosition()) / mParentNode->_getDerivedScale();
384 Vector3 diff = newPos - nextElem.position;
385 Real sqlen = diff.squaredLength();
386 if (sqlen >= mSquaredElemLength)
388 // Move existing head to mElemLength
389 Vector3 scaledDiff = diff * (mElemLength / Math::Sqrt(sqlen));
390 headElem.position = nextElem.position + scaledDiff;
391 // Add a new element to be the new head
392 Element newElem(newPos, mInitialWidth[index], 0.0f, mInitialColour[index]);
393 addChainElement(index, newElem);
394 // alter diff to represent new head size
395 diff = newPos - headElem.position;
396 // check whether another step is needed or not
397 if (diff.squaredLength() <= mSquaredElemLength)
398 done = true;
401 else
403 // Extend existing head
404 headElem.position = newPos;
405 done = true;
408 // Is this segment full?
409 if ((seg.tail + 1) % mMaxElementsPerChain == seg.head)
411 // If so, shrink tail gradually to match head extension
412 Element& tailElem = mChainElementList[seg.start + seg.tail];
413 size_t preTailIdx;
414 if (seg.tail == 0)
415 preTailIdx = mMaxElementsPerChain - 1;
416 else
417 preTailIdx = seg.tail - 1;
418 Element& preTailElem = mChainElementList[seg.start + preTailIdx];
420 // Measure tail diff from pretail to tail
421 Vector3 taildiff = tailElem.position - preTailElem.position;
422 Real taillen = taildiff.length();
423 if (taillen > 1e-06)
425 Real tailsize = mElemLength - diff.length();
426 taildiff *= tailsize / taillen;
427 tailElem.position = preTailElem.position + taildiff;
431 } // end while
434 mBoundsDirty = true;
435 // Need to dirty the parent node, but can't do it using needUpdate() here
436 // since we're in the middle of the scene graph update (node listener),
437 // so re-entrant calls don't work. Queue.
438 if (mParentNode)
440 Node::queueNeedUpdate(getParentSceneNode());
444 //-----------------------------------------------------------------------
445 void RibbonTrail::_timeUpdate(Real time)
447 // Apply all segment effects
448 for (size_t s = 0; s < mChainSegmentList.size(); ++s)
450 ChainSegment& seg = mChainSegmentList[s];
451 if (seg.head != SEGMENT_EMPTY && seg.head != seg.tail)
454 for(size_t e = seg.head + 1;; ++e) // until break
456 e = e % mMaxElementsPerChain;
458 Element& elem = mChainElementList[seg.start + e];
459 elem.width = elem.width - (time * mDeltaWidth[s]);
460 elem.width = std::max(Real(0.0f), elem.width);
461 elem.colour = elem.colour - (mDeltaColour[s] * time);
462 elem.colour.saturate();
464 if (e == seg.tail)
465 break;
473 //-----------------------------------------------------------------------
474 void RibbonTrail::resetTrail(size_t index, const Node* node)
476 assert(index < mChainCount);
478 ChainSegment& seg = mChainSegmentList[index];
479 // set up this segment
480 seg.head = seg.tail = SEGMENT_EMPTY;
481 // Create new element, v coord is always 0.0f
482 Element e(node->_getDerivedPosition(),
483 mInitialWidth[index], 0.0f, mInitialColour[index]);
484 // Add the start position
485 addChainElement(index, e);
486 // Add another on the same spot, this will extend
487 addChainElement(index, e);
489 //-----------------------------------------------------------------------
490 void RibbonTrail::resetAllTrails(void)
492 for (size_t i = 0; i < mNodeList.size(); ++i)
494 resetTrail(i, mNodeList[i]);
497 //-----------------------------------------------------------------------
498 const String& RibbonTrail::getMovableType(void) const
500 return RibbonTrailFactory::FACTORY_TYPE_NAME;
502 //-----------------------------------------------------------------------
503 //-----------------------------------------------------------------------
504 String RibbonTrailFactory::FACTORY_TYPE_NAME = "RibbonTrail";
505 //-----------------------------------------------------------------------
506 const String& RibbonTrailFactory::getType(void) const
508 return FACTORY_TYPE_NAME;
510 //-----------------------------------------------------------------------
511 MovableObject* RibbonTrailFactory::createInstanceImpl( const String& name,
512 const NameValuePairList* params)
514 size_t maxElements = 20;
515 size_t numberOfChains = 1;
516 bool useTex = true;
517 bool useCol = true;
518 // optional params
519 if (params != 0)
521 NameValuePairList::const_iterator ni = params->find("maxElements");
522 if (ni != params->end())
524 maxElements = StringConverter::parseUnsignedLong(ni->second);
526 ni = params->find("numberOfChains");
527 if (ni != params->end())
529 numberOfChains = StringConverter::parseUnsignedLong(ni->second);
531 ni = params->find("useTextureCoords");
532 if (ni != params->end())
534 useTex = StringConverter::parseBool(ni->second);
536 ni = params->find("useVertexColours");
537 if (ni != params->end())
539 useCol = StringConverter::parseBool(ni->second);
544 return OGRE_NEW RibbonTrail(name, maxElements, numberOfChains, useTex, useCol);
547 //-----------------------------------------------------------------------
548 void RibbonTrailFactory::destroyInstance( MovableObject* obj)
550 OGRE_DELETE obj;