khanat-opennel-code/code/nel/src/3d/animation_playlist.cpp

353 lines
9.6 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "std3d.h"
#include "nel/3d/animation_playlist.h"
#include "nel/misc/common.h"
#include "nel/misc/stream.h"
using namespace NLMISC;
namespace NL3D
{
// ***************************************************************************
CAnimationPlaylist::CAnimationPlaylist()
{
// Empty the playlist
emptyPlayList ();
// Set default wrap mode
for (uint i=0; i<CChannelMixer::NumAnimationSlot; i++)
_WrapMode[i]=Clamp;
}
// ***************************************************************************
void CAnimationPlaylist::emptyPlayList ()
{
// Empty each slot
for (uint i=0; i<CChannelMixer::NumAnimationSlot; i++)
{
_Animations[i]=empty;
_SkeletonWeight[i]=empty;
_InvertWeight[i]=false;
_TimeOrigin[i]=0.f;
_SpeedFactor[i]=1.f;
_StartWeight[i]=1.f;
_EndWeight[i]=1.f;
_StartWeightTime[i]= 0.f;
_EndWeightTime[i]= 0.f;
_Smoothness[i]= 0.f;
}
}
// ***************************************************************************
void CAnimationPlaylist::setAnimation (uint8 slot, uint animation)
{
_Animations[slot]=animation;
}
// ***************************************************************************
uint CAnimationPlaylist::getAnimation (uint8 slot) const
{
return _Animations[slot];
}
// ***************************************************************************
void CAnimationPlaylist::setSkeletonWeight (uint8 slot, uint skeletonId, bool inverted)
{
_SkeletonWeight[slot]=skeletonId;
_InvertWeight[slot]=inverted;
}
// ***************************************************************************
uint CAnimationPlaylist::getSkeletonWeight (uint8 slot, bool &inverted) const
{
inverted=_InvertWeight[slot];
return _SkeletonWeight[slot];
}
// ***************************************************************************
void CAnimationPlaylist::setTimeOrigin (uint8 slot, TGlobalAnimationTime timeOrigin)
{
_TimeOrigin[slot]=timeOrigin;
}
// ***************************************************************************
TGlobalAnimationTime CAnimationPlaylist::getTimeOrigin (uint8 slot) const
{
return _TimeOrigin[slot];
}
// ***************************************************************************
void CAnimationPlaylist::setSpeedFactor (uint8 slot, float speedFactor)
{
_SpeedFactor[slot]=speedFactor;
}
// ***************************************************************************
float CAnimationPlaylist::getSpeedFactor (uint8 slot) const
{
return _SpeedFactor[slot];
}
// ***************************************************************************
void CAnimationPlaylist::setStartWeight (uint8 slot, float startWeight, TGlobalAnimationTime time)
{
_StartWeight[slot]=startWeight;
_StartWeightTime[slot]=time;
}
// ***************************************************************************
float CAnimationPlaylist::getStartWeight (uint8 slot, TGlobalAnimationTime& time) const
{
time=_StartWeightTime[slot];
return _StartWeight[slot];
}
// ***************************************************************************
void CAnimationPlaylist::setEndWeight (uint8 slot, float endWeight, TGlobalAnimationTime time)
{
_EndWeight[slot]=endWeight;
_EndWeightTime[slot]=time;
}
// ***************************************************************************
float CAnimationPlaylist::getEndWeight (uint8 slot, TGlobalAnimationTime& time) const
{
time=_EndWeightTime[slot];
return _EndWeight[slot];
}
// ***************************************************************************
void CAnimationPlaylist::setWeightSmoothness (uint8 slot, float smoothness)
{
_Smoothness[slot]=smoothness;
}
// ***************************************************************************
float CAnimationPlaylist::getWeightSmoothness (uint8 slot) const
{
return _Smoothness[slot];
}
// ***************************************************************************
void CAnimationPlaylist::setupMixer (CChannelMixer& mixer, TGlobalAnimationTime time) const
{
// For each slot
for (uint8 s=0; s<CChannelMixer::NumAnimationSlot; s++)
{
// *** Set the time
// Animation enabled
bool enabled=true;
// Get the animationSet pointer from the mixer
const CAnimationSet *animSet=mixer.getAnimationSet ();
// If is exists
if (animSet)
{
if (_Animations[s]!=empty)
{
// Get the local time
TAnimationTime wrappedTime = getLocalTime (s, time, *animSet);
// Get the animation
const CAnimation *pAnimation=animSet->getAnimation (_Animations[s]);
// Disable mode ?
if ((_WrapMode[s]==Disable)&&((wrappedTime<pAnimation->getBeginTime ())||(wrappedTime>pAnimation->getEndTime ())))
enabled=false;
// Set the time
if (enabled)
mixer.setSlotTime (s, wrappedTime);
}
}
// *** Set the animation
// Still enabled
if (enabled)
{
// empty ?
if (_Animations[s]==empty)
// Empty the slot
mixer.emptySlot (s);
else
// Set the animation id
mixer.setSlotAnimation (s, _Animations[s]);
// *** Set the skeleton weight
// empty ?
if (_SkeletonWeight[s]==empty)
// Empty the slot
mixer.resetSkeletonWeight (s);
else
// Set the animation id
mixer.applySkeletonWeight (s, _SkeletonWeight[s], _InvertWeight[s]);
// *** Set the weight
mixer.setSlotWeight (s, getWeightValue (_StartWeightTime[s], _EndWeightTime[s], time, _StartWeight[s], _EndWeight[s], _Smoothness[s]));
}
else
{
// Disable this slot
mixer.emptySlot (s);
}
}
}
// ***************************************************************************
float CAnimationPlaylist::getWeightValue (TGlobalAnimationTime startWeightTime, TGlobalAnimationTime endWeightTime, TGlobalAnimationTime time, float startWeight, float endWeight, float smoothness)
{
// Clamp left
if (time<=startWeightTime)
return startWeight;
// Clamp left
if (time>=endWeightTime)
return endWeight;
// *** Interpolate
// Linear value
TGlobalAnimationTime linear=startWeight+(endWeight-startWeight)*(time-startWeightTime)/(endWeightTime-startWeightTime);
// Linear ?
if (smoothness<0.0001f)
return (float)linear;
// Quadratic value
double a=2.f*startWeight-2.f*endWeight;
double b=3.f*endWeight-3.f*startWeight;
double x=(time-startWeightTime)/(endWeightTime-startWeightTime);
double xSquare=x*x;
double xCube=x*xSquare;
double quad=a*xCube+b*xSquare+startWeight;
// Interpolate between linear and quadratic
return (float)(smoothness*quad+(1.f-smoothness)*linear);
}
// ***************************************************************************
void CAnimationPlaylist::setWrapMode (uint8 slot, TWrapMode wrapMode)
{
_WrapMode[slot]=wrapMode;
}
// ***************************************************************************
CAnimationPlaylist::TWrapMode CAnimationPlaylist::getWrapMode (uint8 slot) const
{
return _WrapMode[slot];
}
// ***************************************************************************
void CAnimationPlaylist::serial (NLMISC::IStream& f)
{
// Serial a version
(void)f.serialVersion (0);
// Serial all the values
for (uint i=0; i<CChannelMixer::NumAnimationSlot; i++)
{
f.serial (_Animations[i]);
f.serial (_SkeletonWeight[i]);
f.serial (_InvertWeight[i]);
f.serial (_TimeOrigin[i]);
f.serial (_SpeedFactor[i]);
f.serial (_StartWeight[i]);
f.serial (_StartWeightTime[i]);
f.serial (_EndWeight[i]);
f.serial (_EndWeightTime[i]);
f.serial (_Smoothness[i]);
f.serialEnum (_WrapMode[i]);
}
}
// ***************************************************************************
TAnimationTime CAnimationPlaylist::getLocalTime (uint8 slot, TGlobalAnimationTime globalTime, const CAnimationSet& animSet) const
{
// Get the animation
const CAnimation *pAnimation=animSet.getAnimation (_Animations[slot]);
// If this animation exists
if (pAnimation)
{
// Compute the non-wrapped time
TAnimationTime wrappedTime=pAnimation->getBeginTime ()+(TAnimationTime)((globalTime-_TimeOrigin[slot])*_SpeedFactor[slot]);
// Wrap mode
switch (_WrapMode[slot])
{
case Clamp:
clamp (wrappedTime, pAnimation->getBeginTime (), pAnimation->getEndTime ());
break;
case Repeat:
// Mod repeat the time
{
float length=pAnimation->getEndTime ()-pAnimation->getBeginTime();
if (wrappedTime>=pAnimation->getBeginTime())
wrappedTime=pAnimation->getBeginTime()+(float)fmod (wrappedTime-pAnimation->getBeginTime(), length);
else
wrappedTime=pAnimation->getBeginTime()+(float)fmod (wrappedTime-pAnimation->getBeginTime(), length)+length;
}
break;
default: break;
}
// Return localTime
return wrappedTime;
}
return (TAnimationTime)globalTime;
}
// ***************************************************************************
float CAnimationPlaylist::getLocalWeight (uint8 slot, TGlobalAnimationTime globalTime) const
{
return getWeightValue (_StartWeightTime[slot], _EndWeightTime[slot], globalTime, _StartWeight[slot], _EndWeight[slot], _Smoothness[slot]);
}
// ***************************************************************************
} // NL3D