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