452 lines
11 KiB
C++
452 lines
11 KiB
C++
/*
|
|
Object Viewer Qt
|
|
Copyright (C) 2010 Dzmitry Kamiahin <dnk-88@tut.by>
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "stdpch.h"
|
|
#include "entity.h"
|
|
|
|
#include <QtCore/QString>
|
|
|
|
// NeL includes
|
|
#include <nel/misc/path.h>
|
|
#include <nel/3d/u_driver.h>
|
|
#include <nel/3d/u_instance.h>
|
|
#include <nel/3d/u_scene.h>
|
|
#include <nel/3d/u_skeleton.h>
|
|
#include <nel/3d/u_animation_set.h>
|
|
#include <nel/3d/u_animation.h>
|
|
#include <nel/3d/u_play_list_manager.h>
|
|
#include <nel/3d/u_play_list.h>
|
|
#include <nel/3d/u_track.h>
|
|
|
|
// Project includes
|
|
#include "modules.h"
|
|
|
|
using namespace NLMISC;
|
|
using namespace NL3D;
|
|
|
|
namespace NLQT
|
|
{
|
|
|
|
CSlotInfo::CSlotInfo()
|
|
: Animation("empty"),
|
|
Skeleton("empty"),
|
|
Offset(0),
|
|
StartTime(0),
|
|
EndTime(0),
|
|
StartBlend(1),
|
|
EndBlend (1),
|
|
Smoothness(1),
|
|
SpeedFactor(1),
|
|
ClampMode(0),
|
|
SkeletonInverted(false),
|
|
Enable(true)
|
|
{
|
|
}
|
|
|
|
CSlotInfo &CSlotInfo::operator=(const CSlotInfo &slotInfo)
|
|
{
|
|
if ( this != &slotInfo)
|
|
{
|
|
Animation = slotInfo.Animation;
|
|
ClampMode = slotInfo.ClampMode;
|
|
Enable = slotInfo.Enable;
|
|
EndBlend = slotInfo.EndBlend;
|
|
EndTime = slotInfo.EndTime;
|
|
Offset = slotInfo.Offset;
|
|
Skeleton = slotInfo.Skeleton;
|
|
SkeletonInverted = slotInfo.SkeletonInverted;
|
|
Smoothness = slotInfo.Smoothness;
|
|
SpeedFactor = slotInfo.SpeedFactor;
|
|
StartBlend = slotInfo.StartBlend;
|
|
StartTime = slotInfo.StartTime;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
CEntity::CEntity(void)
|
|
: _Name("<Unknown>"),
|
|
_FileNameShape(""),
|
|
_FileNameSkeleton(""),
|
|
_inPlace(false),
|
|
_incPos(false),
|
|
_Instance(NULL),
|
|
_Skeleton(NULL),
|
|
_PlayList(NULL),
|
|
_AnimationSet(NULL)
|
|
{
|
|
_CharacterScalePos = 1;
|
|
}
|
|
|
|
CEntity::~CEntity(void)
|
|
{
|
|
if (_PlayList != NULL)
|
|
{
|
|
_PlayList->resetAllChannels();
|
|
Modules::objView().getPlayListManager()->deletePlayList (_PlayList);
|
|
_PlayList = NULL;
|
|
}
|
|
if (_AnimationSet != NULL)
|
|
{
|
|
Modules::objView().getDriver()->deleteAnimationSet(_AnimationSet);
|
|
_AnimationSet = NULL;
|
|
}
|
|
if (!_Skeleton.empty())
|
|
{
|
|
_Skeleton.detachSkeletonSon(_Instance);
|
|
Modules::objView().getScene()->deleteSkeleton(_Skeleton);
|
|
_Skeleton = NULL;
|
|
}
|
|
if (!_Instance.empty())
|
|
{
|
|
Modules::objView().getScene()->deleteInstance(_Instance);
|
|
_Instance = NULL;
|
|
}
|
|
}
|
|
|
|
void CEntity::loadAnimation(std::string &fileName)
|
|
{
|
|
uint id = _AnimationSet->addAnimation(fileName.c_str(),CFile::getFilenameWithoutExtension(fileName).c_str());
|
|
_AnimationList.push_back(_AnimationSet->getAnimationName(id));
|
|
_AnimationSet->build();
|
|
if (!_Skeleton.empty()) _PlayList->registerTransform(_Skeleton);
|
|
else _PlayList->registerTransform(_Instance);
|
|
}
|
|
|
|
void CEntity::loadSWT(std::string &fileName)
|
|
{
|
|
uint id = _AnimationSet->addSkeletonWeight(fileName.c_str(),CFile::getFilenameWithoutExtension(fileName).c_str());
|
|
_SWTList.push_back(_AnimationSet->getSkeletonWeightName(id));
|
|
}
|
|
|
|
void CEntity::addAnimToPlayList(std::string &name)
|
|
{
|
|
_PlayListAnimation.push_back(name);
|
|
|
|
_AnimationStatus.EndAnim = this->getPlayListLength();
|
|
|
|
_Instance.start();
|
|
}
|
|
|
|
void CEntity::removeAnimToPlayList(uint row)
|
|
{
|
|
if (row < _PlayListAnimation.size())
|
|
_PlayListAnimation.erase(_PlayListAnimation.begin() + row);
|
|
|
|
_AnimationStatus.EndAnim = this->getPlayListLength();
|
|
}
|
|
|
|
void CEntity::swapAnimToPlayList(uint row1, uint row2)
|
|
{
|
|
if ((row1 < _PlayListAnimation.size()) && (row2 < _PlayListAnimation.size()))
|
|
std::swap(_PlayListAnimation[row1], _PlayListAnimation[row2]);
|
|
}
|
|
|
|
void CEntity::playbackAnim(bool play)
|
|
{
|
|
_AnimationStatus.PlayAnim = play;
|
|
|
|
if (play)
|
|
_Instance.start();
|
|
else
|
|
_Instance.freezeHRC();
|
|
}
|
|
|
|
void CEntity::reset()
|
|
{
|
|
_PlayListAnimation.clear();
|
|
_AnimationList.clear();
|
|
_SWTList.clear();
|
|
|
|
_PlayList->resetAllChannels();
|
|
}
|
|
|
|
float CEntity::getPlayListLength()
|
|
{
|
|
// Accumul all the time
|
|
float time = 0;
|
|
for(size_t i = 0; i < _PlayListAnimation.size(); ++i)
|
|
time += getAnimLength(_PlayListAnimation[i]);
|
|
return time;
|
|
}
|
|
|
|
float CEntity::getAnimLength(std::string name)
|
|
{
|
|
uint id = _AnimationSet->getAnimationIdByName(name.c_str());
|
|
NL3D::UAnimation *anim = _AnimationSet->getAnimation(id);
|
|
return anim->getEndTime() - anim->getBeginTime();
|
|
}
|
|
|
|
void CEntity::update(NL3D::TAnimationTime time)
|
|
{
|
|
this->resetChannel();
|
|
switch (_AnimationStatus.Mode)
|
|
{
|
|
case Mode::PlayList:
|
|
animatePlayList(time);
|
|
break;
|
|
case Mode::Mixer:
|
|
animateChannelMixer();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void CEntity::resetChannel()
|
|
{
|
|
for(uint i = 0; i < NL3D::CChannelMixer::NumAnimationSlot; ++i)
|
|
_PlayList->setAnimation(i, UPlayList::empty);
|
|
}
|
|
|
|
void CEntity::addTransformation (CMatrix ¤t, UAnimation *anim, float begin, float end, UTrack *posTrack, UTrack *rotquatTrack,
|
|
UTrack *nextPosTrack, UTrack *nextRotquatTrack, bool removeLast)
|
|
{
|
|
// In place ?
|
|
if (_inPlace)
|
|
{
|
|
// Just identity
|
|
current.identity();
|
|
}
|
|
else
|
|
{
|
|
// Remove the start of the animation
|
|
CQuat rotEnd (0,0,0,1);
|
|
CVector posEnd (0,0,0);
|
|
if (rotquatTrack)
|
|
{
|
|
// Interpolate the rotation
|
|
rotquatTrack->interpolate (end, rotEnd);
|
|
}
|
|
if (posTrack)
|
|
{
|
|
// Interpolate the position
|
|
posTrack->interpolate (end, posEnd);
|
|
}
|
|
|
|
// Add the final rotation and position
|
|
CMatrix tmp;
|
|
tmp.identity ();
|
|
tmp.setRot (rotEnd);
|
|
tmp.setPos (posEnd);
|
|
|
|
// Incremental ?
|
|
if (_incPos)
|
|
current *= tmp;
|
|
else
|
|
current = tmp;
|
|
|
|
if (removeLast)
|
|
{
|
|
CQuat rotStart (0,0,0,1);
|
|
CVector posStart (0,0,0);
|
|
if (nextRotquatTrack)
|
|
{
|
|
// Interpolate the rotation
|
|
nextRotquatTrack->interpolate (begin, rotStart);
|
|
}
|
|
if (nextPosTrack)
|
|
{
|
|
// Interpolate the position
|
|
nextPosTrack->interpolate (begin, posStart);
|
|
}
|
|
// Remove the init rotation and position of the next animation
|
|
tmp.identity ();
|
|
tmp.setRot (rotStart);
|
|
tmp.setPos (posStart);
|
|
tmp.invert ();
|
|
current *= tmp;
|
|
|
|
// Normalize the mt
|
|
CVector I = current.getI ();
|
|
CVector J = current.getJ ();
|
|
I.z = 0;
|
|
J.z = 0;
|
|
J.normalize ();
|
|
CVector K = I^J;
|
|
K.normalize ();
|
|
I = J^K;
|
|
I.normalize ();
|
|
tmp.setRot (I, J, K);
|
|
tmp.setPos (current.getPos ());
|
|
current = tmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEntity::animatePlayList(NL3D::TAnimationTime time)
|
|
{
|
|
if (!_PlayListAnimation.empty())
|
|
{
|
|
// Animation index
|
|
uint id = _AnimationSet->getAnimationIdByName(_PlayListAnimation[0].c_str());
|
|
|
|
// Try channel AnimationSet
|
|
NL3D::UAnimation *anim = _AnimationSet->getAnimation(id);
|
|
|
|
bool there = false;
|
|
|
|
UTrack *posTrack = NULL;
|
|
UTrack *rotQuatTrack = NULL;
|
|
|
|
// Current matrix
|
|
CMatrix current;
|
|
current.identity();
|
|
|
|
// read an animation for init matrix
|
|
rotQuatTrack = anim->getTrackByName("rotquat");
|
|
posTrack = anim->getTrackByName("pos");
|
|
|
|
there = posTrack || rotQuatTrack;
|
|
|
|
// Accumul time
|
|
float startTime = 0;
|
|
float endTime = anim->getEndTime() - anim->getBeginTime();
|
|
|
|
uint index = 0;
|
|
while (time >= endTime)
|
|
{
|
|
index++;
|
|
if (index < _PlayListAnimation.size())
|
|
{
|
|
id = _AnimationSet->getAnimationIdByName(_PlayListAnimation[index].c_str());
|
|
NL3D::UAnimation *newAnim = _AnimationSet->getAnimation(id);
|
|
|
|
UTrack *newPosTrack = newAnim->getTrackByName ("pos");
|
|
UTrack *newRotquatTrack = newAnim->getTrackByName ("rotquat");
|
|
|
|
// Add the transformation
|
|
addTransformation (current, anim, newAnim->getBeginTime(), anim->getEndTime(), posTrack, rotQuatTrack, newPosTrack, newRotquatTrack, true);
|
|
|
|
|
|
anim = newAnim;
|
|
posTrack = newPosTrack;
|
|
rotQuatTrack = newRotquatTrack;
|
|
|
|
// Add start time
|
|
startTime = endTime;
|
|
endTime = startTime + (anim->getEndTime() - anim->getBeginTime());
|
|
}
|
|
else
|
|
{
|
|
// Add the transformation
|
|
addTransformation (current, anim, 0, anim->getEndTime(), posTrack, rotQuatTrack, NULL, NULL, false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Time cropped ?
|
|
if (index >= _PlayListAnimation.size())
|
|
{
|
|
// Yes
|
|
index--;
|
|
id = _AnimationSet->getAnimationIdByName(_PlayListAnimation[index].c_str());
|
|
anim = _AnimationSet->getAnimation(id);
|
|
|
|
// End time for last anim
|
|
startTime = anim->getEndTime() - time;
|
|
}
|
|
else
|
|
{
|
|
// No
|
|
|
|
// Add the transformation
|
|
addTransformation (current, anim, 0, anim->getBeginTime() + time - startTime, posTrack, rotQuatTrack, NULL, NULL, false);
|
|
|
|
id = _AnimationSet->getAnimationIdByName(_PlayListAnimation[index].c_str());
|
|
anim = _AnimationSet->getAnimation(id);
|
|
|
|
// Final time
|
|
startTime -= anim->getBeginTime();
|
|
}
|
|
|
|
// Set the slot
|
|
_PlayList->setAnimation(0, id);
|
|
_PlayList->setTimeOrigin(0, startTime);
|
|
_PlayList->setSpeedFactor(0, 1.0f);
|
|
_PlayList->setWeightSmoothness(0, 1.0f);
|
|
_PlayList->setStartWeight(0, 1, 0);
|
|
_PlayList->setEndWeight(0, 1, 1);
|
|
_PlayList->setWrapMode(0, UPlayList::Clamp);
|
|
|
|
// Setup the pos and rot for this shape
|
|
if (there)
|
|
{
|
|
CVector pos = current.getPos();
|
|
|
|
// If a skeleton model
|
|
if(!_Skeleton.empty())
|
|
{
|
|
// scale animated pos value with the CFG scale
|
|
pos *= _CharacterScalePos;
|
|
_Skeleton.setPos(pos);
|
|
_Skeleton.setRotQuat(current.getRot());
|
|
}
|
|
else
|
|
{
|
|
_Instance.setPos(pos);
|
|
_Instance.setRotQuat(current.getRot());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEntity::animateChannelMixer()
|
|
{
|
|
for (uint i = 0; i < NL3D::CChannelMixer::NumAnimationSlot; ++i)
|
|
{
|
|
if (_SlotInfo[i].Enable)
|
|
{
|
|
// Set the animation
|
|
uint animId = _AnimationSet->getAnimationIdByName(_SlotInfo[i].Animation);
|
|
if (animId == UAnimationSet::NotFound)
|
|
_PlayList->setAnimation(i, UPlayList::empty);
|
|
else
|
|
_PlayList->setAnimation(i, animId);
|
|
|
|
// Set the skeleton weight
|
|
uint skelId = _AnimationSet->getSkeletonWeightIdByName(_SlotInfo[i].Skeleton);
|
|
if (skelId == UAnimationSet::NotFound)
|
|
_PlayList->setSkeletonWeight(i, UPlayList::empty, false);
|
|
else
|
|
_PlayList->setSkeletonWeight(i, skelId, _SlotInfo[i].SkeletonInverted);
|
|
|
|
// Set others values
|
|
_PlayList->setTimeOrigin(i, _SlotInfo[i].Offset);
|
|
_PlayList->setSpeedFactor(i, _SlotInfo[i].SpeedFactor);
|
|
_PlayList->setStartWeight(i, _SlotInfo[i].StartBlend, _SlotInfo[i].StartTime);
|
|
_PlayList->setEndWeight(i, _SlotInfo[i].EndBlend, _SlotInfo[i].EndTime);
|
|
_PlayList->setWeightSmoothness(i, _SlotInfo[i].Smoothness);
|
|
|
|
// Switch between wrap modes
|
|
switch (_SlotInfo[i].ClampMode)
|
|
{
|
|
case 0:
|
|
_PlayList->setWrapMode (i, UPlayList::Clamp);
|
|
break;
|
|
case 1:
|
|
_PlayList->setWrapMode (i, UPlayList::Repeat);
|
|
break;
|
|
case 2:
|
|
_PlayList->setWrapMode (i, UPlayList::Disable);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* namespace NLQT */
|