From 34241247df50c41686c91beee0adac6ae8b385ce Mon Sep 17 00:00:00 2001 From: Lukas Aldershaab Date: Wed, 16 Dec 2020 07:24:20 +0100 Subject: [PATCH] Add Tween --- Engine/source/T3D/tween.cpp | 413 ++++++++++++++++++++++++++++++++++++ Engine/source/T3D/tween.h | 67 ++++++ 2 files changed, 480 insertions(+) create mode 100644 Engine/source/T3D/tween.cpp create mode 100644 Engine/source/T3D/tween.h diff --git a/Engine/source/T3D/tween.cpp b/Engine/source/T3D/tween.cpp new file mode 100644 index 0000000000..ac8b2fd9d5 --- /dev/null +++ b/Engine/source/T3D/tween.cpp @@ -0,0 +1,413 @@ +#include "tween.h" + +#include "T3D\gameBase\gameBase.h" + +IMPLEMENT_CONOBJECT(Tween); + +//------------------------------------------- +// Tween +//------------------------------------------- + +Tween::Tween() +{ + mDuration = 0.f; + mCurrentTime = 0.f; + mPreviousTime = 0.f; + mTarget = NULL; + mValueName = NULL; + mValueTarget = 0.f; + mCurrentValue = 0.f; + mEaseDirection = Ease::InOut; + mEaseType = Ease::Linear; + mState = TweenState::Idle; +} + +bool Tween::onAdd() +{ + if (!Parent::onAdd()) + return false; + + return true; +} + +void Tween::initPersistFields() +{ + addField("Duration", TypeF32, Offset(mDuration, Tween), ""); + addField("Target", TYPEID< SimObject >(), Offset(mTarget, Tween), ""); + addField("ValueName", TYPEID(), Offset(mValueName, Tween), ""); + addField("ValueTarget", TypeF32, Offset(mValueTarget, Tween), ""); + addField("EaseDirection", TypeS32, Offset(mEaseDirection, Tween), ""); + addField("EaseType", TypeS32, Offset(mEaseType, Tween), ""); + + Parent::initPersistFields(); +} + +bool Tween::setTargetProperty(void* obj, const char* index, const char* value) +{ + if (value == NULL || !value || !value[0]) + { + Con::errorf("Tween::setDataBlockProperty - Can't unset target on Tween objects"); + return false; + } + + Tween* object = static_cast(obj); + SimObject* target; + if (Sim::findObject(value, target)) { + object->mTarget = target; + return true; + } + + Con::errorf("Tween::setTargetProperty - Could not find target \"%s\"", value); + return false; +} + +void Tween::advanceTime(F32 time) +{ + if (!mTarget || mTarget->isDeleted()) { + mTarget = NULL; + return; + } + switch (mState) + { + case Tween::Idle: + break; + case Tween::Playing: + SetValueByTime(mCurrentTime + time); + if (mCurrentTime >= mDuration) + { + mCurrentTime = mDuration; + SetValueByTime(mDuration); + mState = Tween::Idle; + } + break; + case Tween::Paused: + break; + case Tween::PlayingReversed: + SetValueByTime(mCurrentTime - time); + if (mCurrentTime <= 0.0f) + { + mCurrentTime = 0.0f; + SetValueByTime(0); + mState = Tween::Idle; + } + break; + default: + break; + } +} + +void Tween::Play() +{ + mState = TweenState::Playing; +} + +void Tween::Pause() +{ + mState = TweenState::Paused; +} + +void Tween::Rewind() +{ + mPreviousTime = 0; + mCurrentTime = 0; + SetValueByTime(0); +} + +void Tween::Reverse() +{ + mState = TweenState::PlayingReversed; +} + +void Tween::SetValueByTime(F32 time) +{ + mPreviousTime = mCurrentTime; + mCurrentTime = time; + EaseF ease = EaseF(mEaseDirection, mEaseType); + F32 d = mDuration; + F32 newValue = ease.getValue(mCurrentTime, 0, mValueTarget, d); + F32 deltaValue = newValue - mCurrentValue; + if (mTarget) + SetTargetField(deltaValue); + else + SetGlobalField(deltaValue); +} + +void Tween::SetGlobalField(F32 value) +{ + char buffer[6]; + dSprintf(buffer, sizeof(buffer), "%f", value); + Con::setVariable(mValueName, buffer); +} + +bool Tween::SetTargetField(F32 value) +{ + int size = strlen(mValueName); + + // Syntactic sugar for SceneObjects. + SceneObject* obj = dynamic_cast((SimObject*)mTarget); + if (obj && SetSceneObjectTargetField(obj, value)) + { + return true;; + } + + GuiControl* guiCtrl = dynamic_cast((SimObject*)mTarget); + if (guiCtrl && SetGuiControlTargetField(guiCtrl, value)) + { + return true; + } + + F32 oldValue = dAtof(mTarget->getDataField(mValueName, NULL)); + char buffer[18]; + dSprintf(buffer, sizeof(buffer), "%f", oldValue + value); + if (size <= 0) + { + Con::errorf("ValueName not set, pausing Tween %s", getId()); + Pause(); + return true; + } + mTarget->setDataField(mValueName, NULL, buffer); + F32 newValue = dAtof(mTarget->getDataField(mValueName, NULL)); + + mCurrentValue = newValue - oldValue; + + return true; +} + + +bool Tween::SetSceneObjectTargetField(SceneObject* obj, F32 value) +{ + int size = strlen(mValueName); + + switch (mValueName[0]) + { + // Position BEGIN ----- + case 'x': + case 'X': + if (size == 1) + { + Point3F pos = obj->getPosition(); + obj->setPosition(Point3F(pos.x + value, pos.y, pos.z)); + mCurrentValue += value; + return true; + } + break; + case 'y': + case 'Y': + if (size == 1) + { + Point3F pos = obj->getPosition(); + obj->setPosition(Point3F(pos.x, pos.y + value, pos.z)); + mCurrentValue += value; + return true; + } + break; + case 'z': + case 'Z': + if (size == 1) + { + Point3F pos = obj->getPosition(); + obj->setPosition(Point3F(pos.x, pos.y, pos.z + value)); + mCurrentValue += value; + return true; + } + break; + // Position END ----- + // Rotation BEGIN ----- + case 'R': + case 'r': + switch (mValueName[1]) + { + case 'x': + case 'X': + if (size == 2) + { + EulerF euler = obj->getTransform().toEuler(); + euler = Point3F(euler.x + value, euler.y, euler.z); + obj->setTransform(MatrixF(euler)); + mCurrentValue += value; + return true; + } + break; + case 'y': + case 'Y': + if (size == 2) + { + EulerF euler = obj->getTransform().toEuler(); + euler = Point3F(euler.x, euler.y + value, euler.z); + obj->setTransform(MatrixF(euler)); + mCurrentValue += value; + return true; + } + break; + case 'z': + case 'Z': + if (size == 2) + { + EulerF euler = obj->getTransform().toEuler(); + euler = Point3F(euler.x, euler.y, euler.z + value); + obj->setTransform(MatrixF(euler)); + mCurrentValue += value; + return true; + } + break; + default: + break; + } + break; + // Rotation END ----- + // Scale BEGIN ----- + case 's': + case 'S': + switch (mValueName[1]) + { + case 'x': + case 'X': + if (size == 2) + { + VectorF scale = obj->getScale(); + obj->setScale(VectorF(scale.x + value, scale.y, scale.z)); + mCurrentValue += value; + return true; + } + break; + case 'y': + case 'Y': + if (size == 2) + { + VectorF scale = obj->getScale(); + obj->setScale(VectorF(scale.x, scale.y + value, scale.z)); + mCurrentValue += value; + return true; + } + break; + case 'z': + case 'Z': + if (size == 2) + { + VectorF scale = obj->getScale(); + obj->setScale(VectorF(scale.z, scale.y, scale.z + value)); + mCurrentValue += value; + return true; + } + break; + default: + break; + } + break; + // Scale END ----- + default: + break; + } + + return false; +} + +bool Tween::SetGuiControlTargetField(GuiControl* obj, F32 value) +{ + int size = strlen(mValueName); + + switch (mValueName[0]) + { + // Position BEGIN ----- + case 'x': + case 'X': + if (size == 1) + { + obj->setPosition(obj->getPosition().x + S32(value), obj->getPosition().y); + mCurrentValue += S32(value); + return true; + } + break; + case 'y': + case 'Y': + if (size == 1) + { + obj->setPosition(obj->getPosition().x, obj->getPosition().y + S32(value)); + mCurrentValue += S32(value); + return true; + } + break; + // Scale BEGIN ----- + case 's': + case 'S': + switch (mValueName[1]) + { + case 'x': + case 'X': + if (size == 2) + { + obj->setExtent(obj->getExtent().x + S32(value), obj->getExtent().y); + mCurrentValue += S32(value); + return true; + } + break; + case 'y': + case 'Y': + if (size == 2) + { + obj->setExtent(obj->getExtent().x, obj->getExtent().y + S32(value)); + mCurrentValue += S32(value); + return true; + } + break; + default: + break; + } + break; + // Scale END ----- + // Min Scale BEGIN ----- + case 'm': + case 'M': + switch (mValueName[1]) + { + case 'x': + case 'X': + if (size == 2) + { + obj->setExtent(obj->getMinExtent().x + S32(value), obj->getMinExtent().y); + mCurrentValue += S32(value); + return true; + } + break; + case 'y': + case 'Y': + if (size == 2) + { + obj->setExtent(obj->getMinExtent().x, obj->getMinExtent().y + S32(value)); + mCurrentValue += S32(value); + return true; + } + break; + default: + break; + } + break; + // Min Scale END ----- + default: + break; + } + + return false; +} + +IMPLEMENT_CALLBACK(Tween, onFinished, void, (), (), ""); + +DefineEngineMethod(Tween, Play, void, (), , "") +{ + object->Play(); +} + +DefineEngineMethod(Tween, Pause, void, (), , "") +{ + object->Pause(); +} + +DefineEngineMethod(Tween, Rewind, void, (), , "") +{ + object->Rewind(); +} + +DefineEngineMethod(Tween, Reverse, void, (), , "") +{ + object->Reverse(); +} diff --git a/Engine/source/T3D/tween.h b/Engine/source/T3D/tween.h new file mode 100644 index 0000000000..5b44d12c35 --- /dev/null +++ b/Engine/source/T3D/tween.h @@ -0,0 +1,67 @@ +#ifndef TWEEN_ENGINE_H +#define TWEEN_ENGINE_H + +#include "platform/platform.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "console/simObject.h" +#include "core\iTickable.h" +#include "math/mEase.h" +#include "scene/sceneObject.h" + +class Tween : public SimObject, public ITickable +{ + typedef SimObject Parent; + + enum TweenState { + Idle, + Playing, + Paused, + PlayingReversed + }; + +public: + Tween(); + void SetValueByTime(F32 time); + bool SetTargetField(F32 value); + bool SetSceneObjectTargetField(SceneObject* obj, F32 value); + bool SetGuiControlTargetField(GuiControl* obj, F32 value); + void SetGlobalField(F32 value); + + static bool setTargetProperty(void* obj, const char* index, const char* db); + + void Play(); + void Reverse(); + void Rewind(); + void Pause(); + + //------------------------------------------- + // SimObject + //------------------------------------------- + virtual bool onAdd(); + static void initPersistFields(); + + //------------------------------------------- + // ITickable + //------------------------------------------- + virtual void interpolateTick(F32 delta) {}; + virtual void processTick() {}; + virtual void advanceTime(F32 timeDelta); + + // Fields ------------------------------------ + F32 mDuration; + F32 mCurrentTime; + F32 mPreviousTime; + SimObject* mTarget; + const char* mValueName; + F32 mValueTarget; + F32 mCurrentValue; + Ease::enumDirection mEaseDirection; + Ease::enumType mEaseType; + TweenState mState; + + DECLARE_CALLBACK(void, onFinished, ()); + DECLARE_CONOBJECT(Tween); +}; + +#endif // TWEEN_ENGINE_H