Skip to content

Commit

Permalink
Modified stop motion animations. Now it can suppoort a lot of widgets
Browse files Browse the repository at this point in the history
  • Loading branch information
ValeriiKoniushenko committed Oct 22, 2023
1 parent 71d736b commit d3ac590
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 205 deletions.
3 changes: 2 additions & 1 deletion lib/core/animations/include/IAnimation.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#pragma once

#include "CopyableAndMoveable.h"
#include "Camera.h"

#include <cstdio>

Expand All @@ -46,7 +47,7 @@ class IAnimation : public Utils::CopyableAndMoveable
};

virtual void setMode(Mode mode) { mode_ = mode; };
virtual void draw(ShaderPack& shaderPack) = 0;
virtual void draw(ShaderPack& shaderPack, Camera* camera = nullptr) = 0;
virtual void start() = 0;
virtual void stop() = 0;
virtual void pause() = 0;
Expand Down
252 changes: 244 additions & 8 deletions lib/core/animations/include/StopMotionAnimation.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,35 @@
#pragma once

#include "IAnimation.h"
#include "Image.h"
#include "Rect.h"
#include "ShaderPack.h"
#include "Texture.h"
#include "Widget.h"
#include "glm/glm.hpp"

#include <stdexcept>
#include <chrono>

class Widget;

template<class WidgetT = Widget>
class StopMotionAnimation : public IAnimation
{
public:
void draw(ShaderPack& shaderPack) override;
void draw(ShaderPack& shaderPack, Camera* camera = nullptr) override;
void start() override;
void stop() override;
void pause() override;

void setupAnimation(const Utils::IRect& firstFrame, const Utils::IRect& lastFrame, Widget& widget);
void setupAnimation(const Utils::IRect& firstFrame, const Utils::IRect& lastFrame, WidgetT& widget);
void setupAnimation(WidgetT& widget);
void setFrameGap(const std::size_t& ms);
[[nodiscard]] std::size_t getFrameGap() const;
[[nodiscard]] std::size_t getFramesCount() const;

private:
Widget* widget_ = nullptr;
WidgetT* widget_ = nullptr;

std::size_t frameGap_{}; // ms
std::size_t frameGap_{ 500 }; // ms

std::chrono::system_clock::time_point lastAnimatingFrame;

Expand All @@ -61,10 +66,241 @@ class StopMotionAnimation : public IAnimation

bool isSingleShot = false;

enum class PingPongDirection // for ping-pong
enum class PingPongDirection
{
ToEnd,
ToStart
};
PingPongDirection direction_ = PingPongDirection::ToEnd;
};
};

namespace
{

enum class Direction
{
None,
Vertical,
Horizontal
};

Direction determineDirection(const Utils::IRect& firstFrame, const Utils::IRect& lastFrame)
{
if (firstFrame.position.x != lastFrame.position.x && firstFrame.position.y == lastFrame.position.y)
{
return Direction::Horizontal;
}

if (firstFrame.position.x == lastFrame.position.x && firstFrame.position.y != lastFrame.position.y)
{
return Direction::Vertical;
}

return Direction::None;
}

} // namespace

template<class WidgetT>
void StopMotionAnimation<WidgetT>::setupAnimation(const Utils::IRect& firstFrame, const Utils::IRect& lastFrame, WidgetT& widget)
{
if (firstFrame.size != lastFrame.size)
{
throw std::runtime_error("You can't setup the animation with different texture sizes.");
}

widget_ = &widget;

startPosition_ = firstFrame.position;
endPosition_ = lastFrame.position;

textureSize_ = firstFrame.size;

switch (determineDirection(firstFrame, lastFrame))
{
case Direction::Horizontal:
{
framesCount_ = static_cast<std::size_t>((lastFrame.position.x - firstFrame.position.x) / firstFrame.size.width);
offsetPosition_ = glm::ivec2((lastFrame.position.x - firstFrame.position.x) / framesCount_, firstFrame.position.y);
break;
}
case Direction::Vertical:
{
framesCount_ = static_cast<std::size_t>((lastFrame.position.y - firstFrame.position.y) / firstFrame.size.height);
offsetPosition_ = glm::ivec2(firstFrame.position.x, (lastFrame.position.y - firstFrame.position.y) / framesCount_);
break;
}
case Direction::None:
{
throw std::runtime_error("Can't determine the direction of the animation. Maybe it's going by diagonal.");
}
}

++framesCount_;
}

template<class WidgetT>
void StopMotionAnimation<WidgetT>::setupAnimation(WidgetT& widget)
{
Utils::IRect firstFrame;
Utils::IRect lastFrame;

firstFrame.position = { 0.f, 0.f };
if (widget.getSize().width < widget.getSize().height)
{
firstFrame.size = { static_cast<int>(widget.getSize().width), static_cast<int>(widget.getSize().width) };
lastFrame.size = { static_cast<int>(widget.getSize().width), static_cast<int>(widget.getSize().width) };
}
else
{
firstFrame.size = { static_cast<int>(widget.getSize().height), static_cast<int>(widget.getSize().height) };
lastFrame.size = { static_cast<int>(widget.getSize().height), static_cast<int>(widget.getSize().height) };
}

if (widget.getTexture().getImage()->getSize().x < widget.getTexture().getImage()->getSize().y)
{
lastFrame.position = { 0.f, widget.getTexture().getImage()->getSize().y - lastFrame.size.height };
}
else
{
lastFrame.position = { widget.getTexture().getImage()->getSize().x - lastFrame.size.width, 0.f };
}

setupAnimation(firstFrame, lastFrame, widget);
}

template<class WidgetT>
std::size_t StopMotionAnimation<WidgetT>::getFramesCount() const
{
return framesCount_;
}

template<class WidgetT>
void StopMotionAnimation<WidgetT>::draw(ShaderPack& shaderPack, Camera* camera/* = nullptr*/)
{
if (!widget_ || state_ == State::Stopped)
{
return;
}

if (state_ != State::Paused)
{
switch (mode_)
{
case Mode::PingPong:
{
const auto cooldown = (std::chrono::system_clock::now() - lastAnimatingFrame).count() / 10'000ll;
if (cooldown >= frameGap_)
{
if (direction_ == PingPongDirection::ToEnd)
{
currentOffsetPosition_ += offsetPosition_;
++currentFrame_;
}
else if (direction_ == PingPongDirection::ToStart)
{
currentOffsetPosition_ -= offsetPosition_;
--currentFrame_;
}

if (currentFrame_ >= framesCount_)
{
if (direction_ == PingPongDirection::ToEnd)
{
currentOffsetPosition_ = endPosition_;
currentFrame_ = framesCount_ - 1;
direction_ = PingPongDirection::ToStart;
}
else if (direction_ == PingPongDirection::ToStart)
{
currentOffsetPosition_ = startPosition_;
currentFrame_ = 0;
direction_ = PingPongDirection::ToEnd;
}
}

widget_->setTextureRect(Utils::IRect{currentOffsetPosition_, textureSize_});

lastAnimatingFrame = std::chrono::system_clock::now();
}
break;
}
case Mode::Repeating:
{
const auto cooldown = (std::chrono::system_clock::now() - lastAnimatingFrame).count() / 10'000ll;
if (cooldown >= frameGap_)
{
if (currentFrame_ >= framesCount_ - 1)
{
currentOffsetPosition_ = startPosition_;
currentFrame_ = 0;
}

currentOffsetPosition_ += offsetPosition_;

++currentFrame_;

widget_->setTextureRect(Utils::IRect{currentOffsetPosition_, textureSize_});

lastAnimatingFrame = std::chrono::system_clock::now();
}
break;
}
case Mode::SingleShot:
{
const auto cooldown = (std::chrono::system_clock::now() - lastAnimatingFrame).count() / 10'000ll;
if (cooldown >= frameGap_)
{
if (currentFrame_ >= framesCount_ - 1)
{
currentOffsetPosition_ = endPosition_;
widget_->setTextureRect(Utils::IRect{currentOffsetPosition_, textureSize_});
currentFrame_ = framesCount_;
break;
}

currentOffsetPosition_ += offsetPosition_;

++currentFrame_;

widget_->setTextureRect(Utils::IRect{currentOffsetPosition_, textureSize_});

lastAnimatingFrame = std::chrono::system_clock::now();
}
break;
}
}
}
widget_->draw(shaderPack, camera);
}

template<class WidgetT>
void StopMotionAnimation<WidgetT>::start()
{
state_ = State::Running;
}

template<class WidgetT>
void StopMotionAnimation<WidgetT>::stop()
{
currentOffsetPosition_ = startPosition_;
state_ = State::Stopped;
}

template<class WidgetT>
void StopMotionAnimation<WidgetT>::pause()
{
state_ = State::Paused;
}

template<class WidgetT>
void StopMotionAnimation<WidgetT>::setFrameGap(const std::size_t& ms)
{
frameGap_ = ms;
}

template<class WidgetT>
std::size_t StopMotionAnimation<WidgetT>::getFrameGap() const
{
return frameGap_;
}
Loading

0 comments on commit d3ac590

Please sign in to comment.