Started work on animated sprites

This commit is contained in:
OusmBlueNinja 2025-04-21 22:28:43 -05:00
parent d09fd6b8e8
commit 9e33e61ed0
12 changed files with 277 additions and 60 deletions

View File

@ -78,10 +78,10 @@ Collapsed=0
DockId=0x00000005,1
[Window][Color Correction]
Pos=0,19
Size=342,662
Pos=1588,867
Size=332,310
Collapsed=0
DockId=0x00000009,1
DockId=0x00000006,1
[Window][Asset Browser]
Pos=958,867
@ -102,5 +102,5 @@ DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1920,1158 Split=
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=1242,481 Split=X Selected=0x9B5D3198
DockNode ID=0x0000000D Parent=0x00000005 SizeRef=612,248 HiddenTabBar=1 Selected=0xEA83D666
DockNode ID=0x0000000E Parent=0x00000005 SizeRef=628,248 HiddenTabBar=1 Selected=0x36AF052B
DockNode ID=0x00000006 Parent=0x00000008 SizeRef=332,481 HiddenTabBar=1 Selected=0x3FC1A724
DockNode ID=0x00000006 Parent=0x00000008 SizeRef=332,481 Selected=0x3FC1A724

View File

@ -1,3 +1,3 @@
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -Isrc/vendor/box2d -IC:/msys64/mingw64/include -IC:\msys64\mingw64\lib\libyaml-cpp.a -Isrc\vendor\imgui -MMD -MP -c src\src\Engine.cpp -o src\build\Engine.o
[LINK] g++ src\build\Engine.o src\build\main.o src\build\Renderer.o src\build\Components\CameraComponent.o src\build\Components\LightComponent.o src\build\Components\ParticleComponent.o src\build\Components\PhysicsComponent.o src\build\Components\ScriptComponent.o src\build\Components\SpriteComponent.o src\build\Components\TextComonent.o src\build\Components\TilemapComponent.o src\build\core\utils\AssetLoader.o src\build\core\utils\EngineConfig.o src\build\core\utils\ExceptionHandler.o src\build\core\utils\FileDialog.o src\build\core\utils\input.o src\build\core\utils\Logging.o src\build\core\utils\Profiler.o src\build\core\utils\utils.o src\build\editor\windows\AssetBrowser.o src\build\editor\windows\Inspector.o src\build\Entitys\Object.o src\build\utils\GameObjectsList.o src\build\utils\Shader.o src\build\utils\UID.o src\build\lapi.o src\build\lauxlib.o src\build\lbaselib.o src\build\lcode.o src\build\lcorolib.o src\build\lctype.o src\build\ldblib.o src\build\ldebug.o src\build\ldo.o src\build\ldump.o src\build\lfunc.o src\build\lgc.o src\build\linit.o src\build\liolib.o src\build\llex.o src\build\lmathlib.o src\build\lmem.o src\build\loadlib.o src\build\lobject.o src\build\lopcodes.o src\build\loslib.o src\build\lparser.o src\build\lstate.o src\build\lstring.o src\build\lstrlib.o src\build\ltable.o src\build\ltablib.o src\build\ltm.o src\build\lua.o src\build\luac.o src\build\lundump.o src\build\lutf8lib.o src\build\lvm.o src\build\lzio.o src\build\imgui.o src\build\imgui_demo.o src\build\imgui_draw.o src\build\imgui_impl_glfw.o src\build\imgui_impl_opengl3.o src\build\imgui_tables.o src\build\imgui_widgets.o src\build\aabb.o src\build\arena_allocator.o src\build\array.o src\build\bitset.o src\build\body.o src\build\broad_phase.o src\build\constraint_graph.o src\build\contact.o src\build\contact_solver.o src\build\core.o src\build\distance.o src\build\distance_joint.o src\build\dynamic_tree.o src\build\geometry.o src\build\hull.o src\build\id_pool.o src\build\island.o src\build\joint.o src\build\manifold.o src\build\math_functions.o src\build\motor_joint.o src\build\mouse_joint.o src\build\mover.o src\build\prismatic_joint.o src\build\revolute_joint.o src\build\sensor.o src\build\shape.o src\build\solver.o src\build\solver_set.o src\build\table.o src\build\timer.o src\build\types.o src\build\weld_joint.o src\build\wheel_joint.o src\build\world.o -o src\build\app.exe -LC:\msys64\mingw64\lib -lglfw3 -lglew32 -lopengl32 -lgdi32 -lyaml-cpp -lcomdlg32 -lssl -lcrypto
[RUN] Executed app.exe successfully.
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -Isrc/vendor/box2d -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Renderer.cpp -o src\build\Renderer.o
[LINK] g++ src\build\Engine.o src\build\main.o src\build\Renderer.o src\build\Components\AnimationComponent.o src\build\Components\CameraComponent.o src\build\Components\LightComponent.o src\build\Components\ParticleComponent.o src\build\Components\PhysicsComponent.o src\build\Components\ScriptComponent.o src\build\Components\SpriteComponent.o src\build\Components\TextComonent.o src\build\Components\TilemapComponent.o src\build\core\utils\AssetLoader.o src\build\core\utils\EngineConfig.o src\build\core\utils\ExceptionHandler.o src\build\core\utils\FileDialog.o src\build\core\utils\input.o src\build\core\utils\Logging.o src\build\core\utils\Profiler.o src\build\core\utils\Texture.o src\build\core\utils\utils.o src\build\editor\windows\AssetBrowser.o src\build\editor\windows\Inspector.o src\build\Entitys\Object.o src\build\utils\GameObjectsList.o src\build\utils\Shader.o src\build\utils\UID.o src\build\lapi.o src\build\lauxlib.o src\build\lbaselib.o src\build\lcode.o src\build\lcorolib.o src\build\lctype.o src\build\ldblib.o src\build\ldebug.o src\build\ldo.o src\build\ldump.o src\build\lfunc.o src\build\lgc.o src\build\linit.o src\build\liolib.o src\build\llex.o src\build\lmathlib.o src\build\lmem.o src\build\loadlib.o src\build\lobject.o src\build\lopcodes.o src\build\loslib.o src\build\lparser.o src\build\lstate.o src\build\lstring.o src\build\lstrlib.o src\build\ltable.o src\build\ltablib.o src\build\ltm.o src\build\lua.o src\build\luac.o src\build\lundump.o src\build\lutf8lib.o src\build\lvm.o src\build\lzio.o src\build\imgui.o src\build\imgui_demo.o src\build\imgui_draw.o src\build\imgui_impl_glfw.o src\build\imgui_impl_opengl3.o src\build\imgui_tables.o src\build\imgui_widgets.o src\build\aabb.o src\build\arena_allocator.o src\build\array.o src\build\bitset.o src\build\body.o src\build\broad_phase.o src\build\constraint_graph.o src\build\contact.o src\build\contact_solver.o src\build\core.o src\build\distance.o src\build\distance_joint.o src\build\dynamic_tree.o src\build\geometry.o src\build\hull.o src\build\id_pool.o src\build\island.o src\build\joint.o src\build\manifold.o src\build\math_functions.o src\build\motor_joint.o src\build\mouse_joint.o src\build\mover.o src\build\prismatic_joint.o src\build\revolute_joint.o src\build\sensor.o src\build\shape.o src\build\solver.o src\build\solver_set.o src\build\table.o src\build\timer.o src\build\types.o src\build\weld_joint.o src\build\wheel_joint.o src\build\world.o -o src\build\app.exe -LC:\msys64\mingw64\lib -lglfw3 -lglew32 -lopengl32 -lgdi32 -lyaml-cpp -lcomdlg32 -lssl -lcrypto
[ERROR] Interrupted by user.

View File

@ -1,28 +1,18 @@
#include "AnimationComponent.h"
#include "Renderer.h"
AnimationComponent::AnimationComponent(Object* owner): Component(owner) {}
AnimationComponent::AnimationComponent(Object* owner)
: Component(owner) {}
void AnimationComponent::SetTextureAtlas(const std::string& path, int cols, int rows, float duration) {
texture = std::make_shared<Texture>(path);
columns = cols;
rows = rows;
totalFrames = cols * rows;
frameDuration = duration;
GenerateUVs();
}
void AnimationComponent::GenerateUVs() {
uvOffsets.clear();
float frameW = 1.0f / float(columns);
float frameH = 1.0f / float(rows);
for (int y = 0; y < rows; ++y) {
for (int x = 0; x < columns; ++x) {
uvOffsets.emplace_back(core::types::Vec2(x * frameW, y * frameH));
}
void AnimationComponent::SetTextureAtlas(const std::string& path, int cols, int rows, float duration) {
texturePath = path;
texture = std::make_shared<Texture>(path);
atlas = TextureAtlas(texture, cols, rows);
columns = cols;
rows = rows;
totalFrames = cols * rows;
frameDuration = duration;
}
}
void AnimationComponent::Play() { playing = true; }
void AnimationComponent::Stop() { playing = false; }
@ -49,12 +39,33 @@ void AnimationComponent::Update(float dt) {
}
}
void AnimationComponent::Draw() {
if (!texture) return;
float frameW = 1.0f / float(columns);
float frameH = 1.0f / float(rows);
core::types::Vec2 uv = uvOffsets[currentFrame];
//Renderer::Draw(*texture, GetOwner()->GetWorldPosition(), uv, core::types::Vec2(frameW, frameH));
std::string AnimationComponent::GetName() const {
return "AnimationComponent";
}
void AnimationComponent::Save(YAML::Emitter& out) const {
out << YAML::Key << "AnimationComponent";
out << YAML::BeginMap;
out << YAML::Key << "TexturePath" << YAML::Value << texturePath;
out << YAML::Key << "Columns" << YAML::Value << columns;
out << YAML::Key << "Rows" << YAML::Value << rows;
out << YAML::Key << "FrameDuration" << YAML::Value << frameDuration;
out << YAML::EndMap;
}
void AnimationComponent::Load(const YAML::Node& node) {
if (!node["AnimationComponent"])
return;
const auto& data = node["AnimationComponent"];
std::string path = data["TexturePath"].as<std::string>();
int cols = data["Columns"].as<int>();
int rows = data["Rows"].as<int>();
float duration = data["FrameDuration"].as<float>();
SetTextureAtlas(path, cols, rows, duration);
}

View File

@ -1,29 +1,47 @@
#pragma once
#include "Component.h"
#include "core/types/Vec2.h"
#include "../core/types/Vec2.h"
#include <string>
#include <vector>
#include <memory>
#include "../core/utils/Texture.h"
#include "../core/utils/TextureAtlas.h"
class Texture;
class AnimationComponent : public Component {
class AnimationComponent : public Component
{
public:
AnimationComponent(Object* owner);;
AnimationComponent(Object *owner);
void SetTextureAtlas(const std::string &path, int cols, int rows, float frameDuration);
void SetTextureAtlas(const std::string& path, int cols, int rows, float frameDuration);
void Play();
void Stop();
void SetLooping(bool loop);
void SetSpeed(float speed);
void SetFrame(int frame);
bool IsPlaying() const { return playing; }
bool IsLooping() const { return loop; }
float GetSpeed() const { return speed; }
TextureAtlas *GetAtlas() { return &atlas; }
int GetCurrentFrame() const { return currentFrame; }
const std::string &GetTexturePath() const { return texturePath; }
float GetFrameDuration() const { return frameDuration; }
void Update(float dt);
void Draw();
std::string GetName() const override;
void Save(YAML::Emitter &out) const override;
void Load(const YAML::Node &node) override;
private:
std::string texturePath;
std::shared_ptr<Texture> texture;
std::vector<core::types::Vec2> uvOffsets;
TextureAtlas atlas;
int columns = 1;
int rows = 1;
int totalFrames = 1;
@ -34,6 +52,4 @@ private:
bool playing = true;
bool loop = true;
float speed = 1.0f;
void GenerateUVs();
};

View File

@ -8,6 +8,8 @@
#include "components/ScriptComponent.h"
#include "components/PhysicsComponent.h"
#include "components/ParticleComponent.h"
#include "components/AnimationComponent.h"
#include "core/utils/FileDialog.h"
#include "core/utils/Logging.h"
@ -890,7 +892,16 @@ void Engine::Run()
Renderer::DrawSprite(spritePtr.get(), worldPos, cameraZoom, cameraPos);
profiler.EndSection();
}
if (auto animator = obj->GetComponent<AnimationComponent>()) {
profiler.BeginSection("Draw Animation: " + obj->GetName());
Renderer::DrawTextureAtlas(animator->GetAtlas(), animator->GetCurrentFrame(), obj->GetWorldPosition(), obj->GetWorldRotation(), 1.0f);
profiler.EndSection();
}
if (auto particle = obj->GetComponent<ParticleComponent>())
{

View File

@ -7,6 +7,8 @@
#include "../Components/TextComponent.h"
#include "../Components/ScriptComponent.h"
#include "../Components/ParticleComponent.h"
#include "../Components/AnimationComponent.h"
#include "../core/utils/Logging.h"
@ -156,6 +158,8 @@ void Object::Load(const YAML::Node &node)
else if (type == "TextComponent") AddComponent<TextComponent>()->Load(compNode);
else if (type == "ScriptComponent") AddComponent<ScriptComponent>()->Load(compNode);
else if (type == "ParticleComponent") AddComponent<ParticleComponent>()->Load(compNode);
else if (type == "AnimationComponent") AddComponent<AnimationComponent>()->Load(compNode);
}
}

View File

@ -395,7 +395,6 @@ void Renderer::Resize(int w, int h)
width = w;
height = h;
// Resize all textures
glBindTexture(GL_TEXTURE_2D, textureColorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
@ -567,6 +566,39 @@ void Renderer::DrawSprite(SpriteComponent *sprite, const glm::vec2 &pos, float z
sortedDrawList.push_back(drawEntry);
}
void Renderer::DrawTextureAtlas(const TextureAtlas *atlas, int index, const core::types::Vec2 &pos, float rotationDeg, float zoom)
{
PROFILE_DEEP_SCOPE("DrawTextureAtlas");
if (!atlas || !atlas->texture || index >= atlas->GetTotalFrames())
return;
const auto &tex = atlas->texture;
const core::types::Vec2 uvMin = atlas->GetFrameUV(index);
const core::types::Vec2 uvMax = uvMin + atlas->GetFrameSizeUV();
glm::vec2 screenPos = pos * zoom + glm::vec2(width, height) * 0.5f;
glm::vec2 finalSize = glm::vec2(1.0f) * zoom;
BatchedSprite entry;
entry.screenPos = screenPos;
entry.size = finalSize;
entry.rotationRad = glm::radians(rotationDeg);
entry.textureID = tex->GetID();
entry.normalMapID = defaultNormalMap;
entry.renderType = SpriteComponent::RenderType::Unlit; // no lighting for raw atlas drawing
entry.sprite = nullptr;
entry.texCoords = glm::vec4(uvMin.x, uvMin.y, uvMax.x, uvMax.y);
SortedDrawEntry drawEntry;
drawEntry.sprite = entry;
drawEntry.shader = &unlitShader;
drawEntry.useLighting = false;
sortedDrawList.push_back(drawEntry);
}
void Renderer::FlushQuads()
{
PROFILE_ENGINE_SCOPE("Renderer::FlushQuads");

View File

@ -10,6 +10,8 @@
#include "utils/Shader.h"
#include "core/utils/Profiler.h"
#include "core/types/all.h"
#include "core/utils/TextureAtlas.h"
#include "core/utils/Texture.h"
struct ColorCorrection
{
@ -80,6 +82,8 @@ public:
static void DrawQuad(const core::types::Vec2 &pos, const core::types::Vec2 &size, float rotation, const core::types::Color &color);
static void BatchQuad(const core::types::Vec2 &worldPos, const core::types::Vec2 &size, float rotation, const core::types::Color &color, const core::types::Vec2 &cameraPos, float zoom);
static void DrawTextureAtlas(const TextureAtlas *atlas, int index, const core::types::Vec2 &pos, float rotationDeg, float zoom);
static void FlushQuads();
private:

View File

@ -0,0 +1,20 @@
#include "Texture.h"
#include <GL/glew.h>
#include <stb_image.h>
#include <iostream>
#include "utils.h"
Texture::Texture(const std::string &path)
{
id = LoadTextureIfNeeded(path);
}
Texture::~Texture()
{
}
unsigned int Texture::GetID() const
{
return id;
}

View File

@ -0,0 +1,13 @@
#pragma once
#include <string>
class Texture {
public:
Texture(const std::string& path);
~Texture();
unsigned int GetID() const;
private:
unsigned int id = 0;
};

View File

@ -1,10 +1,10 @@
#pragma once
#include <memory>
#include "core/types/Vec2.h"
#include "../types/Vec2.h"
#include "Texture.h"
class Texture;
struct TextureAtlas {
struct TextureAtlas
{
std::shared_ptr<Texture> texture;
int columns = 1;
int rows = 1;
@ -13,19 +13,29 @@ struct TextureAtlas {
TextureAtlas(std::shared_ptr<Texture> tex, int cols, int rows)
: texture(std::move(tex)), columns(cols), rows(rows) {}
core::types::Vec2 GetFrameUV(int index) const {
float w = 1.0f / float(columns);
float h = 1.0f / float(rows);
inline core::types::Vec2 GetFrameUV(int index) const
{
if (columns <= 0 || rows <= 0)
{
return core::types::Vec2(0.0f);
}
int x = index % columns;
int y = index / columns;
return core::types::Vec2(x * w, y * h);
float u = static_cast<float>(x) / static_cast<float>(columns);
float v = static_cast<float>(y) / static_cast<float>(rows);
return core::types::Vec2(u, v);
}
core::types::Vec2 GetFrameSizeUV() const {
core::types::Vec2 GetFrameSizeUV() const
{
return core::types::Vec2(1.0f / float(columns), 1.0f / float(rows));
}
int GetTotalFrames() const {
int GetTotalFrames() const
{
return columns * rows;
}
};

View File

@ -8,6 +8,7 @@
#include "../../components/ScriptComponent.h"
#include "../../components/PhysicsComponent.h"
#include "../../components/ParticleComponent.h"
#include "../../components/AnimationComponent.h"
void DrawInspectorUI(std::shared_ptr<Object> selected)
{
@ -72,7 +73,8 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
"LightComponent",
"ScriptComponent",
"TilemapComponent",
"ParticleComponent"};
"ParticleComponent",
"AnimationComponent"};
static int selectedIndex = -1;
@ -106,13 +108,14 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
selected->AddComponent<TilemapComponent>();
else if (type == "ParticleComponent" && !selected->GetComponent<ParticleComponent>())
selected->AddComponent<ParticleComponent>();
else if (type == "AnimationComponent" && !selected->GetComponent<AnimationComponent>())
selected->AddComponent<AnimationComponent>();
}
if (auto sprite = selected->GetComponent<SpriteComponent>())
{
ImGui::SeparatorText("Sprite Component");
// — Render Type (unchanged) —
const char *renderTypes[] = {"Unlit", "Lit"};
int currentTypeIndex = static_cast<int>(sprite->GetRenderType());
if (ImGui::Combo("Render Type", &currentTypeIndex, renderTypes, IM_ARRAYSIZE(renderTypes)))
@ -120,13 +123,10 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
ImGui::SeparatorText("Texture");
// — Texture File with Drag & Drop + Load Button —
{
std::string texFile = GetFilenameFromPath(sprite->GetTexturePath());
// filename text
ImGui::Text(texFile.c_str());
// accept draganddrop here
if (ImGui::BeginDragDropTarget())
{
if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("ASSET_TEXTURE"))
@ -227,6 +227,7 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
if (ImGui::Button("Remove LightComponent"))
selected->RemoveComponent<LightComponent>();
}
if (auto script = selected->GetComponent<ScriptComponent>())
{
ImGui::SeparatorText("Script Component");
@ -251,6 +252,101 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
if (ImGui::Button("Remove ScriptComponent"))
selected->RemoveComponent<ScriptComponent>();
}
if (auto anim = selected->GetComponent<AnimationComponent>())
{
ImGui::SeparatorText("Animation Component");
static int columns = anim->GetAtlas()->columns;
static int rows = anim->GetAtlas()->rows;
static float frameDuration = anim->GetFrameDuration();
static int frameIndex = anim->GetCurrentFrame();
// — Texture Preview & Selector —
ImGui::SeparatorText("Texture Atlas");
{
std::string texFile = GetFilenameFromPath(anim->GetTexturePath());
ImGui::Text(texFile.c_str());
if (ImGui::BeginDragDropTarget())
{
if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("ASSET_TEXTURE"))
{
const char *dropped = (const char *)payload->Data;
anim->SetTextureAtlas(dropped, columns, rows, frameDuration);
}
ImGui::EndDragDropTarget();
}
if (ImGui::SmallButton("Load##AnimTex"))
{
auto path = OpenFileDialog(FileDialogType::Images);
if (!path.empty())
anim->SetTextureAtlas(path, columns, rows, frameDuration);
}
}
// — Grid settings —
ImGui::InputInt("Columns", &columns);
ImGui::InputInt("Rows", &rows);
ImGui::InputFloat("Frame Duration", &frameDuration, 0.01f, 0.1f, "%.3f");
if (ImGui::Button("Apply Grid"))
{
anim->SetTextureAtlas(anim->GetTexturePath(), columns, rows, frameDuration);
}
// — Playback Controls —
ImGui::SeparatorText("Playback");
bool isPlaying = anim->IsPlaying();
if (ImGui::Checkbox("Playing", &isPlaying))
{
isPlaying ? anim->Play() : anim->Stop();
}
bool loop = anim->IsLooping();
if (ImGui::Checkbox("Loop", &loop))
{
anim->SetLooping(loop);
}
float speed = anim->GetSpeed();
if (ImGui::SliderFloat("Speed", &speed, 0.1f, 5.0f))
{
anim->SetSpeed(speed);
}
// Frame scrubber
int maxFrame = columns * rows - 1;
frameIndex = anim->GetCurrentFrame();
if (ImGui::SliderInt("Current Frame", &frameIndex, 0, maxFrame))
{
anim->SetFrame(frameIndex);
}
// — Atlas Viewer —
if (anim->GetAtlas()->texture)
{
ImGui::SeparatorText("Atlas Viewer");
const float previewSize = 128.0f;
ImVec2 uv0, uv1;
core::types::Vec2 uvMin = anim->GetAtlas()->GetFrameUV(frameIndex);
core::types::Vec2 uvSize = anim->GetAtlas()->GetFrameSizeUV();
uv0 = ImVec2(uvMin.x, uvMin.y);
uv1 = ImVec2(uvMin.x + uvSize.x, uvMin.y + uvSize.y);
ImGui::Image((ImTextureID)(intptr_t)anim->GetAtlas()->texture->GetID(), ImVec2(previewSize, previewSize), uv0, uv1);
}
ImGui::Separator();
if (ImGui::Button("Remove AnimationComponent"))
selected->RemoveComponent<AnimationComponent>();
}
if (auto part = selected->GetComponent<ParticleComponent>())
{
ImGui::SeparatorText("Particle Component");