# Home    # nevrax.com   
Nevrax
Nevrax.org
#News
#Mailing-list
#Documentation
#CVS
#Bugs
#License
Docs
 
Documentation  
Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages   Search  

channel_mixer.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2001 Nevrax Ltd.
00008  *
00009  * This file is part of NEVRAX NEL.
00010  * NEVRAX NEL is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2, or (at your option)
00013  * any later version.
00014 
00015  * NEVRAX NEL is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * General Public License for more details.
00019 
00020  * You should have received a copy of the GNU General Public License
00021  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023  * MA 02111-1307, USA.
00024  */
00025 
00026 #include "std3d.h"
00027 
00028 #include "3d/channel_mixer.h"
00029 #include "3d/track.h"
00030 #include "3d/animatable.h"
00031 #include "3d/skeleton_weight.h"
00032 #include "nel/misc/debug.h"
00033 #include "nel/misc/common.h"
00034 
00035 using namespace NLMISC;
00036 using namespace std;
00037 
00038 namespace NL3D 
00039 {
00040 
00041 // ***************************************************************************
00042 
00043 CChannelMixer::CChannelMixer()
00044 {
00045         // No channel in the list
00046         _FirstChannelGlobal=NULL;
00047         _FirstChannelDetail=NULL;
00048 
00049         // No animation set
00050         _AnimationSet=NULL;
00051 
00052         // Mixer no dirty
00053         _Dirt=false;
00054         _ListToEvalDirt= false;
00055 
00056         // never evaluated.
00057         _LastEvalDetailDate= -1;
00058 }
00059 
00060 // ***************************************************************************
00061 
00062 void CChannelMixer::setAnimationSet (const CAnimationSet* animationSet)
00063 {
00064         // Set the animationSet Pointer
00065         _AnimationSet=animationSet;
00066 
00067         // clear the channels.
00068         resetChannels();
00069 }
00070 
00071 // ***************************************************************************
00072 
00073 const CAnimationSet* CChannelMixer::getAnimationSet () const
00074 {
00075         // Return the animationSet Pointer
00076         return _AnimationSet;
00077 }
00078 
00079 // ***************************************************************************
00080 
00081 void CChannelMixer::eval (bool detail, uint64 evalDetailDate)
00082 {
00083         // Setup an array of animation that are not empty and stay
00084         uint numActive=0;
00085         uint activeSlot[NumAnimationSlot];
00086 
00087         if(detail && (sint64)evalDetailDate== _LastEvalDetailDate)
00088                 return;
00089 
00090         // clean list according to anim setup
00091         if(_Dirt)
00092         {
00093                 refreshList();
00094                 cleanAll();
00095         }
00096 
00097         // clean eval list, according to channels enabled.
00098         if(_ListToEvalDirt)
00099         {
00100                 refreshListToEval();
00101                 nlassert(!_ListToEvalDirt);
00102         }
00103 
00104         // Setup it up
00105         for (uint s=0; s<NumAnimationSlot; s++)
00106         {
00107                 // Dirt, not empty and has an influence? (add)
00108                 if (!_SlotArray[s].isEmpty() && _SlotArray[s]._Weight>0)
00109                         // Add a dirt slot
00110                         activeSlot[numActive++]=s;
00111         }
00112 
00113         // no slot enabled at all?? skip
00114         if(numActive==0)
00115                 return;
00116 
00117         // For each selected channel
00118         CChannel        **channelArrayPtr = 0;
00119         uint            numChans;
00120         if(detail)
00121         {
00122                 numChans= _DetailListToEval.size();
00123                 if(numChans)
00124                         channelArrayPtr= &_DetailListToEval[0];
00125                 // eval the animation only one time per scene traversal.
00126                 _LastEvalDetailDate= evalDetailDate;
00127         }
00128         else
00129         {
00130                 numChans= _GlobalListToEval.size();
00131                 if(numChans)
00132                         channelArrayPtr= &_GlobalListToEval[0];
00133         }
00134 
00135 
00136         for(;numChans>0; numChans--, channelArrayPtr++)
00137         {
00138                 CChannel        *pChannel= *channelArrayPtr;
00139 
00140                 // For Quat animated value only.
00141                 CQuat   firstQuat;
00142 
00143                 // First slot found
00144                 bool bFirst=true;
00145 
00146                 // Last blend factor
00147                 float lastBlend=0.0;
00148 
00149                 // Eval each slot
00150                 for (uint a=0; a<numActive; a++)
00151                 {
00152                         // Slot number
00153                         uint slot=activeSlot[a];
00154 
00155                         // Current blend factor
00156                         float blend=pChannel->_Weights[slot]*_SlotArray[slot]._Weight;
00157 
00158                         if(blend!=0.0f)
00159                         {
00160                                 // Eval the track at this time
00161                                 ((ITrack*)pChannel->_Tracks[slot])->eval (_SlotArray[slot]._Time);
00162 
00163                                 // First track to be eval ?
00164                                 if (bFirst)
00165                                 {
00166                                         // If channel is a Quaternion animated Value, must store the first Quat.
00167                                         if (pChannel->_IsQuat)
00168                                         {
00169                                                 CAnimatedValueBlendable<NLMISC::CQuat>  *pQuatValue=(CAnimatedValueBlendable<NLMISC::CQuat>*)&pChannel->_Tracks[slot]->getValue();
00170                                                 firstQuat=pQuatValue->Value;
00171                                         }
00172 
00173                                         // Copy the interpolated value
00174                                         pChannel->_Value->affect (pChannel->_Tracks[slot]->getValue());
00175 
00176                                         // First blend factor
00177                                         lastBlend=blend;
00178 
00179                                         // Not first anymore
00180                                         bFirst=false;
00181                                 }
00182                                 else
00183                                 {
00184                                         // If channel is a Quaternion animated Value, must makeClosest the ith result of the track, from firstQuat.
00185                                         if (pChannel->_IsQuat)
00186                                         {
00187                                                 CAnimatedValueBlendable<NLMISC::CQuat>  *pQuatValue=(CAnimatedValueBlendable<NLMISC::CQuat>*)&pChannel->_Tracks[slot]->getValue();
00188                                                 pQuatValue->Value.makeClosest (firstQuat);
00189                                         }
00190 
00191                                         // Blend with this value and the previous sum
00192                                         pChannel->_Value->blend (pChannel->_Tracks[slot]->getValue(), lastBlend/(lastBlend+blend));
00193 
00194                                         // last blend update
00195                                         lastBlend+=blend;
00196                                 }
00197                         }
00198 
00199                         // NB: if all weights are 0, the AnimatedValue is not modified...
00200                 }
00201 
00202                 // Touch the animated value and its owner to recompute them later.
00203                 pChannel->_Object->touch (pChannel->_ValueId, pChannel->_OwnerValueId);
00204         }
00205 }
00206 
00207 // ***************************************************************************
00208 
00209 sint CChannelMixer::addChannel (const string& channelName, IAnimatable* animatable, IAnimatedValue* value, ITrack* defaultValue, uint32 valueId, uint32 ownerValueId, bool detail)
00210 {
00211         // Check the animationSet has been set
00212         nlassert (_AnimationSet);
00213 
00214         // Check args
00215         nlassert (animatable);
00216         nlassert (value);
00217         nlassert (defaultValue);
00218 
00219         // Get the channel Id having the same name than the tracks in this animation set.
00220         uint iDInAnimationSet=_AnimationSet->getChannelIdByName (channelName);
00221 
00222         // Tracks exist in this animation set?
00223         if (iDInAnimationSet!=CAnimationSet::NotFound)
00224         {
00225                 // The channel entry
00226                 CChannel        entry;
00227 
00228                 // Set the channel name
00229                 entry._ChannelName=channelName;
00230 
00231                 // Set the object pointer
00232                 entry._Object=animatable;
00233 
00234                 // Set the pointer on the value in the object
00235                 entry._Value=value;
00236 
00237                 // Is this a CQuat animated value???
00238                 entry._IsQuat= (typeid (*(entry._Value))==typeid (CAnimatedValueBlendable<NLMISC::CQuat>))!=0;
00239 
00240 
00241                 // Set the default track pointer
00242                 entry._DefaultTracks=defaultValue;
00243 
00244                 // Set the value ID in the object
00245                 entry._ValueId=valueId;
00246 
00247                 // Set the First value ID in the object
00248                 entry._OwnerValueId=ownerValueId;
00249 
00250                 // in what mode is the channel?
00251                 entry._Detail= detail;
00252 
00253                 // All weights default to 1. All Tracks default to defaultTrack.
00254                 for(sint s=0;s<NumAnimationSlot;s++)
00255                 {
00256                         entry._Weights[s]= 1.0f;
00257                         entry._Tracks[s]= entry._DefaultTracks;
00258                 }
00259 
00260                 // add (if not already done) the entry in the map.
00261                 _Channels[iDInAnimationSet]= entry;
00262 
00263                 // Dirt all the slots
00264                 dirtAll ();
00265 
00266                 // Affect the default value in the animated value
00267                 entry._Value->affect (entry._DefaultTracks->getValue());
00268 
00269                 // Touch the animated value and its owner to recompute them later.
00270                 entry._Object->touch (entry._ValueId, entry._OwnerValueId);
00271 
00272                 // return the id.
00273                 return iDInAnimationSet;
00274         }
00275         else
00276         {
00277                 // return Not found.
00278                 return -1;
00279         }
00280 }
00281 
00282 // ***************************************************************************
00283 
00284 void CChannelMixer::resetChannels ()
00285 {
00286         _Channels.clear();
00287         dirtAll ();
00288 }
00289 
00290 
00291 // ***************************************************************************
00292 void CChannelMixer::enableChannel (uint channelId, bool enable)
00293 {
00294         std::map<uint, CChannel>::iterator      it= _Channels.find(channelId);
00295         if(it!=_Channels.end())
00296         {
00297                 it->second._EnableFlags &= ~CChannel::EnableUserFlag;
00298                 if(enable)
00299                         it->second._EnableFlags |= CChannel::EnableUserFlag;
00300 
00301                 // Must recompute the channels to animate.
00302                 _ListToEvalDirt= true;
00303         }
00304 }
00305 
00306 
00307 // ***************************************************************************
00308 bool CChannelMixer::isChannelEnabled (uint channelId) const
00309 {
00310         std::map<uint, CChannel>::const_iterator        it= _Channels.find(channelId);
00311         if(it!=_Channels.end())
00312         {
00313                 return (it->second._EnableFlags & CChannel::EnableUserFlag) != 0;
00314         }
00315         else
00316                 return false;
00317 }
00318 
00319 
00320 // ***************************************************************************
00321 void CChannelMixer::lodEnableChannel (uint channelId, bool enable)
00322 {
00323         std::map<uint, CChannel>::iterator      it= _Channels.find(channelId);
00324         if(it!=_Channels.end())
00325         {
00326                 it->second._EnableFlags &= ~CChannel::EnableLodFlag;
00327                 if(enable)
00328                         it->second._EnableFlags |= CChannel::EnableLodFlag;
00329 
00330                 // Must recompute the channels to animate.
00331                 _ListToEvalDirt= true;
00332         }
00333 }
00334 
00335 
00336 // ***************************************************************************
00337 bool CChannelMixer::isChannelLodEnabled (uint channelId) const
00338 {
00339         std::map<uint, CChannel>::const_iterator        it= _Channels.find(channelId);
00340         if(it!=_Channels.end())
00341         {
00342                 return (it->second._EnableFlags & CChannel::EnableLodFlag) != 0;
00343         }
00344         else
00345                 return false;
00346 }
00347 
00348 
00349 
00350 // ***************************************************************************
00351 
00352 void CChannelMixer::setSlotAnimation (uint slot, uint animation)
00353 {
00354         // Check alot arg
00355         nlassert (slot<NumAnimationSlot);
00356 
00357         // Check an animationSet as been set.
00358         nlassert (_AnimationSet);
00359 
00360         // Find the animation pointer for this animation
00361         const CAnimation* pAnimation=_AnimationSet->getAnimation (animation);
00362 
00363         // Does this animation change ?
00364         if (_SlotArray[slot]._Animation!=pAnimation)
00365         {
00366                 // Change it
00367                 _SlotArray[slot]._Animation=pAnimation;
00368 
00369                 // Dirt it
00370                 _SlotArray[slot]._Dirt=true;
00371 
00372                 // Dirt the mixer
00373                 _Dirt=true;
00374         }
00375 }
00376 
00377 // ***************************************************************************
00378 
00379 const CAnimation        *CChannelMixer::getSlotAnimation(uint slot) const
00380 {
00381         nlassert(slot < NumAnimationSlot);
00382         return _SlotArray[slot]._Animation;
00383 }
00384 
00385 
00386 // ***************************************************************************
00387 
00388 void CChannelMixer::emptySlot (uint slot)
00389 {
00390         // Check alot arg
00391         nlassert (slot<NumAnimationSlot);
00392 
00393         // Does this animation already empty ?
00394         if (!_SlotArray[slot].isEmpty ())
00395         {
00396                 // Change it
00397                 _SlotArray[slot].empty ();
00398 
00399                 // Dirt it
00400                 _SlotArray[slot]._Dirt=true;
00401 
00402                 // Dirt the mixer
00403                 _Dirt=true;
00404         }
00405 }
00406 
00407 // ***************************************************************************
00408 
00409 void CChannelMixer::resetSlots ()
00410 {
00411         // Empty all slots
00412         for (uint s=0; s<NumAnimationSlot; s++)
00413                 // Empty it
00414                 emptySlot (s);
00415 }
00416 
00417 // ***************************************************************************
00418 
00419 void CChannelMixer::applySkeletonWeight (uint slot, uint skeleton, bool invert)
00420 {
00421         // Check alot arg
00422         nlassert (slot<NumAnimationSlot);
00423 
00424         // Check the animationSet has been set
00425         nlassert (_AnimationSet);
00426 
00427         // Get the skeleton weight
00428         const CSkeletonWeight *pSkeleton=_AnimationSet->getSkeletonWeight (skeleton);
00429 
00430         // Something to change ?
00431         if ((pSkeleton!=_SlotArray[slot]._SkeletonWeight)||(invert!=_SlotArray[slot]._InvertedSkeletonWeight))
00432         {
00433                 // Set the current skeleton
00434                 _SlotArray[slot]._SkeletonWeight=pSkeleton;
00435                 _SlotArray[slot]._InvertedSkeletonWeight=invert;
00436 
00437                 // Get number of node in the skeleton weight
00438                 uint sizeSkel=pSkeleton->getNumNode ();
00439 
00440                 // For each entry of the skeleton weight
00441                 for (uint n=0; n<sizeSkel; n++)
00442                 {
00443                         // Get the name of the channel for this node
00444                         const string& channelName=pSkeleton->getNodeName (n);
00445 
00446                         // Get the channel Id having the same name than the tracks in this animation set.
00447                         uint channelId=_AnimationSet->getChannelIdByName (channelName);
00448 
00449                         // Tracks exist in this animation set?
00450                         if (channelId!=CAnimationSet::NotFound)
00451                         {
00452                                 // Get the weight of the channel for this node
00453                                 float weight=pSkeleton->getNodeWeight (n);
00454 
00455                                 // Set the weight of this channel for this slot (only if channel setuped!!)
00456                                 std::map<uint, CChannel>::iterator ite=_Channels.find(channelId);
00457                                 if (ite!=_Channels.end())
00458                                         ite->second._Weights[slot]=invert?1.f-weight:weight;
00459                         }
00460                 }
00461         }
00462 }
00463 
00464 // ***************************************************************************
00465 
00466 void CChannelMixer::resetSkeletonWeight (uint slot)
00467 {
00468         // Check alot arg
00469         nlassert (slot<NumAnimationSlot);
00470 
00471         // Something to change ?
00472         if (_SlotArray[slot]._SkeletonWeight!=NULL)
00473         {
00474                 // Set skeleton
00475                 _SlotArray[slot]._SkeletonWeight=NULL;
00476                 _SlotArray[slot]._InvertedSkeletonWeight=false;
00477 
00478                 // For each channels
00479                 map<uint, CChannel>::iterator           itChannel;
00480                 for(itChannel= _Channels.begin(); itChannel!=_Channels.end();itChannel++)
00481                 {
00482                         // Reset
00483                         (*itChannel).second._Weights[slot]=1.f;
00484                 }
00485         }
00486 }
00487 
00488 // ***************************************************************************
00489 
00490 void CChannelMixer::cleanAll ()
00491 {
00492         // For each slot
00493         for (uint s=0; s<NumAnimationSlot; s++)
00494         {
00495                 // Clean it
00496                 _SlotArray[s]._Dirt=false;
00497         }
00498 
00499         // Clean the mixer
00500         _Dirt=false;
00501 }
00502 
00503 // ***************************************************************************
00504 
00505 void CChannelMixer::dirtAll ()
00506 {
00507         // For each slot
00508         for (uint s=0; s<NumAnimationSlot; s++)
00509         {
00510                 // Dirt
00511                 if (!_SlotArray[s].isEmpty())
00512                 {
00513                         // Dirt it
00514                         _SlotArray[s]._Dirt=true;
00515 
00516                         // Dirt the mixer
00517                         _Dirt=true;
00518                 }
00519         }
00520 }
00521 
00522 // ***************************************************************************
00523 
00524 void CChannelMixer::refreshList ()
00525 {
00526         // Setup an array of animation to add
00527         uint numAdd=0;
00528         uint addSlot[NumAnimationSlot];
00529 
00530         // Setup an array of animation that are not empty and stay
00531         uint numStay=0;
00532         uint staySlot[NumAnimationSlot];
00533 
00534         // Setup it up
00535         uint s;
00536         for (s=0; s<NumAnimationSlot; s++)
00537         {
00538                 // Dirt and not empty ? (add)
00539                 if ((_SlotArray[s]._Dirt)&&(!_SlotArray[s].isEmpty()))
00540                         // Add a dirt slot
00541                         addSlot[numAdd++]=s;
00542 
00543                 // Not empty and not dirt ? (stay)
00544                 if ((!_SlotArray[s]._Dirt)&&(!_SlotArray[s].isEmpty()))
00545                         // Add a dirt slot
00546                         staySlot[numStay++]=s;
00547         }
00548 
00549         // Last channel pointer
00550         CChannel **lastPointerGlobal=&_FirstChannelGlobal;
00551         CChannel **lastPointerDetail=&_FirstChannelDetail;
00552 
00553 
00554         // Now scan each channel
00555         map<uint, CChannel>::iterator           itChannel;
00556         for(itChannel= _Channels.begin(); itChannel!=_Channels.end();itChannel++)
00557         {
00558                 CChannel        &channel= (*itChannel).second;
00559 
00560                 // Add this channel to the list if true
00561                 bool add=false;
00562 
00563                 // For each slot to add
00564                 for (s=0; s<numAdd; s++)
00565                 {
00566                         // Find the index of the channel track in the animation set
00567                         uint iDTrack=_SlotArray[addSlot[s]]._Animation->getIdTrackByName (channel._ChannelName);
00568 
00569                         // If this track exist
00570                         if (iDTrack!=CAnimation::NotFound)
00571                         {
00572                                 // Set the track
00573                                 channel._Tracks[addSlot[s]]=_SlotArray[addSlot[s]]._Animation->getTrack (iDTrack);
00574 
00575                                 // Add this channel to the list
00576                                 add=true;
00577                         }
00578                         else
00579                         {
00580                                 // Set the default track
00581                                 channel._Tracks[addSlot[s]]=channel._DefaultTracks;
00582                         }
00583                 }
00584 
00585                 // Add this channel to the list ?
00586                 if (!add)
00587                 {
00588                         // Was it in the list ?
00589                         if (channel._InTheList)
00590                         {
00591                                 // Check if this channel is still in use
00592 
00593                                 // For each slot in the stay list
00594                                 for (s=0; s<numStay; s++)
00595                                 {
00596                                         // Use anything interesting ?
00597                                         if (channel._Tracks[staySlot[s]]!=channel._DefaultTracks)
00598                                         {
00599                                                 // Ok, add it to the list
00600                                                 add=true;
00601 
00602                                                 // Stop
00603                                                 break;
00604                                         }
00605                                 }
00606 
00607                                 // Still in use?
00608                                 if (!add)
00609                                 {
00610                                         // Set it's value to default and touch it's object
00611                                         channel._Value->affect (channel._DefaultTracks->getValue());
00612                                         channel._Object->touch (channel._ValueId, channel._OwnerValueId);
00613                                 }
00614                         }
00615                 }
00616 
00617                 // Do i have to add the channel to the list
00618                 if (add)
00619                 {
00620                         // It is in the list
00621                         channel._InTheList=true;
00622 
00623                         if(channel._Detail)
00624                         {
00625                                 // Set the last pointer value
00626                                 *lastPointerDetail=&channel;
00627                                 // Change last pointer
00628                                 lastPointerDetail=&channel._Next;
00629                         }
00630                         else
00631                         {
00632                                 // Set the last pointer value
00633                                 *lastPointerGlobal=&channel;
00634                                 // Change last pointer
00635                                 lastPointerGlobal=&channel._Next;
00636                         }
00637 
00638                 }
00639                 else
00640                 {
00641                         // It is not in the list
00642                         channel._InTheList=false;
00643                 }
00644         }
00645 
00646         // End of the list
00647         *lastPointerGlobal=NULL;
00648         *lastPointerDetail=NULL;
00649 
00650         // Must recompute the channels to animate.
00651         _ListToEvalDirt= true;
00652 }
00653 
00654 
00655 // ***************************************************************************
00656 void CChannelMixer::refreshListToEval ()
00657 {
00658         CChannel* pChannel;
00659 
00660         /* NB: this save if(), especially when Used with Skeleton, and CLod mode
00661         */
00662 
00663         // Global list.
00664         _GlobalListToEval.clear();
00665         _GlobalListToEval.reserve(_Channels.size());
00666         pChannel=_FirstChannelGlobal;
00667         while(pChannel)
00668         {
00669                 // if the channel is enabled (both user and lod), must eval all active slot.
00670                 if(pChannel->_EnableFlags == CChannel::EnableAllFlag)
00671                         _GlobalListToEval.push_back(pChannel);
00672                 // next
00673                 pChannel= pChannel->_Next;
00674         }
00675 
00676         // Global list.
00677         _DetailListToEval.clear();
00678         _DetailListToEval.reserve(_Channels.size());
00679         pChannel=_FirstChannelDetail;
00680         while(pChannel)
00681         {
00682                 // if the channel is enabled (both user and lod), must eval all active slot.
00683                 if(pChannel->_EnableFlags == CChannel::EnableAllFlag)
00684                         _DetailListToEval.push_back(pChannel);
00685                 // next
00686                 pChannel= pChannel->_Next;
00687         }
00688 
00689         // done
00690         _ListToEvalDirt= false;
00691 }
00692 
00693 // ***************************************************************************
00694 void CChannelMixer::resetEvalDetailDate()
00695 {
00696         _LastEvalDetailDate= -1;
00697 }
00698 
00699 
00700 } // NL3D