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
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"
32 #include "OgreException.h"
33 #include "OgreSceneNode.h"
34 #include "OgreStringConverter.h"
40 /** Controller value for pass frame time to RibbonTrail
42 class _OgrePrivate TimeControllerValue
: public ControllerValue
<Real
>
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),
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()
73 for (NodeList::iterator i
= mNodeList
.begin(); i
!= mNodeList
.end(); ++i
)
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");
96 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS
,
97 mName
+ " cannot monitor node " + n
->getName() + " since it already has a listener.",
98 "RibbonTrail::addNode");
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");
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
);
138 mFreeChains
.push_back(chainIndex
);
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
)
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
;
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();)
193 i
= mFreeChains
.erase(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
);
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
;
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
;
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;
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
);
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
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;
372 if (nextElemIdx
== mMaxElementsPerChain
)
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();
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
)
403 // Extend existing head
404 headElem
.position
= newPos
;
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
];
415 preTailIdx
= mMaxElementsPerChain
- 1;
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();
425 Real tailsize
= mElemLength
- diff
.length();
426 taildiff
*= tailsize
/ taillen
;
427 tailElem
.position
= preTailElem
.position
+ taildiff
;
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.
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();
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;
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
)