2019-12-12 17:32:41 +01:00
|
|
|
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
|
|
|
// This file is part of the "Irrlicht Engine".
|
|
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
|
|
|
|
#include "CSceneNodeAnimatorFollowSpline.h"
|
|
|
|
|
|
|
|
namespace irr
|
|
|
|
{
|
|
|
|
namespace scene
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
//! constructor
|
|
|
|
CSceneNodeAnimatorFollowSpline::CSceneNodeAnimatorFollowSpline(u32 time,
|
|
|
|
const core::array<core::vector3df>& points, f32 speed,
|
2021-08-20 18:39:37 +02:00
|
|
|
f32 tightness, bool loop, bool pingpong, bool steer)
|
2019-12-12 17:32:41 +01:00
|
|
|
: ISceneNodeAnimatorFinishing(0), Points(points), Speed(speed), Tightness(tightness)
|
2021-08-20 18:39:37 +02:00
|
|
|
, Loop(loop), PingPong(pingpong), Steer(steer)
|
2019-12-12 17:32:41 +01:00
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
|
|
|
setDebugName("CSceneNodeAnimatorFollowSpline");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
StartTime = time;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline s32 CSceneNodeAnimatorFollowSpline::clamp(s32 idx, s32 size)
|
|
|
|
{
|
|
|
|
return ( idx<0 ? size+idx : ( idx>=size ? idx-size : idx ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! animates a scene node
|
|
|
|
void CSceneNodeAnimatorFollowSpline::animateNode(ISceneNode* node, u32 timeMs)
|
|
|
|
{
|
|
|
|
if(!node)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const u32 pSize = Points.size();
|
|
|
|
if (pSize==0)
|
|
|
|
{
|
|
|
|
if ( !Loop )
|
|
|
|
HasFinished = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (pSize==1)
|
|
|
|
{
|
|
|
|
if ( timeMs > (StartTime+PauseTimeSum) )
|
|
|
|
{
|
|
|
|
node->setPosition(Points[0]);
|
|
|
|
if ( !Loop )
|
|
|
|
HasFinished = true;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const f32 dt = ( (timeMs-(StartTime+PauseTimeSum)) * Speed * 0.001f );
|
|
|
|
const s32 unwrappedIdx = core::floor32( dt );
|
|
|
|
if ( !Loop && unwrappedIdx >= (s32)pSize-1 )
|
|
|
|
{
|
|
|
|
node->setPosition(Points[pSize-1]);
|
|
|
|
HasFinished = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const bool pong = PingPong && (unwrappedIdx/(pSize-1))%2;
|
|
|
|
const f32 u = pong ? 1.f-core::fract ( dt ) : core::fract ( dt );
|
|
|
|
const s32 idx = pong ? (pSize-2) - (unwrappedIdx % (pSize-1))
|
|
|
|
: (PingPong ? unwrappedIdx % (pSize-1)
|
|
|
|
: unwrappedIdx % pSize);
|
|
|
|
//const f32 u = 0.001f * fmodf( dt, 1000.0f );
|
|
|
|
|
|
|
|
const core::vector3df& p0 = Points[ clamp( idx - 1, pSize ) ];
|
|
|
|
const core::vector3df& p1 = Points[ clamp( idx + 0, pSize ) ]; // starting point
|
|
|
|
const core::vector3df& p2 = Points[ clamp( idx + 1, pSize ) ]; // end point
|
|
|
|
const core::vector3df& p3 = Points[ clamp( idx + 2, pSize ) ];
|
|
|
|
|
|
|
|
// hermite polynomials
|
|
|
|
const f32 h1 = 2.0f * u * u * u - 3.0f * u * u + 1.0f;
|
|
|
|
const f32 h2 = -2.0f * u * u * u + 3.0f * u * u;
|
|
|
|
const f32 h3 = u * u * u - 2.0f * u * u + u;
|
|
|
|
const f32 h4 = u * u * u - u * u;
|
|
|
|
|
|
|
|
// tangents
|
|
|
|
const core::vector3df t1 = ( p2 - p0 ) * Tightness;
|
|
|
|
const core::vector3df t2 = ( p3 - p1 ) * Tightness;
|
|
|
|
|
|
|
|
// interpolated point
|
2021-08-20 18:39:37 +02:00
|
|
|
const core::vector3df lastPos(node->getPosition());
|
|
|
|
const core::vector3df pos(p1 * h1 + p2 * h2 + t1 * h3 + t2 * h4);
|
|
|
|
node->setPosition(pos);
|
|
|
|
|
|
|
|
// steering (rotate in direction of movement)
|
|
|
|
if (Steer && !pos.equals(lastPos)) // equality check fixes glitches due to very high frame rates
|
|
|
|
{
|
|
|
|
const core::vector3df toTarget(pos - lastPos);
|
|
|
|
const core::vector3df requiredRotation = toTarget.getHorizontalAngle();
|
|
|
|
node->setRotation(requiredRotation);
|
|
|
|
}
|
2019-12-12 17:32:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Writes attributes of the scene node animator.
|
|
|
|
void CSceneNodeAnimatorFollowSpline::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
|
|
|
|
{
|
|
|
|
ISceneNodeAnimatorFinishing::serializeAttributes(out, options);
|
|
|
|
|
|
|
|
out->addFloat("Speed", Speed);
|
|
|
|
out->addFloat("Tightness", Tightness);
|
|
|
|
out->addBool("Loop", Loop);
|
|
|
|
out->addBool("PingPong", PingPong);
|
2021-08-20 18:39:37 +02:00
|
|
|
out->addBool("Steer", Steer);
|
2019-12-12 17:32:41 +01:00
|
|
|
|
|
|
|
u32 count = Points.size();
|
|
|
|
|
|
|
|
if ( options && (options->Flags & io::EARWF_FOR_EDITOR))
|
|
|
|
{
|
|
|
|
// add one point in addition when serializing for editors
|
|
|
|
// to make it easier to add points quickly
|
|
|
|
count += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (u32 i=0; i<count; ++i)
|
|
|
|
{
|
|
|
|
core::stringc tname = "Point";
|
|
|
|
tname += (int)(i+1);
|
|
|
|
|
|
|
|
out->addVector3d(tname.c_str(), i<Points.size() ? Points[i] : core::vector3df(0,0,0) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Reads attributes of the scene node animator.
|
|
|
|
void CSceneNodeAnimatorFollowSpline::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
|
|
|
|
{
|
|
|
|
ISceneNodeAnimatorFinishing::deserializeAttributes(in, options);
|
|
|
|
|
2021-08-20 18:39:37 +02:00
|
|
|
Speed = in->getAttributeAsFloat("Speed", Speed);
|
|
|
|
Tightness = in->getAttributeAsFloat("Tightness", Tightness);
|
|
|
|
Loop = in->getAttributeAsBool("Loop", Loop);
|
|
|
|
PingPong = in->getAttributeAsBool("PingPong", PingPong);
|
|
|
|
Steer = in->getAttributeAsBool("Steer", Steer);
|
2019-12-12 17:32:41 +01:00
|
|
|
Points.clear();
|
|
|
|
|
|
|
|
for(u32 i=1; true; ++i)
|
|
|
|
{
|
|
|
|
core::stringc pname = "Point";
|
|
|
|
pname += i;
|
|
|
|
|
|
|
|
if (in->existsAttribute(pname.c_str()))
|
|
|
|
Points.push_back(in->getAttributeAsVector3d(pname.c_str()));
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove last point if double entry from editor
|
|
|
|
if ( options && (options->Flags & io::EARWF_FOR_EDITOR) &&
|
|
|
|
Points.size() > 2 && Points.getLast() == core::vector3df(0,0,0))
|
|
|
|
{
|
|
|
|
Points.erase(Points.size()-1);
|
|
|
|
|
|
|
|
if (Points.size() > 2 && Points.getLast() == core::vector3df(0,0,0))
|
|
|
|
Points.erase(Points.size()-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ISceneNodeAnimator* CSceneNodeAnimatorFollowSpline::createClone(ISceneNode* node, ISceneManager* newManager)
|
|
|
|
{
|
|
|
|
CSceneNodeAnimatorFollowSpline * newAnimator =
|
|
|
|
new CSceneNodeAnimatorFollowSpline(StartTime, Points, Speed, Tightness);
|
|
|
|
newAnimator->cloneMembers(this);
|
|
|
|
|
|
|
|
return newAnimator;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // end namespace scene
|
|
|
|
} // end namespace irr
|
|
|
|
|