From eb6c4c8076110ddf1d06c78d13609c2ff695dde3 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Wed, 7 Dec 2022 20:13:59 -0800 Subject: [PATCH] Tileset animations. --- src/dawn/display/Tileset.hpp | 4 +- src/dawn/display/animation/Animation.hpp | 148 +----------------- .../display/animation/SimpleAnimation.hpp | 124 +++++++++++++++ .../animation/TiledSpriteAnimation.hpp | 27 ++++ src/dawn/scene/components/Components.hpp | 1 + .../display/AnimationController.cpp | 30 ++++ .../display/AnimationController.hpp | 24 +++ .../scene/components/display/CMakeLists.txt | 1 + src/dawn/scene/components/display/Camera.hpp | 2 +- .../scene/components/display/TiledSprite.cpp | 14 +- .../scene/components/display/TiledSprite.hpp | 4 +- src/dawnpokergame/prefabs/VNPenny.hpp | 24 ++- src/dawnpokergame/scenes/TestScene.hpp | 2 +- src/dawnpokergame/ui/PokerPlayerDisplay.cpp | 10 +- src/dawnpokergame/ui/PokerPlayerDisplay.hpp | 4 +- src/dawntools/display/tilesetgen/main.c | 8 +- 16 files changed, 262 insertions(+), 165 deletions(-) create mode 100644 src/dawn/display/animation/SimpleAnimation.hpp create mode 100644 src/dawn/display/animation/TiledSpriteAnimation.hpp create mode 100644 src/dawn/scene/components/display/AnimationController.cpp create mode 100644 src/dawn/scene/components/display/AnimationController.hpp diff --git a/src/dawn/display/Tileset.hpp b/src/dawn/display/Tileset.hpp index d2396155..3a3ab0fb 100644 --- a/src/dawn/display/Tileset.hpp +++ b/src/dawn/display/Tileset.hpp @@ -60,8 +60,8 @@ namespace Dawn { this->columns = columns; // Calculate division sizes (pixels) - this->divX = (w - (borderX * 2.0f) - (gapX * (columns - 1))) / columns; - this->divY = (h - (borderY * 2.0f) - (gapY * (rows - 1))) / rows; + this->divX = (w - (borderX * 2) - (gapX * (columns - 1))) / columns; + this->divY = (h - (borderY * 2) - (gapY * (rows - 1))) / rows; // Calculate the division sizes (units) float_t tdivX = (float_t)this->divX / (float_t)w; diff --git a/src/dawn/display/animation/Animation.hpp b/src/dawn/display/animation/Animation.hpp index b4e95d21..d700091e 100644 --- a/src/dawn/display/animation/Animation.hpp +++ b/src/dawn/display/animation/Animation.hpp @@ -9,155 +9,20 @@ #include "util/mathutils.hpp" namespace Dawn { - template - struct Keyframe { - float_t time; - T value; - }; - - template - struct TimelineItem { - T *modifies; - std::vector> keyframes; - }; - - template struct Animation { public: bool_t loop = false; bool_t finished = false; float_t time = 0; float_t duration = 0; - easefunction_t *easing = &easeOutQuad; - std::vector> timelineItems; Event<> eventAnimationEnd; - + + virtual void tick(float_t delta) = 0; + /** - * Get an existing timeline item based on the value that will be modified. - * - * @param modifies Value that is intended to be modified for the timeline. - * @return The existing timeline item OR NULL if not found. + * Restart a running animation. */ - struct TimelineItem * getTimelineItem(T *modifies) { - assertNotNull(modifies); - - auto it = this->timelineItems.begin(); - while(it != this->timelineItems.end()) { - if(it->modifies == modifies) return &(*it); - ++it; - } - - return nullptr; - } - - /** - * Add a timeline item to an animation. - * - * @param modifies Value that will be modified for the timeline item. - * @return The timeline item for that modified value. - */ - struct TimelineItem * addTimelineItem(T *modifies) { - assertNotNull(modifies); - struct TimelineItem item; - item.modifies = modifies; - this->timelineItems.push_back(item); - return &this->timelineItems.back(); - } - - /** - * Add a keyframe to the animation. - * - * @param modifies Pointer to the value that will be modified. - * @param time Time that the animation will occur at (gametime seconds). - * @param value Value for this keyframe - * @return The keyframe that was added. - */ - struct Keyframe * addKeyframe(T *modifies, float_t time, T value) { - auto item = this->getTimelineItem(modifies); - if(item == nullptr) item = this->addTimelineItem(modifies); - - struct Keyframe keyframe; - keyframe.time = time; - keyframe.value = value; - this->duration = mathMax(this->duration, time); - this->finished = false; - - item->keyframes.push_back(keyframe); - return &item->keyframes.back(); - } - - /** - * Tick/Update the animation. - * - * @param delta Delta (in seconds) to update the animation by. - */ - void tick(float_t delta) { - if(this->finished) return; - - float_t newTime = this->time + delta; - - auto it = this->timelineItems.begin(); - while(it != this->timelineItems.end()) { - struct Keyframe *keyframeNext = nullptr; - struct Keyframe *keyframeCurrent = nullptr; - - // For each keyframe - auto itKey = it->keyframes.begin(); - while(itKey != it->keyframes.end()) { - if(itKey->time > newTime) { - keyframeNext = &(*itKey); - break; - } - keyframeCurrent = &(*itKey); - ++itKey; - } - - // Skip when no keyframe - float_t oldTime; - T oldValue; - - if(keyframeCurrent == nullptr) { - if(keyframeNext == nullptr) continue; - oldTime = this->time; - oldValue = *it->modifies; - } else if(keyframeNext == nullptr) { - *it->modifies = keyframeCurrent->value; - ++it; - continue; - } else { - oldValue = keyframeCurrent->value; - oldTime = keyframeCurrent->time; - } - - // Slerp between keyframes - float_t keyframeDelta = this->easing( - (newTime - oldTime) / (keyframeNext->time - oldTime) - ); - *it->modifies = oldValue + ( - (keyframeNext->value - oldValue) * keyframeDelta - ); - ++it; - } - - // Update time. - this->time = newTime; - - // Has the animation finished? - if(newTime < this->duration) return; - - // Do we need to loop? - if(this->loop) { - this->time = 0; - return; - } - - // Animation end. - this->finished = true; - - this->eventAnimationEnd.invoke(); - } - - void restart() { + virtual void restart() { this->time = 0; this->finished = false; } @@ -165,8 +30,7 @@ namespace Dawn { /** * Clears an animaton of all its animation items and keyframes. */ - void clear() { - this->timelineItems.clear(); + virtual void clear() { this->duration = 0; } }; diff --git a/src/dawn/display/animation/SimpleAnimation.hpp b/src/dawn/display/animation/SimpleAnimation.hpp new file mode 100644 index 00000000..af18c763 --- /dev/null +++ b/src/dawn/display/animation/SimpleAnimation.hpp @@ -0,0 +1,124 @@ +// Copyright (c) 2022 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "Animation.hpp" + +namespace Dawn { + template + struct SimpleKeyframe { + float_t time; + T value; + }; + + template + struct SimpleAnimation : public Animation { + public: + easefunction_t *easing = &easeLinear; + T *modifies; + std::vector> keyframes; + + SimpleAnimation(T *modifies) { + this->modifies = modifies; + } + + void addKeyframe(float_t time, T value) { + struct SimpleKeyframe keyframe; + keyframe.time = time; + keyframe.value = value; + this->duration = mathMax(this->duration, time); + this->finished = false; + this->keyframes.push_back(keyframe); + } + + void addSequentialKeyframes( + float_t startTime, + float_t frameTime, + T start, + T end, + T step + ) { + T v = start; + float_t n = startTime; + while(v != end) { + this->addKeyframe(n, v); + n += frameTime; + v += step; + } + } + + void addSequentialKeyframes(float_t frameTime, T start, T end) { + this->addSequentialKeyframes(0, frameTime, start, end, 1); + } + + void tick(float_t delta) override { + if(this->finished) return; + + float_t newTime = this->time + delta; + + struct SimpleKeyframe *keyframeNext = nullptr; + struct SimpleKeyframe *keyframeCurrent = nullptr; + + // Find current and next keyframe(s) + auto itKey = this->keyframes.begin(); + while(itKey != this->keyframes.end()) { + if(itKey->time > newTime) { + keyframeNext = &(*itKey); + break; + } + keyframeCurrent = &(*itKey); + ++itKey; + } + + // Update values + if(keyframeCurrent != nullptr && keyframeNext == nullptr) { + // "End of animation" + *this->modifies = keyframeCurrent->value; + } else if(keyframeNext != nullptr) { + T oldValue; + float_t oldTime; + + if(keyframeCurrent == nullptr) { + // "Start of animation" + oldValue = keyframeCurrent->value; + oldTime = keyframeCurrent->time; + } else { + // "Mid animation" + oldTime = this->time; + oldValue = *this->modifies; + } + + // Slerp between keyframes + float_t keyframeDelta = this->easing( + (newTime - oldTime) / (keyframeNext->time - oldTime) + ); + *this->modifies = oldValue + ( + (keyframeNext->value - oldValue) * keyframeDelta + ); + } + + // Update time. + this->time = newTime; + + // Has the animation finished? + if(newTime < this->duration) return; + + // Do we need to loop? + if(this->loop) { + this->time = 0; + return; + } + + // Animation end. + this->finished = true; + this->eventAnimationEnd.invoke(); + } + + void clear() override { + Animation::clear(); + this->keyframes.clear(); + } + }; +} \ No newline at end of file diff --git a/src/dawn/display/animation/TiledSpriteAnimation.hpp b/src/dawn/display/animation/TiledSpriteAnimation.hpp new file mode 100644 index 00000000..9a172939 --- /dev/null +++ b/src/dawn/display/animation/TiledSpriteAnimation.hpp @@ -0,0 +1,27 @@ +// Copyright (c) 2022 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "SimpleAnimation.hpp" +#include "scene/components/Components.hpp" + +namespace Dawn { + struct TiledSpriteAnimation : public SimpleAnimation { + public: + int32_t frame = 0; + TiledSprite *sprite = nullptr; + + TiledSpriteAnimation(TiledSprite *sprite) : + SimpleAnimation(&frame), + sprite(sprite) + { + } + + void tick(float_t delta) override { + SimpleAnimation::tick(delta); + this->sprite->setTile(frame); + } + }; +} \ No newline at end of file diff --git a/src/dawn/scene/components/Components.hpp b/src/dawn/scene/components/Components.hpp index dd35e821..1a873f4e 100644 --- a/src/dawn/scene/components/Components.hpp +++ b/src/dawn/scene/components/Components.hpp @@ -4,6 +4,7 @@ // https://opensource.org/licenses/MIT #pragma once +#include "scene/components/display/AnimationController.hpp" #include "scene/components/display/Camera.hpp" #include "scene/components/display/MeshHost.hpp" #include "scene/components/display/MeshRenderer.hpp" diff --git a/src/dawn/scene/components/display/AnimationController.cpp b/src/dawn/scene/components/display/AnimationController.cpp new file mode 100644 index 00000000..b9972325 --- /dev/null +++ b/src/dawn/scene/components/display/AnimationController.cpp @@ -0,0 +1,30 @@ +// Copyright (c) 2022 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include "AnimationController.hpp" +#include "scene/Scene.hpp" +#include "game/DawnGame.hpp" + +using namespace Dawn; + +AnimationController::AnimationController(SceneItem *item) : + SceneItemComponent(item) +{ + +} + +void AnimationController::onSceneUpdate() { + if(this->animation == nullptr) return; + this->animation->tick(this->getGame()->timeManager.delta); +} + +void AnimationController::onStart() { + SceneItemComponent::onStart(); + getScene()->eventSceneUnpausedUpdate.addListener(this, &AnimationController::onSceneUpdate); +} + +AnimationController::~AnimationController() { + getScene()->eventSceneUnpausedUpdate.removeListener(this, &AnimationController::onSceneUpdate); +} \ No newline at end of file diff --git a/src/dawn/scene/components/display/AnimationController.hpp b/src/dawn/scene/components/display/AnimationController.hpp new file mode 100644 index 00000000..1e368f37 --- /dev/null +++ b/src/dawn/scene/components/display/AnimationController.hpp @@ -0,0 +1,24 @@ +// Copyright (c) 2022 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "scene/SceneItemComponent.hpp" +#include "display/animation/Animation.hpp" + +namespace Dawn { + class AnimationController : public SceneItemComponent { + private: + void onSceneUpdate(); + + public: + Animation *animation = nullptr; + + AnimationController(SceneItem *item); + + void onStart() override; + + ~AnimationController(); + }; +} \ No newline at end of file diff --git a/src/dawn/scene/components/display/CMakeLists.txt b/src/dawn/scene/components/display/CMakeLists.txt index cc61d3b8..9384ce7c 100644 --- a/src/dawn/scene/components/display/CMakeLists.txt +++ b/src/dawn/scene/components/display/CMakeLists.txt @@ -6,6 +6,7 @@ # Sources target_sources(${DAWN_TARGET_NAME} PRIVATE + AnimationController.cpp Camera.cpp Material.cpp MeshHost.cpp diff --git a/src/dawn/scene/components/display/Camera.hpp b/src/dawn/scene/components/display/Camera.hpp index 74985fb9..f497056e 100644 --- a/src/dawn/scene/components/display/Camera.hpp +++ b/src/dawn/scene/components/display/Camera.hpp @@ -41,7 +41,7 @@ namespace Dawn { // Shared float_t clipNear = 0.001f; - float_t clipFar = 100.0f; + float_t clipFar = 1000.0f; /** * Create a new Camera Component. diff --git a/src/dawn/scene/components/display/TiledSprite.cpp b/src/dawn/scene/components/display/TiledSprite.cpp index 34f3ad7e..923f7f07 100644 --- a/src/dawn/scene/components/display/TiledSprite.cpp +++ b/src/dawn/scene/components/display/TiledSprite.cpp @@ -13,6 +13,7 @@ TiledSprite::TiledSprite(SceneItem *item) : SceneItemComponent(item) { } glm::vec2 TiledSprite::getUV0() { + assertNotNull(this->tileset); auto tile = this->tileset->getTile(tileIndex); return glm::vec2( (this->flipState & TILED_SPRITE_FLIP_X) == 0 ? tile.uv0.x : tile.uv1.x, @@ -21,6 +22,7 @@ glm::vec2 TiledSprite::getUV0() { } glm::vec2 TiledSprite::getUV1() { + assertNotNull(this->tileset); auto tile = this->tileset->getTile(tileIndex); return glm::vec2( (this->flipState & TILED_SPRITE_FLIP_X) == 0 ? tile.uv1.x : tile.uv0.x, @@ -34,8 +36,18 @@ void TiledSprite::setTileset(Tileset *tileset) { this->setTile(0); } +void TiledSprite::setTilesetAndSize(TilesetGrid *tileset, glm::vec2 center) { + this->setTileset(tileset); + this->setSize(glm::vec2(tileset->divX, tileset->divY), center); +} + +void TiledSprite::setTilesetAndSize(TilesetGrid *tileset) { + this->setTileset(tileset); + this->setSize(glm::vec2(tileset->divX, tileset->divY)); +} + void TiledSprite::setTile(int32_t tileIndex) { - assertNotNull(this->tileset); + if(tileIndex == this->tileIndex) return; this->tileIndex = tileIndex; if(this->host != nullptr) { QuadMesh::bufferCoordinates( diff --git a/src/dawn/scene/components/display/TiledSprite.hpp b/src/dawn/scene/components/display/TiledSprite.hpp index 0660bce1..127fdc24 100644 --- a/src/dawn/scene/components/display/TiledSprite.hpp +++ b/src/dawn/scene/components/display/TiledSprite.hpp @@ -20,7 +20,7 @@ namespace Dawn { MeshHost *host = nullptr; Tileset *tileset = nullptr; flag_t flipState = TILED_SPRITE_FLIP_Y; - int32_t tileIndex; + int32_t tileIndex = -1; glm::vec2 xy0 = glm::vec2(0, 0); glm::vec2 xy1 = glm::vec2(1, 1); @@ -31,6 +31,8 @@ namespace Dawn { TiledSprite(SceneItem *item); void setTileset(Tileset *tileset); + void setTilesetAndSize(TilesetGrid *gridTileset, glm::vec2 center); + void setTilesetAndSize(TilesetGrid *gridTileset); void setTile(int32_t tile); void setFlippedState(flag_t flippedState); void setSize(glm::vec2 size, glm::vec2 center); diff --git a/src/dawnpokergame/prefabs/VNPenny.hpp b/src/dawnpokergame/prefabs/VNPenny.hpp index 28da0706..d6f757ae 100644 --- a/src/dawnpokergame/prefabs/VNPenny.hpp +++ b/src/dawnpokergame/prefabs/VNPenny.hpp @@ -7,6 +7,7 @@ #include "asset/AssetManager.hpp" #include "poker/PokerPlayer.hpp" #include "scene/components/Components.hpp" +#include "display/animation/TiledSpriteAnimation.hpp" namespace Dawn { class VNPenny { @@ -21,19 +22,26 @@ namespace Dawn { static SceneItem * create(Scene *scene) { auto item = scene->createSceneItem(); - auto meshRenderer = item->addComponent(); + auto textureAsset = scene->game->assetManager.get("texture_penny"); + auto tilesetAsset = scene->game->assetManager.get("tileset_penny"); + auto meshRenderer = item->addComponent(); auto material = item->addComponent(); - auto asset = scene->game->assetManager.get("texture_penny"); - auto param = material->getShader()->getParameterByName("u_Text"); - material->textureValues[param] = &asset->texture; - auto meshHost = item->addComponent(); auto tiledSprite = item->addComponent(); - tiledSprite->setTileset(&scene->game->assetManager.get("tileset_penny")->tileset); - tiledSprite->setSize(glm::vec2(2, 2)); - auto pokerPlayer = item->addComponent(); + auto animation = item->addComponent(); + + auto param = material->getShader()->getParameterByName("u_Text"); + material->textureValues[param] = &textureAsset->texture; + + tiledSprite->setTileset(&tilesetAsset->tileset); + tiledSprite->setSize(glm::vec2(tilesetAsset->tileset.divX, tilesetAsset->tileset.divY)); + + auto anim = new TiledSpriteAnimation(tiledSprite); + anim->addSequentialKeyframes(0.1f, 0, 22); + anim->loop = true; + animation->animation = anim; return item; } diff --git a/src/dawnpokergame/scenes/TestScene.hpp b/src/dawnpokergame/scenes/TestScene.hpp index 25805dda..b787a4d1 100644 --- a/src/dawnpokergame/scenes/TestScene.hpp +++ b/src/dawnpokergame/scenes/TestScene.hpp @@ -39,7 +39,7 @@ namespace Dawn { void stage() override { // Camera auto camera = Camera::create(this); - camera->transform->lookAt(glm::vec3(3, 3, 3), glm::vec3(0, 0, 0)); + camera->transform->lookAt(glm::vec3(50, 50, 50), glm::vec3(0, 0, 0)); // UI auto canvas = UICanvas::createCanvas(this); diff --git a/src/dawnpokergame/ui/PokerPlayerDisplay.cpp b/src/dawnpokergame/ui/PokerPlayerDisplay.cpp index facd4e8f..325bd5ce 100644 --- a/src/dawnpokergame/ui/PokerPlayerDisplay.cpp +++ b/src/dawnpokergame/ui/PokerPlayerDisplay.cpp @@ -11,7 +11,8 @@ using namespace Dawn; PokerPlayerDisplay::PokerPlayerDisplay(UICanvas *canvas) : UIEmpty(canvas), labelName(canvas), - labelChips(canvas) + labelChips(canvas), + animChips(&animChipsValue) { this->font = getGame()->assetManager.get("truetype_ark"); @@ -36,6 +37,9 @@ PokerPlayerDisplay::PokerPlayerDisplay(UICanvas *canvas) : 0.0f ); + // Anim + this->animChips.easing = &easeOutQuart; + // Events getScene()->eventSceneUnpausedUpdate.addListener(this, &PokerPlayerDisplay::onSceneUpdate); } @@ -68,8 +72,8 @@ void PokerPlayerDisplay::onPlayerChipsChanged() { std::cout << "Chips" << player->chips << std::endl; this->animChips.clear(); - this->animChips.addKeyframe(&this->animChipsValue, 0.0f, this->animChipsValue); - this->animChips.addKeyframe(&this->animChipsValue, 1.0f, this->player->chips); + this->animChips.addKeyframe(0.0f, this->animChipsValue); + this->animChips.addKeyframe(1.0f, this->player->chips); this->animChips.restart(); } diff --git a/src/dawnpokergame/ui/PokerPlayerDisplay.hpp b/src/dawnpokergame/ui/PokerPlayerDisplay.hpp index e276d8bd..1f8054d9 100644 --- a/src/dawnpokergame/ui/PokerPlayerDisplay.hpp +++ b/src/dawnpokergame/ui/PokerPlayerDisplay.hpp @@ -9,12 +9,12 @@ #include "poker/PokerPlayer.hpp" #include "asset/AssetManager.hpp" #include "asset/assets/TrueTypeAsset.hpp" -#include "display/animation/Animation.hpp" +#include "display/animation/SimpleAnimation.hpp" namespace Dawn { class PokerPlayerDisplay : public UIEmpty { private: - Animation animChips; + SimpleAnimation animChips; int32_t animChipsValue; protected: diff --git a/src/dawntools/display/tilesetgen/main.c b/src/dawntools/display/tilesetgen/main.c index e2653be1..9afac5fd 100644 --- a/src/dawntools/display/tilesetgen/main.c +++ b/src/dawntools/display/tilesetgen/main.c @@ -119,11 +119,11 @@ int main(int argc, char *argv[]) { sprintf(buffer, "%i|%i|%i|%i|", cols, rows, divX, divY); // Now prep tileset. - for(int y = 0; y < rows; y++) { - for(int x = 0; x < cols; x++) { - float ux0 = (borderX + ((float)divX * x) + ((float)gapX * x)) / w; + for(float y = 0; y < rows; y++) { + for(float x = 0; x < cols; x++) { + float ux0 = ((float)borderX + ((float)divX * x) + ((float)gapX * x)) / (float)w; float ux1 = ux0 + tdivX; - float uy0 = (borderY + ((float)divY * y) + ((float)gapY * y)) / h; + float uy0 = ((float)borderY + ((float)divY * y) + ((float)gapY * y)) / (float)h; float uy1 = uy0 + tdivY; sprintf(buffer, "%s%f,%f,%f,%f|", buffer, ux0, ux1, uy0, uy1); }