1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
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
;
37 // ***************************************************************************
39 CChannelMixer::CChannelMixer()
41 // No channel in the list
42 _FirstChannelGlobal
=NULL
;
43 _FirstChannelDetail
=NULL
;
50 _ListToEvalDirt
= false;
53 _LastEvalDetailDate
= -1;
56 // ***************************************************************************
58 CChannelMixer::~CChannelMixer()
63 // ***************************************************************************
65 void CChannelMixer::setAnimationSet (const CAnimationSet
* animationSet
)
67 // Set the animationSet Pointer
68 _AnimationSet
=animationSet
;
70 // clear the channels.
74 // ***************************************************************************
76 const CAnimationSet
* CChannelMixer::getAnimationSet () const
78 // Return the animationSet Pointer
82 // ***************************************************************************
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
96 // For Quat animated value only.
106 for (uint a
=0; a
<numActive
; a
++)
109 uint slot
=activeSlot
[a
];
111 // Current blend factor
112 float blend
=chan
._Weights
[slot
]*_SlotArray
[slot
]._Weight
;
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 ?
122 // If channel is a Quaternion animated Value, must store the first Quat.
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
140 // If channel is a Quaternion animated Value, must makeClosest the ith result of the track, from firstQuat.
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
));
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.
170 if((sint64
)evalDetailDate
== _LastEvalDetailDate
)
172 _LastEvalDetailDate
= evalDetailDate
;
175 // clean list according to anim setup
182 // clean eval list, according to channels enabled.
186 nlassert(!_ListToEvalDirt
);
189 // If the number of channels to draw is 0, quick quit.
190 CChannel
**channelArrayPtr
;
194 numChans
= (uint
)_DetailListToEval
.size();
196 channelArrayPtr
= &_DetailListToEval
[0];
202 numChans
= (uint
)_GlobalListToEval
.size();
204 channelArrayPtr
= &_GlobalListToEval
[0];
209 // Setup an array of animation that are not empty and stay. HTimer: 0.0% (because CLod skeletons not parsed here)
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)
217 activeSlot
[numActive
++]=s
;
220 // no slot enabled at all?? skip
224 // For each selected channel
225 // fast 'just one slot Activated' version
229 uint slot
=activeSlot
[0];
231 TAnimationTime slotTime
= _SlotArray
[slot
]._Time
;
234 for(;numChans
>0; numChans
--, channelArrayPtr
++)
236 CChannel
&chan
= **channelArrayPtr
;
238 // If the refPtr of the object handled has been deleted, then no-op
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
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
275 uint activeSlot
[NumAnimationSlot
];
277 // clean list according to anim setup
284 // clean eval list, according to channels enabled.
288 nlassert(!_ListToEvalDirt
);
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)
297 activeSlot
[numActive
++]=s
;
300 // no slot enabled at all?? skip
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
);
325 nlassert (animatable
);
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
)
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
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
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
);
383 return iDInAnimationSet
;
392 // ***************************************************************************
394 void CChannelMixer::resetChannels ()
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
;
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;
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
;
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;
461 // ***************************************************************************
463 void CChannelMixer::setSlotAnimation (uint slot
, uint animation
)
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
)
478 _SlotArray
[slot
]._Animation
=pAnimation
;
481 _SlotArray
[slot
]._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
)
502 nlassert (slot
<NumAnimationSlot
);
504 // Does this animation already empty ?
505 if (!_SlotArray
[slot
].isEmpty ())
508 _SlotArray
[slot
].empty ();
511 _SlotArray
[slot
]._Dirt
=true;
518 // ***************************************************************************
520 void CChannelMixer::resetSlots ()
523 for (uint s
=0; s
<NumAnimationSlot
; s
++)
528 // ***************************************************************************
530 void CChannelMixer::applySkeletonWeight (uint slot
, uint skeleton
, bool invert
)
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
)
580 nlassert (slot
<NumAnimationSlot
);
582 // Something to change ?
583 if (_SlotArray
[slot
]._SkeletonWeight
!=NULL
)
586 _SlotArray
[slot
]._SkeletonWeight
=NULL
;
587 _SlotArray
[slot
]._InvertedSkeletonWeight
=false;
590 map
<uint
, CChannel
>::iterator itChannel
;
591 for(itChannel
= _Channels
.begin(); itChannel
!=_Channels
.end();itChannel
++)
594 (*itChannel
).second
._Weights
[slot
]=1.f
;
599 // ***************************************************************************
601 void CChannelMixer::cleanAll ()
604 for (uint s
=0; s
<NumAnimationSlot
; s
++)
607 _SlotArray
[s
]._Dirt
=false;
614 // ***************************************************************************
616 void CChannelMixer::dirtAll ()
619 for (uint s
=0; s
<NumAnimationSlot
; s
++)
622 if (!_SlotArray
[s
].isEmpty())
625 _SlotArray
[s
]._Dirt
=true;
633 // ***************************************************************************
635 void CChannelMixer::refreshList ()
637 // Setup an array of animation to add
639 uint addSlot
[NumAnimationSlot
];
641 // Setup an array of animation that are not empty and stay
643 uint staySlot
[NumAnimationSlot
];
647 for (s
=0; s
<NumAnimationSlot
; s
++)
649 // Dirt and not empty ? (add)
650 if ((_SlotArray
[s
]._Dirt
)&&(!_SlotArray
[s
].isEmpty()))
654 // Not empty and not dirt ? (stay)
655 if ((!_SlotArray
[s
]._Dirt
)&&(!_SlotArray
[s
].isEmpty()))
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
675 // For each slot to add
676 for (s
=0; s
<numAdd
; s
++)
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
);
686 iDTrack
= _SlotArray
[addSlot
[s
]]._Animation
->getIdTrackByName (channel
._ChannelName
);
688 // If this track exist
689 if (iDTrack
!=CAnimation::NotFound
)
692 channel
._Tracks
[addSlot
[s
]]=_SlotArray
[addSlot
[s
]]._Animation
->getTrack (iDTrack
);
694 // Add this channel to the list
699 // Set the default track
700 channel
._Tracks
[addSlot
[s
]]=channel
._DefaultTracks
;
704 // Add this channel to the list ?
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
729 // Ensure first the object is not deleted
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
744 channel
._InTheList
=true;
748 // Set the last pointer value
749 *lastPointerDetail
=&channel
;
750 // Change last pointer
751 lastPointerDetail
=&channel
._Next
;
755 // Set the last pointer value
756 *lastPointerGlobal
=&channel
;
757 // Change last pointer
758 lastPointerGlobal
=&channel
._Next
;
763 // It is not in the list
764 channel
._InTheList
=false;
769 *lastPointerGlobal
=NULL
;
770 *lastPointerDetail
=NULL
;
772 // Must recompute the channels to animate.
773 _ListToEvalDirt
= true;
777 // ***************************************************************************
778 void CChannelMixer::refreshListToEval ()
782 /* NB: this save if(), especially when Used with Skeleton, and CLod mode
786 _GlobalListToEval
.clear();
787 _GlobalListToEval
.reserve(_Channels
.size());
788 pChannel
=_FirstChannelGlobal
;
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
);
795 pChannel
= pChannel
->_Next
;
799 _DetailListToEval
.clear();
800 _DetailListToEval
.reserve(_Channels
.size());
801 pChannel
=_FirstChannelDetail
;
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
);
808 pChannel
= pChannel
->_Next
;
812 _ListToEvalDirt
= false;
815 // ***************************************************************************
816 void CChannelMixer::resetEvalDetailDate()
818 _LastEvalDetailDate
= -1;