Show bonus/malus timer text if available
[ryzomcore.git] / nel / src / 3d / channel_mixer.cpp
blobbd8a4dff5eb1bf0baeca31ce0dbdc8ca37526bd4
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "std3d.h"
19 #include "nel/3d/channel_mixer.h"
20 #include "nel/3d/track.h"
21 #include "nel/3d/animatable.h"
22 #include "nel/3d/skeleton_weight.h"
23 #include "nel/misc/debug.h"
24 #include "nel/misc/common.h"
25 #include "nel/misc/hierarchical_timer.h"
27 using namespace NLMISC;
28 using namespace std;
30 #ifdef DEBUG_NEW
31 #define new DEBUG_NEW
32 #endif
34 namespace NL3D
37 // ***************************************************************************
39 CChannelMixer::CChannelMixer()
41 // No channel in the list
42 _FirstChannelGlobal=NULL;
43 _FirstChannelDetail=NULL;
45 // No animation set
46 _AnimationSet=NULL;
48 // Mixer no dirty
49 _Dirt=false;
50 _ListToEvalDirt= false;
52 // never evaluated.
53 _LastEvalDetailDate= -1;
56 // ***************************************************************************
58 CChannelMixer::~CChannelMixer()
60 resetChannels();
63 // ***************************************************************************
65 void CChannelMixer::setAnimationSet (const CAnimationSet* animationSet)
67 // Set the animationSet Pointer
68 _AnimationSet=animationSet;
70 // clear the channels.
71 resetChannels();
74 // ***************************************************************************
76 const CAnimationSet* CChannelMixer::getAnimationSet () const
78 // Return the animationSet Pointer
79 return _AnimationSet;
82 // ***************************************************************************
83 // Temp Data
84 static CAnimatedValueBlock TempAnimatedValueBlock;
87 // ***************************************************************************
88 void CChannelMixer::evalSingleChannel(CChannel &chan, uint numActive, uint activeSlot[NumAnimationSlot])
90 // If the refPtr of the object handled has been deleted, then no-op
91 if(!chan._Object)
93 return;
96 // For Quat animated value only.
97 CQuat firstQuat;
99 // First slot found
100 bool bFirst=true;
102 // Last blend factor
103 float lastBlend=0.0;
105 // Eval each slot
106 for (uint a=0; a<numActive; a++)
108 // Slot number
109 uint slot=activeSlot[a];
111 // Current blend factor
112 float blend=chan._Weights[slot]*_SlotArray[slot]._Weight;
114 if(blend!=0.0f)
116 // Eval the track at this time
117 const IAnimatedValue &trackResult= ((ITrack*)chan._Tracks[slot])->eval (_SlotArray[slot]._Time, TempAnimatedValueBlock);
119 // First track to be eval ?
120 if (bFirst)
122 // If channel is a Quaternion animated Value, must store the first Quat.
123 if (chan._IsQuat)
125 CAnimatedValueBlendable<NLMISC::CQuat> &quatValue=(CAnimatedValueBlendable<NLMISC::CQuat>&)trackResult;
126 firstQuat=quatValue.Value;
129 // Copy the interpolated value
130 chan._Value->affect (trackResult);
132 // First blend factor
133 lastBlend=blend;
135 // Not first anymore
136 bFirst=false;
138 else
140 // If channel is a Quaternion animated Value, must makeClosest the ith result of the track, from firstQuat.
141 if (chan._IsQuat)
143 CAnimatedValueBlendable<NLMISC::CQuat> &quatValue=(CAnimatedValueBlendable<NLMISC::CQuat>&)trackResult;
144 quatValue.Value.makeClosest (firstQuat);
147 // Blend with this value and the previous sum
148 chan._Value->blend (trackResult, lastBlend/(lastBlend+blend));
150 // last blend update
151 lastBlend+=blend;
155 // NB: if all weights are 0, the AnimatedValue is not modified...
158 // Touch the animated value and its owner to recompute them later.
159 chan._Object->touch (chan._ValueId, chan._OwnerValueId);
163 // ***************************************************************************
165 void CChannelMixer::eval (bool detail, uint64 evalDetailDate)
167 // eval the detail animation only one time per scene traversal.
168 if(detail)
170 if((sint64)evalDetailDate== _LastEvalDetailDate)
171 return;
172 _LastEvalDetailDate= evalDetailDate;
175 // clean list according to anim setup
176 if(_Dirt)
178 refreshList();
179 cleanAll();
182 // clean eval list, according to channels enabled.
183 if(_ListToEvalDirt)
185 refreshListToEval();
186 nlassert(!_ListToEvalDirt);
189 // If the number of channels to draw is 0, quick quit.
190 CChannel **channelArrayPtr;
191 uint numChans;
192 if(detail)
194 numChans= (uint)_DetailListToEval.size();
195 if(numChans)
196 channelArrayPtr= &_DetailListToEval[0];
197 else
198 return;
200 else
202 numChans= (uint)_GlobalListToEval.size();
203 if(numChans)
204 channelArrayPtr= &_GlobalListToEval[0];
205 else
206 return;
209 // Setup an array of animation that are not empty and stay. HTimer: 0.0% (because CLod skeletons not parsed here)
210 uint numActive=0;
211 uint activeSlot[NumAnimationSlot];
212 for (uint s=0; s<NumAnimationSlot; s++)
214 // Dirt, not empty and has an influence? (add)
215 if (!_SlotArray[s].isEmpty() && _SlotArray[s]._Weight>0)
216 // Add a dirt slot
217 activeSlot[numActive++]=s;
220 // no slot enabled at all?? skip
221 if(numActive==0)
222 return;
224 // For each selected channel
225 // fast 'just one slot Activated' version
226 if(numActive==1)
228 // Slot number
229 uint slot=activeSlot[0];
230 // Slot time
231 TAnimationTime slotTime= _SlotArray[slot]._Time;
233 // For all channels
234 for(;numChans>0; numChans--, channelArrayPtr++)
236 CChannel &chan= **channelArrayPtr;
238 // If the refPtr of the object handled has been deleted, then no-op
239 if(!chan._Object)
241 continue;
244 // if Current blend factor is not 0
245 if(chan._Weights[slot]!=0.0f)
247 // Eval the track and copy the interpolated value. HTimer: 1.4%
248 chan._Value->affect (((ITrack*)chan._Tracks[slot])->eval (slotTime, TempAnimatedValueBlock));
250 // Touch the animated value and its owner to recompute them later. HTimer: 0.6%
251 chan._Object->touch (chan._ValueId, chan._OwnerValueId);
255 // little bit slower Blend version
256 else
258 // For all channels
259 for(;numChans>0; numChans--, channelArrayPtr++)
261 evalSingleChannel(**channelArrayPtr, numActive, activeSlot);
267 // ***************************************************************************
268 void CChannelMixer::evalChannels(sint *channelIdArray, uint numID)
270 if (!channelIdArray) return;
271 if (numID == 0) return;
273 // Setup an array of animation that are not empty and stay
274 uint numActive=0;
275 uint activeSlot[NumAnimationSlot];
277 // clean list according to anim setup
278 if(_Dirt)
280 refreshList();
281 cleanAll();
284 // clean eval list, according to channels enabled.
285 if(_ListToEvalDirt)
287 refreshListToEval();
288 nlassert(!_ListToEvalDirt);
291 // Setup it up
292 for (uint s=0; s<NumAnimationSlot; s++)
294 // Dirt, not empty and has an influence? (add)
295 if (!_SlotArray[s].isEmpty() && _SlotArray[s]._Weight>0)
296 // Add a dirt slot
297 activeSlot[numActive++]=s;
300 // no slot enabled at all?? skip
301 if(numActive==0)
302 return;
304 for(uint k = 0; k < numID; ++k)
306 std::map<uint, CChannel>::iterator it = _Channels.find(channelIdArray[k]);
307 if (it != _Channels.end())
309 evalSingleChannel(it->second, numActive, activeSlot);
317 // ***************************************************************************
319 sint CChannelMixer::addChannel (const string& channelName, IAnimatable* animatable, IAnimatedValue* value, ITrack* defaultValue, uint32 valueId, uint32 ownerValueId, bool detail)
321 // Check the animationSet has been set
322 nlassert (_AnimationSet);
324 // Check args
325 nlassert (animatable);
326 nlassert (value);
327 nlassert (defaultValue);
329 // Get the channel Id having the same name than the tracks in this animation set.
330 uint iDInAnimationSet=_AnimationSet->getChannelIdByName (channelName);
332 // Tracks exist in this animation set?
333 if (iDInAnimationSet!=CAnimationSet::NotFound)
335 // The channel entry
336 CChannel entry;
338 // Set the channel name
339 entry._ChannelName=channelName;
341 // Set the object pointer
342 entry._Object=animatable;
344 // Set the pointer on the value in the object
345 entry._Value=value;
347 // Is this a CQuat animated value???
348 entry._IsQuat= (typeid (*(entry._Value))==typeid (CAnimatedValueBlendable<NLMISC::CQuat>))!=0;
351 // Set the default track pointer
352 entry._DefaultTracks=defaultValue;
354 // Set the value ID in the object
355 entry._ValueId=valueId;
357 // Set the First value ID in the object
358 entry._OwnerValueId=ownerValueId;
360 // in what mode is the channel?
361 entry._Detail= detail;
363 // All weights default to 1. All Tracks default to defaultTrack.
364 for(sint s=0;s<NumAnimationSlot;s++)
366 entry._Weights[s]= 1.0f;
367 entry._Tracks[s]= entry._DefaultTracks;
370 // add (if not already done) the entry in the map.
371 _Channels[iDInAnimationSet]= entry;
373 // Dirt all the slots
374 dirtAll ();
376 // Affect the default value in the animated value
377 entry._Value->affect (((ITrack*)(entry._DefaultTracks))->eval(0, TempAnimatedValueBlock));
379 // Touch the animated value and its owner to recompute them later.
380 entry._Object->touch (entry._ValueId, entry._OwnerValueId);
382 // return the id.
383 return iDInAnimationSet;
385 else
387 // return Not found.
388 return -1;
392 // ***************************************************************************
394 void CChannelMixer::resetChannels ()
396 // clear
397 _Channels.clear();
398 dirtAll ();
402 // ***************************************************************************
403 void CChannelMixer::enableChannel (uint channelId, bool enable)
405 std::map<uint, CChannel>::iterator it= _Channels.find(channelId);
406 if(it!=_Channels.end())
408 it->second._EnableFlags &= ~CChannel::EnableUserFlag;
409 if(enable)
410 it->second._EnableFlags |= CChannel::EnableUserFlag;
412 // Must recompute the channels to animate.
413 _ListToEvalDirt= true;
418 // ***************************************************************************
419 bool CChannelMixer::isChannelEnabled (uint channelId) const
421 std::map<uint, CChannel>::const_iterator it= _Channels.find(channelId);
422 if(it!=_Channels.end())
424 return (it->second._EnableFlags & CChannel::EnableUserFlag) != 0;
426 else
427 return false;
431 // ***************************************************************************
432 void CChannelMixer::lodEnableChannel (uint channelId, bool enable)
434 std::map<uint, CChannel>::iterator it= _Channels.find(channelId);
435 if(it!=_Channels.end())
437 it->second._EnableFlags &= ~CChannel::EnableLodFlag;
438 if(enable)
439 it->second._EnableFlags |= CChannel::EnableLodFlag;
441 // Must recompute the channels to animate.
442 _ListToEvalDirt= true;
447 // ***************************************************************************
448 bool CChannelMixer::isChannelLodEnabled (uint channelId) const
450 std::map<uint, CChannel>::const_iterator it= _Channels.find(channelId);
451 if(it!=_Channels.end())
453 return (it->second._EnableFlags & CChannel::EnableLodFlag) != 0;
455 else
456 return false;
461 // ***************************************************************************
463 void CChannelMixer::setSlotAnimation (uint slot, uint animation)
465 // Check alot arg
466 nlassert (slot<NumAnimationSlot);
468 // Check an animationSet as been set.
469 nlassert (_AnimationSet);
471 // Find the animation pointer for this animation
472 const CAnimation* pAnimation=_AnimationSet->getAnimation (animation);
474 // Does this animation change ?
475 if (_SlotArray[slot]._Animation!=pAnimation)
477 // Change it
478 _SlotArray[slot]._Animation=pAnimation;
480 // Dirt it
481 _SlotArray[slot]._Dirt=true;
483 // Dirt the mixer
484 _Dirt=true;
488 // ***************************************************************************
490 const CAnimation *CChannelMixer::getSlotAnimation(uint slot) const
492 nlassert(slot < NumAnimationSlot);
493 return _SlotArray[slot]._Animation;
497 // ***************************************************************************
499 void CChannelMixer::emptySlot (uint slot)
501 // Check alot arg
502 nlassert (slot<NumAnimationSlot);
504 // Does this animation already empty ?
505 if (!_SlotArray[slot].isEmpty ())
507 // Change it
508 _SlotArray[slot].empty ();
510 // Dirt it
511 _SlotArray[slot]._Dirt=true;
513 // Dirt the mixer
514 _Dirt=true;
518 // ***************************************************************************
520 void CChannelMixer::resetSlots ()
522 // Empty all slots
523 for (uint s=0; s<NumAnimationSlot; s++)
524 // Empty it
525 emptySlot (s);
528 // ***************************************************************************
530 void CChannelMixer::applySkeletonWeight (uint slot, uint skeleton, bool invert)
532 // Check alot arg
533 nlassert (slot<NumAnimationSlot);
535 // Check the animationSet has been set
536 nlassert (_AnimationSet);
538 // Get the skeleton weight
539 const CSkeletonWeight *pSkeleton=_AnimationSet->getSkeletonWeight (skeleton);
541 // Something to change ?
542 if ((pSkeleton!=_SlotArray[slot]._SkeletonWeight)||(invert!=_SlotArray[slot]._InvertedSkeletonWeight))
544 // Set the current skeleton
545 _SlotArray[slot]._SkeletonWeight=pSkeleton;
546 _SlotArray[slot]._InvertedSkeletonWeight=invert;
548 // Get number of node in the skeleton weight
549 uint sizeSkel=pSkeleton->getNumNode ();
551 // For each entry of the skeleton weight
552 for (uint n=0; n<sizeSkel; n++)
554 // Get the name of the channel for this node
555 const string& channelName=pSkeleton->getNodeName (n);
557 // Get the channel Id having the same name than the tracks in this animation set.
558 uint channelId=_AnimationSet->getChannelIdByName (channelName);
560 // Tracks exist in this animation set?
561 if (channelId!=CAnimationSet::NotFound)
563 // Get the weight of the channel for this node
564 float weight=pSkeleton->getNodeWeight (n);
566 // Set the weight of this channel for this slot (only if channel setuped!!)
567 std::map<uint, CChannel>::iterator ite=_Channels.find(channelId);
568 if (ite!=_Channels.end())
569 ite->second._Weights[slot]=invert?1.f-weight:weight;
575 // ***************************************************************************
577 void CChannelMixer::resetSkeletonWeight (uint slot)
579 // Check alot arg
580 nlassert (slot<NumAnimationSlot);
582 // Something to change ?
583 if (_SlotArray[slot]._SkeletonWeight!=NULL)
585 // Set skeleton
586 _SlotArray[slot]._SkeletonWeight=NULL;
587 _SlotArray[slot]._InvertedSkeletonWeight=false;
589 // For each channels
590 map<uint, CChannel>::iterator itChannel;
591 for(itChannel= _Channels.begin(); itChannel!=_Channels.end();itChannel++)
593 // Reset
594 (*itChannel).second._Weights[slot]=1.f;
599 // ***************************************************************************
601 void CChannelMixer::cleanAll ()
603 // For each slot
604 for (uint s=0; s<NumAnimationSlot; s++)
606 // Clean it
607 _SlotArray[s]._Dirt=false;
610 // Clean the mixer
611 _Dirt=false;
614 // ***************************************************************************
616 void CChannelMixer::dirtAll ()
618 // For each slot
619 for (uint s=0; s<NumAnimationSlot; s++)
621 // Dirt
622 if (!_SlotArray[s].isEmpty())
624 // Dirt it
625 _SlotArray[s]._Dirt=true;
627 // Dirt the mixer
628 _Dirt=true;
633 // ***************************************************************************
635 void CChannelMixer::refreshList ()
637 // Setup an array of animation to add
638 uint numAdd=0;
639 uint addSlot[NumAnimationSlot];
641 // Setup an array of animation that are not empty and stay
642 uint numStay=0;
643 uint staySlot[NumAnimationSlot];
645 // Setup it up
646 uint s;
647 for (s=0; s<NumAnimationSlot; s++)
649 // Dirt and not empty ? (add)
650 if ((_SlotArray[s]._Dirt)&&(!_SlotArray[s].isEmpty()))
651 // Add a dirt slot
652 addSlot[numAdd++]=s;
654 // Not empty and not dirt ? (stay)
655 if ((!_SlotArray[s]._Dirt)&&(!_SlotArray[s].isEmpty()))
656 // Add a dirt slot
657 staySlot[numStay++]=s;
660 // Last channel pointer
661 CChannel **lastPointerGlobal=&_FirstChannelGlobal;
662 CChannel **lastPointerDetail=&_FirstChannelDetail;
665 // Now scan each channel
666 map<uint, CChannel>::iterator itChannel;
667 for(itChannel= _Channels.begin(); itChannel!=_Channels.end();itChannel++)
669 uint channelId= itChannel->first;
670 CChannel &channel= (*itChannel).second;
672 // Add this channel to the list if true
673 bool add=false;
675 // For each slot to add
676 for (s=0; s<numAdd; s++)
678 uint iDTrack;
680 // If the animation set is header compressed,
681 if(_AnimationSet->isAnimHeaderOptimized())
682 // can retrieve the animation trough the channel id (faster)
683 iDTrack= _SlotArray[addSlot[s]]._Animation->getIdTrackByChannelId(channelId);
684 else
685 // get by name
686 iDTrack= _SlotArray[addSlot[s]]._Animation->getIdTrackByName (channel._ChannelName);
688 // If this track exist
689 if (iDTrack!=CAnimation::NotFound)
691 // Set the track
692 channel._Tracks[addSlot[s]]=_SlotArray[addSlot[s]]._Animation->getTrack (iDTrack);
694 // Add this channel to the list
695 add=true;
697 else
699 // Set the default track
700 channel._Tracks[addSlot[s]]=channel._DefaultTracks;
704 // Add this channel to the list ?
705 if (!add)
707 // Was it in the list ?
708 if (channel._InTheList)
710 // Check if this channel is still in use
712 // For each slot in the stay list
713 for (s=0; s<numStay; s++)
715 // Use anything interesting ?
716 if (channel._Tracks[staySlot[s]]!=channel._DefaultTracks)
718 // Ok, add it to the list
719 add=true;
721 // Stop
722 break;
726 // Still in use?
727 if (!add)
729 // Ensure first the object is not deleted
730 if(channel._Object)
732 // Set it's value to default and touch it's object
733 channel._Value->affect (((ITrack*)(channel._DefaultTracks))->eval(0, TempAnimatedValueBlock));
734 channel._Object->touch (channel._ValueId, channel._OwnerValueId);
740 // Do i have to add the channel to the list
741 if (add)
743 // It is in the list
744 channel._InTheList=true;
746 if(channel._Detail)
748 // Set the last pointer value
749 *lastPointerDetail=&channel;
750 // Change last pointer
751 lastPointerDetail=&channel._Next;
753 else
755 // Set the last pointer value
756 *lastPointerGlobal=&channel;
757 // Change last pointer
758 lastPointerGlobal=&channel._Next;
761 else
763 // It is not in the list
764 channel._InTheList=false;
768 // End of the list
769 *lastPointerGlobal=NULL;
770 *lastPointerDetail=NULL;
772 // Must recompute the channels to animate.
773 _ListToEvalDirt= true;
777 // ***************************************************************************
778 void CChannelMixer::refreshListToEval ()
780 CChannel* pChannel;
782 /* NB: this save if(), especially when Used with Skeleton, and CLod mode
785 // Global list.
786 _GlobalListToEval.clear();
787 _GlobalListToEval.reserve(_Channels.size());
788 pChannel=_FirstChannelGlobal;
789 while(pChannel)
791 // if the channel is enabled (both user and lod), must eval all active slot.
792 if(pChannel->_EnableFlags == CChannel::EnableAllFlag)
793 _GlobalListToEval.push_back(pChannel);
794 // next
795 pChannel= pChannel->_Next;
798 // Global list.
799 _DetailListToEval.clear();
800 _DetailListToEval.reserve(_Channels.size());
801 pChannel=_FirstChannelDetail;
802 while(pChannel)
804 // if the channel is enabled (both user and lod), must eval all active slot.
805 if(pChannel->_EnableFlags == CChannel::EnableAllFlag)
806 _DetailListToEval.push_back(pChannel);
807 // next
808 pChannel= pChannel->_Next;
811 // done
812 _ListToEvalDirt= false;
815 // ***************************************************************************
816 void CChannelMixer::resetEvalDetailDate()
818 _LastEvalDetailDate= -1;
822 } // NL3D