Sprite, Animation & Sound

This commit is contained in:
Isuru Samarathunga
2025-09-07 01:04:41 +05:30
parent bf958cc2a9
commit 4d40a0d3f2
60 changed files with 20205 additions and 11 deletions

View File

@ -1,6 +1,17 @@
set(IAEngine_Sources
imp/cpp/IAEngine.cpp
imp/cpp/Time.cpp
imp/cpp/Audio.cpp
imp/cpp/Texture.cpp
imp/cpp/Input.cpp
imp/cpp/Scene.cpp
imp/cpp/Random.cpp
imp/cpp/Events/Event.cpp
imp/cpp/Nodes/Transform.cpp
imp/cpp/Nodes/Node.cpp
imp/cpp/Components/SpriteRenderer.cpp
imp/cpp/Components/SoundEmitter.cpp
)
add_library(IAEngine STATIC ${IAEngine_Sources})
@ -8,4 +19,5 @@ add_library(IAEngine STATIC ${IAEngine_Sources})
target_include_directories(IAEngine PUBLIC inc/)
target_include_directories(IAEngine PRIVATE imp/hpp)
target_link_libraries(IAEngine PUBLIC IACore)
target_link_libraries(IAEngine PUBLIC IACore IAMath ImGui)
target_link_libraries(IAEngine PRIVATE SDL3::SDL3 SDL3_mixer::SDL3_mixer)

View File

@ -0,0 +1,92 @@
#include <IAEngine/Audio.hpp>
#include <SDL3/SDL_iostream.h>
#include <SDL3_mixer/SDL_mixer.h>
#include <thread>
namespace ia::iae
{
MIX_Mixer *g_mixer{};
Vector<MIX_Track *> g_tracks;
Vector<MIX_Audio *> g_audioData;
VOID Audio::Initialize()
{
if (!MIX_Init())
THROW_UNKNOWN("Couldn't initialize SDL3 Mixer");
if (!(g_mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL)))
THROW_UNKNOWN(BuildString("Creating a SDL3 mixer failed with code ", SDL_GetError()));
}
VOID Audio::Terminate()
{
g_audioData.clear();
}
Sound Audio::CreateSound(IN PCUINT8 audioData, IN SIZE_T audioDataSize)
{
g_audioData.pushBack(MIX_LoadAudio_IO(g_mixer, SDL_IOFromConstMem(audioData, audioDataSize), false, true));
return Sound(g_audioData.size() - 1);
}
INT64 Audio::CreateTrack()
{
g_tracks.pushBack(MIX_CreateTrack(g_mixer));
return g_tracks.size() - 1;
}
VOID Audio::DestroyTrack(IN INT64 trackHandle)
{
ClearTrack(trackHandle);
}
VOID Audio::QueueTrackData(IN INT64 trackHandle, IN INT64 dataHandle)
{
MIX_SetTrackAudio(g_tracks[trackHandle], g_audioData[dataHandle]);
}
VOID Audio::PlayTrack(IN INT64 trackHandle, IN INT32 loopTimes, IN TimePeriod loopDelay)
{
struct __callback_data
{
INT32 loopTimes;
TimePeriod loopDelay;
};
const auto callbackData = new __callback_data{.loopTimes = loopTimes, .loopDelay = loopDelay};
MIX_PlayTrack(g_tracks[trackHandle], 0);
MIX_SetTrackStoppedCallback(
g_tracks[trackHandle],
[](PVOID _callbackData, MIX_Track *track) {
const auto callbackData = (__callback_data *) _callbackData;
if (callbackData->loopTimes < 0)
goto loop_next_iteration;
else if (callbackData->loopTimes == 0)
return;
else
callbackData->loopTimes -= 1;
loop_next_iteration:
std::this_thread::sleep_for(std::chrono::milliseconds((INT32) (callbackData->loopDelay.GetValue() * 1000)));
MIX_PlayTrack(track, 0);
},
callbackData);
}
VOID Audio::ClearTrack(IN INT64 trackHandle)
{
MIX_StopTrack(g_tracks[trackHandle], 0);
}
VOID Audio::PauseTrack(IN INT64 trackHandle)
{
MIX_PauseTrack(g_tracks[trackHandle]);
}
VOID Audio::ResumeTrack(IN INT64 trackHandle)
{
MIX_ResumeTrack(g_tracks[trackHandle]);
}
} // namespace ia::iae

View File

@ -0,0 +1,43 @@
#include <IAEngine/Components/SoundEmitter.hpp>
namespace ia::iae
{
SoundEmitterComponent::SoundEmitterComponent(IN Node *node):
IComponent(node)
{
m_trackHandle = Audio::CreateTrack();
SetSound(m_activeSound);
}
SoundEmitterComponent::~SoundEmitterComponent()
{
Audio::DestroyTrack(m_trackHandle);
m_trackHandle = INVALID_HANDLE;
}
VOID SoundEmitterComponent::Pause()
{
Audio::PauseTrack(m_trackHandle);
}
VOID SoundEmitterComponent::Resume()
{
Audio::ResumeTrack(m_trackHandle);
}
VOID SoundEmitterComponent::Draw()
{
}
VOID SoundEmitterComponent::Update()
{
}
VOID SoundEmitterComponent::SetSound(IN CONST Sound &sound)
{
m_activeSound = sound;
if(m_trackHandle == INVALID_HANDLE) return;
Audio::QueueTrackData(m_trackHandle, sound.DataHandle());
Audio::PlayTrack(m_trackHandle, sound.LoopTimes(), sound.LoopDelay());
}
} // namespace ia::iae

View File

@ -0,0 +1,117 @@
#include <IAEngine/Components/SpriteRenderer.hpp>
#include <IAEngine/Nodes/Node.hpp>
#include <IAEngine/Time.hpp>
#include <IAMath/Lerp.hpp>
namespace ia::iae
{
SpriteRendererComponent::SpriteRendererComponent(IN Node *node) : IComponent(node)
{
}
Handle SpriteRendererComponent::AddTexture(IN RefPtr<Texture> texture)
{
m_textures.pushBack(texture);
return m_textures.size() - 1;
}
Handle SpriteRendererComponent::AddAnimation(IN CONST Animation &animation)
{
IA_RELEASE_ASSERT(!animation.Keys.empty());
m_animations.pushBack(animation);
return m_animations.size() - 1;
}
Handle SpriteRendererComponent::AddAnimation(IN initializer_list<INT32> frames, IN INT32 frameDuration,
IN BOOL shouldLoop)
{
Animation anim;
anim.ShouldLoop = shouldLoop;
for (const auto &idx : frames)
{
anim.Keys.pushBack(AnimationKeyFrame{.Duration = frameDuration, .TextureHandle = idx});
}
return AddAnimation(anim);
}
VOID SpriteRendererComponent::BakeAnimations()
{
for (auto &anim : m_animations)
{
auto t = anim.Keys.back();
t.Duration = 0;
anim.Keys.pushBack(t);
}
if (m_animations.size())
SetActiveAnimation(0);
}
VOID SpriteRendererComponent::SetActiveTexture(IN Handle texture)
{
IA_RELEASE_ASSERT((texture != INVALID_HANDLE) && (texture < m_textures.size()));
}
VOID SpriteRendererComponent::SetActiveAnimation(IN Handle animation)
{
IA_RELEASE_ASSERT((animation != INVALID_HANDLE) && (animation < m_animations.size()));
m_prevAnimationKeyFrameIndex = 0;
m_activeAnimation = m_animations[animation];
m_prevAnimationKeyFrame = m_activeAnimation.Keys[m_prevAnimationKeyFrameIndex + 0];
m_nextAnimationKeyFrame = m_activeAnimation.Keys[m_prevAnimationKeyFrameIndex + 1];
m_currentAnimationState = m_prevAnimationKeyFrame;
}
VOID SpriteRendererComponent::Update()
{
UpdateAnimation();
}
VOID SpriteRendererComponent::Draw()
{
const auto &animFrame = m_currentAnimationState;
if (animFrame.TextureHandle == INVALID_HANDLE)
return;
m_textures[animFrame.TextureHandle]->Draw(
m_node->GetPosition() + animFrame.Position, m_node->GetScale() + animFrame.Scale,
m_node->GetRotation().Z + animFrame.Rotation.Z, m_isFlippedH, m_isFlippedV, animFrame.ColorOverlay);
}
VOID SpriteRendererComponent::UpdateAnimation()
{
const auto keyCount = m_activeAnimation.Keys.size();
if (!keyCount)
return;
if (m_currentAnimationState.ShouldInterpolate)
{
const auto t = m_timelinePosition / m_prevAnimationKeyFrame.Duration;
#define INTERP_PROPERTY(name) \
m_currentAnimationState.name = iam::Lerp(m_prevAnimationKeyFrame.name, m_nextAnimationKeyFrame.name, t);
INTERP_PROPERTY(Position);
INTERP_PROPERTY(Rotation);
INTERP_PROPERTY(Scale);
INTERP_PROPERTY(ColorOverlay);
#undef INTERP_PROPERTY
}
m_timelinePosition += Time::GetFrameDeltaTime();
if (m_timelinePosition >= m_prevAnimationKeyFrame.Duration)
{
m_prevAnimationKeyFrameIndex = (m_prevAnimationKeyFrameIndex + 1) % (keyCount - 1);
if(!m_prevAnimationKeyFrameIndex && !m_activeAnimation.ShouldLoop)
{
m_activeAnimation = {};
return;
}
m_prevAnimationKeyFrame = m_activeAnimation.Keys[m_prevAnimationKeyFrameIndex + 0];
m_nextAnimationKeyFrame = m_activeAnimation.Keys[m_prevAnimationKeyFrameIndex + 1];
m_currentAnimationState = m_prevAnimationKeyFrame;
m_timelinePosition = 0;
}
}
} // namespace ia::iae

View File

View File

@ -1,6 +1,212 @@
#include <IAEngine/Audio.hpp>
#include <IAEngine/IAEngine.hpp>
#include <IAEngine/Random.hpp>
#include <IAEngine/Time.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <SDL3/SDL.h>
#include <backends/imgui_impl_sdl3.h>
#include <backends/imgui_impl_sdlrenderer3.h>
#include <imgui.h>
namespace ia::iae
{
}
struct EngineContext
{
SDL_Renderer *Renderer{};
SDL_Window *Window{};
SDL_Event Event{};
ImGuiIO ImGUIIO{};
BOOL ShouldClose{false};
};
} // namespace ia::iae
namespace ia::iae
{
CONSTEXPR FLOAT32 GAME_UPDATE_INTERVAL = 1000.0f / 60.0f;
Engine::Engine() : m_context(MakeRefPtr<EngineContext>())
{
}
Engine::~Engine()
{
}
BOOL Engine::Initialize(IN CONST InitConfig &config)
{
IAE_LOG_INFO("Booting IAEngine for \"", config.GameName, "\"");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD))
{
IAE_LOG_ERROR("Couldn't intialize SDL3: ", SDL_GetError());
return false;
}
if (!SDL_CreateWindowAndRenderer(config.GameName.c_str(), config.WindowWidth, config.WindowHeight,
SDL_WINDOW_RESIZABLE, &m_context->Window, &m_context->Renderer))
{
IAE_LOG_ERROR("Couldn't create SDL3 window and renderer: ", SDL_GetError());
return false;
}
SDL_SetWindowResizable(m_context->Window, false);
SDL_SetRenderVSync(m_context->Renderer, 1);
const auto mainScale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
IMGUI_CHECKVERSION();
ImGui::CreateContext();
m_context->ImGUIIO = ImGui::GetIO();
(void) m_context->ImGUIIO;
m_context->ImGUIIO.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
m_context->ImGUIIO.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
ImGui::StyleColorsClassic();
ImGuiStyle &style = ImGui::GetStyle();
style.ScaleAllSizes(mainScale);
style.FontScaleDpi = mainScale;
ImGui_ImplSDL3_InitForSDLRenderer(m_context->Window, m_context->Renderer);
ImGui_ImplSDLRenderer3_Init(m_context->Renderer);
Time::Initialize();
Random::Initialize();
Audio::Initialize();
return true;
}
VOID Engine::Terminate()
{
IAE_LOG_INFO("Shutting down IAEngine");
Audio::Terminate();
ImGui_ImplSDLRenderer3_Shutdown();
ImGui_ImplSDL3_Shutdown();
ImGui::DestroyContext();
SDL_DestroyRenderer(m_context->Renderer);
SDL_DestroyWindow(m_context->Window);
SDL_Quit();
}
VOID Engine::BeginFrame()
{
SDL_PollEvent(&m_context->Event);
if (m_context->Event.type == SDL_EVENT_QUIT)
m_context->ShouldClose = true;
ProcessEvents();
m_updateTimer += Time::GetFrameDeltaTime();
if (m_updateTimer >= GAME_UPDATE_INTERVAL)
{
UpdateGame();
while (m_updateTimer >= GAME_UPDATE_INTERVAL)
m_updateTimer -= GAME_UPDATE_INTERVAL;
}
ImGui_ImplSDLRenderer3_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
RenderDebugUI();
ImGui::Render();
SDL_SetRenderScale(m_context->Renderer, m_context->ImGUIIO.DisplayFramebufferScale.x,
m_context->ImGUIIO.DisplayFramebufferScale.y);
SDL_SetRenderDrawColorFloat(m_context->Renderer, 0.33f, 0.33f, 0.33f, 1.0f);
SDL_RenderClear(m_context->Renderer);
RenderGame();
}
VOID Engine::EndFrame()
{
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), m_context->Renderer);
SDL_RenderPresent(m_context->Renderer);
Time::NextFrame();
}
BOOL Engine::ShouldClose()
{
return m_context->ShouldClose;
}
VOID Engine::RenderDebugUI()
{
}
VOID Engine::ProcessEvents()
{
}
VOID Engine::UpdateGame()
{
if B_LIKELY (m_activeScene)
m_activeScene->Update();
}
VOID Engine::RenderGame()
{
if B_LIKELY (m_activeScene)
m_activeScene->Draw();
}
VOID Engine::ChangeScene(IN RefPtr<Scene> scene)
{
m_activeScene = scene;
}
RefPtr<Scene> Engine::CreateScene()
{
return MakeRefPtr<Scene>(this);
}
PVOID Engine::GetRendererHandle() CONST
{
return m_context->Renderer;
}
} // namespace ia::iae
namespace ia::iae
{
RefPtr<Texture> Engine::CreateTexture(IN CONST Span<CONST UINT8> &encodedData)
{
return CreateTexture(encodedData.data(), encodedData.size());
}
RefPtr<Texture> Engine::CreateTexture(IN PCUINT8 encodedData, IN SIZE_T encodedDataSize)
{
INT32 w, h, nrChannels;
const auto pixels =
stbi_load_from_memory(encodedData, encodedDataSize, &w, &h, &nrChannels, STBI_rgb_alpha);
if (!pixels)
THROW_INVALID_DATA("Failed to decode the provided image data");
const auto result = CreateTexture((PCUINT8) pixels, w, h);
STBI_FREE(pixels);
return result;
}
RefPtr<Texture> Engine::CreateTexture(IN PCUINT8 rgbaData, IN INT32 width, IN INT32 height)
{
const auto result = MakeRefPtr<Texture>(this);
result->m_width = width;
result->m_height = height;
SDL_Surface *surface =
SDL_CreateSurfaceFrom(width, height, SDL_PIXELFORMAT_RGBA32, (void *) rgbaData, width << 2);
if (!surface)
THROW_UNKNOWN("Failed to create SDL surface: ", SDL_GetError());
result->m_handle = SDL_CreateTextureFromSurface(m_context->Renderer, surface);
if (!result->m_handle)
THROW_UNKNOWN("Failed to create SDL Texture: ", SDL_GetError());
SDL_DestroySurface(surface);
return result;
}
} // namespace ia::iae

View File

View File

@ -0,0 +1,57 @@
#include <IAEngine/Nodes/Node.hpp>
#include <IAEngine/IAEngine.hpp>
namespace ia::iae
{
VOID Node::OnAdded(IN Scene *scene)
{
m_scene = scene;
for (auto &c : m_children)
c->OnAdded(scene);
}
VOID Node::OnRemoved()
{
for (auto &c : m_children)
c->OnRemoved();
m_scene = nullptr;
}
VOID Node::Draw()
{
for (auto &c : m_components)
c->Draw();
for (auto &c : m_children)
c->Draw();
}
VOID Node::Update()
{
for (auto &c : m_components)
c->Update();
for (auto &c : m_children)
c->Update();
}
VOID Node::Enable()
{
for (auto &c : m_children)
c->Enable();
m_isEnabled = true;
}
VOID Node::Disable()
{
m_isEnabled = false;
for (auto &c : m_children)
c->Disable();
}
VOID Node::AddComponent(IN RefPtr<IComponent> component)
{
m_components.pushBack(IA_MOVE(component));
}
} // namespace ia::iae

View File

@ -0,0 +1,6 @@
#include <IAEngine/Nodes/Transform.hpp>
namespace ia::iae
{
} // namespace ia::iae

View File

@ -0,0 +1,21 @@
#include <IAEngine/Random.hpp>
#include <IAEngine/Time.hpp>
namespace ia::iae
{
VOID Random::Initialize()
{
srand((INT32)Time::GetCurrentTick());
}
FLOAT32 Random::Get()
{
return (FLOAT32)rand()/(FLOAT32)(RAND_MAX);
}
INT32 Random::GetInRange(IN INT32 min, IN INT32 max)
{
return min + (INT32)((max - min) * Get());
}
} // namespace ia::iae

View File

@ -0,0 +1,40 @@
#include <IAEngine/Scene.hpp>
namespace ia::iae
{
Scene::Scene(IN Engine *engine) : m_engine(engine)
{
}
Scene::~Scene()
{
}
VOID Scene::Draw()
{
for (auto &n : m_nodes)
n->Draw();
}
VOID Scene::Update()
{
for (auto &n : m_nodes)
n->Update();
}
VOID Scene::AddNode(IN RefPtr<Node> node)
{
m_nodes.pushBack(node);
node->OnAdded(this);
}
VOID Scene::RemoveNode(IN RefPtr<Node> node)
{
const auto it = m_nodes.find(node);
if(it != m_nodes.end())
{
m_nodes.erase(it);
node->OnRemoved();
}
}
} // namespace ia::iae

View File

@ -0,0 +1,31 @@
#include <IAEngine/IAEngine.hpp>
#include <IAEngine/Texture.hpp>
#include <SDL3/SDL.h>
#define TEXTURE_HANDLE (SDL_Texture *) m_handle
namespace ia::iae
{
Texture::Texture(IN Engine *engine) : m_engine(engine)
{
}
Texture::~Texture()
{
SDL_DestroyTexture(TEXTURE_HANDLE);
}
VOID Texture::Draw(IN CONST iam::Vec3f &position, IN CONST iam::Vec3f &scale, IN FLOAT32 rotation, IN BOOL flipH,
IN BOOL flipV, IN CONST iam::Vec4f &colorOverlay) CONST
{
SDL_FRect rect{.x = position.X, .y = position.Y, .w = m_width * scale.X, .h = m_height * scale.Y};
SDL_SetTextureColorModFloat(TEXTURE_HANDLE, colorOverlay.X, colorOverlay.Y, colorOverlay.Z);
SDL_SetTextureAlphaModFloat(TEXTURE_HANDLE, colorOverlay.W);
SDL_RenderTextureRotated((SDL_Renderer *) m_engine->GetRendererHandle(), TEXTURE_HANDLE, nullptr, &rect,
rotation, nullptr,
(SDL_FlipMode) (SDL_FLIP_NONE | (flipV ? SDL_FLIP_VERTICAL : SDL_FLIP_NONE) |
(flipH ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE)));
}
} // namespace ia::iae

View File

@ -0,0 +1,49 @@
#include <IAEngine/Random.hpp>
#include <IAEngine/Time.hpp>
#include <chrono>
namespace ia::iae
{
STATIC CONSTEXPR FLOAT32 TIME_SCALE = 1.0f;
FLOAT32 g_deltaTime{1 / 60.0f};
INT64 g_lastFrameTick{0};
std::chrono::time_point<std::chrono::high_resolution_clock> g_startMS;
FLOAT32 TimePeriod::GetValue() CONST
{
return (FLOAT32) m_value + ((FLOAT32) m_value * (Random::Get() * m_randomAdjustment) / 100.0f);
}
VOID Time::Initialize()
{
g_startMS = std::chrono::high_resolution_clock::now();
}
VOID Time::NextFrame()
{
const auto t = GetCurrentTick();
g_deltaTime = t - g_lastFrameTick;
g_lastFrameTick = t;
}
FLOAT32 Time::GetFrameDeltaTime()
{
return g_deltaTime * TIME_SCALE;
}
INT64 Time::GetCurrentSecond()
{
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now() - g_startMS)
.count();
}
INT64 Time::GetCurrentTick()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() -
g_startMS)
.count();
}
} // namespace ia::iae

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,83 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <IAEngine/Time.hpp>
namespace ia::iae
{
class Sound
{
public:
Sound(IN INT64 dataHandle) : m_dataHandle(dataHandle)
{
}
INT64 DataHandle() CONST
{
return m_dataHandle;
}
INT32 LoopTimes() CONST
{
return m_loopTimes;
}
TimePeriod LoopDelay() CONST
{
return m_loopDelay;
}
INT32 &LoopTimes()
{
return m_loopTimes;
}
TimePeriod &LoopDelay()
{
return m_loopDelay;
}
private:
INT64 m_dataHandle{};
INT32 m_loopTimes{0};
TimePeriod m_loopDelay{};
};
class Audio
{
public:
STATIC VOID Initialize();
STATIC VOID Terminate();
STATIC Sound CreateSound(IN CONST Vector<UINT8> &audioData)
{
return CreateSound(audioData.data(), audioData.size());
}
STATIC Sound CreateSound(IN PCUINT8 audioData, IN SIZE_T audioDataSize);
STATIC INT64 CreateTrack();
STATIC VOID DestroyTrack(IN INT64 trackHandle);
STATIC VOID QueueTrackData(IN INT64 trackHandle, IN INT64 dataHandle);
STATIC VOID PlayTrack(IN INT64 trackHandle, IN INT32 loopTimes, IN TimePeriod loopDelay);
STATIC VOID ClearTrack(IN INT64 trackHandle);
STATIC VOID PauseTrack(IN INT64 trackHandle);
STATIC VOID ResumeTrack(IN INT64 trackHandle);
};
} // namespace ia::iae

View File

@ -0,0 +1,55 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <iacore/vector.hpp>
#include <iacore/memory.hpp>
#include <iacore/string.hpp>
#include <iacore/exception.hpp>
#include <IAMath/Vec.hpp>
#define IAE_LOG_INFO ia::iae::LogInfo
#define IAE_LOG_WARN ia::iae::LogWarn
#define IAE_LOG_ERROR ia::iae::LogError
namespace ia::iae
{
using Handle = INT64;
STATIC CONSTEXPR Handle INVALID_HANDLE = -1;
template<typename... Args> VOID LogInfo(Args... args)
{
StringStream ss;
UNUSED((ss << ... << args));
printf(__CC_GREEN "[INFO]: %s\n" __CC_DEFAULT, ss.str().c_str());
}
template<typename... Args> VOID LogWarn(Args... args)
{
StringStream ss;
UNUSED((ss << ... << args));
printf(__CC_YELLOW "[WARN]: %s\n" __CC_DEFAULT, ss.str().c_str());
}
template<typename... Args> VOID LogError(Args... args)
{
StringStream ss;
UNUSED((ss << ... << args));
printf(__CC_RED "[ERROR]: %s\n" __CC_DEFAULT, ss.str().c_str());
}
} // namespace ia::iae

View File

@ -0,0 +1,36 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <IAEngine/Base.hpp>
namespace ia::iae
{
class Node;
class IComponent
{
public:
IComponent(IN Node* node): m_node(node) {}
PURE_VIRTUAL(VOID Draw());
PURE_VIRTUAL(VOID Update());
protected:
Node* m_node{};
};
}

View File

@ -0,0 +1,43 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <IAEngine/Audio.hpp>
#include <IAEngine/Components/Component.hpp>
namespace ia::iae
{
class SoundEmitterComponent : public IComponent
{
public:
SoundEmitterComponent(IN Node *node);
~SoundEmitterComponent();
VOID Pause();
VOID Resume();
VOID SetSound(IN CONST Sound &sound);
public:
VOID Draw();
VOID Update();
private:
INT64 m_trackHandle{INVALID_HANDLE};
Sound m_activeSound{INVALID_HANDLE};
};
} // namespace ia::iae

View File

@ -0,0 +1,75 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <IAEngine/Components/Component.hpp>
#include <IAEngine/Texture.hpp>
namespace ia::iae
{
class SpriteRendererComponent : public IComponent
{
public:
struct AnimationKeyFrame
{
INT32 Duration{100};
iam::Vec3f Position{};
iam::Vec3f Rotation{};
iam::Vec3f Scale{1.0f, 1.0f, 1.0f};
iam::Vec4f ColorOverlay{1.0f, 1.0f, 1.0f, 1.0f};
BOOL ShouldInterpolate{};
INT32 TextureHandle{INVALID_HANDLE};
};
struct Animation
{
BOOL ShouldLoop{false};
Vector<AnimationKeyFrame> Keys;
};
public:
SpriteRendererComponent(IN Node *node);
Handle AddTexture(IN RefPtr<Texture> texture);
Handle AddAnimation(IN CONST Animation &animation);
Handle AddAnimation(IN initializer_list<INT32> frames, IN INT32 frameDuration, IN BOOL shouldLoop);
VOID BakeAnimations();
VOID SetActiveTexture(IN Handle texture);
VOID SetActiveAnimation(IN Handle animation);
public:
VOID Draw();
VOID Update();
private:
VOID UpdateAnimation();
private:
BOOL m_isFlippedV{false};
BOOL m_isFlippedH{false};
FLOAT32 m_timelinePosition{};
Animation m_activeAnimation{};
Vector<Animation> m_animations;
Vector<RefPtr<Texture>> m_textures;
AnimationKeyFrame m_currentAnimationState{};
AnimationKeyFrame m_nextAnimationKeyFrame{};
AnimationKeyFrame m_prevAnimationKeyFrame{};
INT32 m_prevAnimationKeyFrameIndex{};
};
} // namespace ia::iae

View File

@ -1,9 +1,72 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <iacore/base.hpp>
#include <iacore/string.hpp>
#include <IAEngine/Nodes/Node.hpp>
#include <IAEngine/Scene.hpp>
#include <IAEngine/Texture.hpp>
namespace ia::iae
{
}
struct EngineContext;
class Engine
{
public:
struct InitConfig
{
String GameName{"IAEngine Game"};
INT32 WindowWidth{800};
INT32 WindowHeight{600};
};
public:
Engine();
~Engine();
BOOL Initialize(IN CONST InitConfig &config);
VOID Terminate();
VOID BeginFrame();
VOID EndFrame();
BOOL ShouldClose();
public:
VOID ChangeScene(IN RefPtr<Scene> scene);
public:
RefPtr<Scene> CreateScene();
RefPtr<Texture> CreateTexture(IN CONST Span<CONST UINT8> &encodedData);
RefPtr<Texture> CreateTexture(IN PCUINT8 encodedData, IN SIZE_T encodedDataSize);
RefPtr<Texture> CreateTexture(IN PCUINT8 rgbaData, IN INT32 width, IN INT32 height);
public:
PVOID GetRendererHandle() CONST;
private:
VOID RenderDebugUI();
VOID ProcessEvents();
VOID UpdateGame();
VOID RenderGame();
private:
FLOAT32 m_updateTimer{};
RefPtr<Scene> m_activeScene{};
CONST RefPtr<EngineContext> m_context;
};
} // namespace ia::iae

View File

View File

@ -0,0 +1,67 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <IAEngine/Components/Component.hpp>
#include <IAEngine/Nodes/Transform.hpp>
#include <IAEngine/Texture.hpp>
namespace ia::iae
{
class Scene;
class Node : public Transform<Node>
{
public:
VIRTUAL VOID OnAdded(IN Scene* scene);
VIRTUAL VOID OnRemoved();
VIRTUAL VOID Draw();
VIRTUAL VOID Update();
VIRTUAL VOID Enable();
VIRTUAL VOID Disable();
public:
template<typename _component_type>
RefPtr<_component_type> AddComponent();
CONST Vector<RefPtr<IComponent>>& GetComponents() CONST
{
return m_components;
}
protected:
Scene* m_scene{};
BOOL m_isEnabled{true};
protected:
Vector<RefPtr<IComponent>> m_components;
friend class IComponent;
private:
VOID AddComponent(IN RefPtr<IComponent> component);
};
template<typename _component_type>
RefPtr<_component_type> Node::AddComponent()
{
const auto c = MakeRefPtr<_component_type>(this);
AddComponent(c);
return c;
}
} // namespace ia::iae

View File

@ -0,0 +1,110 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <IAEngine/Base.hpp>
namespace ia::iae
{
template<typename _node_type>
class Transform
{
public:
VOID SetLocalPosition(IN CONST iam::Vec3f &v)
{
m_local.Position = v;
RecalculatePosition();
}
VOID SetLocalScale(IN CONST iam::Vec3f &v)
{
m_local.Scale = v;
RecalculateScale();
}
VOID SetLocalRotation(IN CONST iam::Vec3f &v)
{
m_local.Rotation = v;
RecalculateRotation();
}
public:
CONST iam::Vec3f &GetPosition() CONST
{
return m_global.Position;
}
CONST iam::Vec3f &GetScale() CONST
{
return m_global.Scale;
}
CONST iam::Vec3f &GetRotation() CONST
{
return m_global.Rotation;
}
CONST iam::Vec3f &GetLocalPosition() CONST
{
return m_local.Position;
}
CONST iam::Vec3f &GetLocalScale() CONST
{
return m_local.Scale;
}
CONST iam::Vec3f &GetLocalRotation() CONST
{
return m_local.Rotation;
}
protected:
RefPtr<_node_type> m_parent{};
Vector<RefPtr<_node_type>> m_children{};
protected:
VOID RecalculatePosition()
{
m_global.Position = (m_parent ? m_parent->GetPosition() : iam::Vec3f{}) + m_local.Position;
for (auto &c : m_children)
c->RecalculatePosition();
}
VOID RecalculateRotation()
{
m_global.Rotation = (m_parent ? m_parent->GetRotation() : iam::Vec3f{}) + m_local.Rotation;
for (auto &c : m_children)
c->RecalculateRotation();
}
VOID RecalculateScale()
{
m_global.Scale = (m_parent ? m_parent->GetScale() : iam::Vec3f{}) + m_local.Scale;
for (auto &c : m_children)
c->RecalculateScale();
}
private:
struct
{
iam::Vec3f Position{0.0f, 0.0f, 0.0f};
iam::Vec3f Rotation{0.0f, 0.0f, 0.0f};
iam::Vec3f Scale{1.0f, 1.0f, 1.0f};
} m_local{}, m_global{};
};
} // namespace ia::iae

View File

@ -0,0 +1,31 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <IAEngine/Base.hpp>
namespace ia::iae
{
class Random
{
public:
STATIC VOID Initialize();
STATIC FLOAT32 Get();
STATIC INT32 GetInRange(IN INT32 min, IN INT32 max);
};
}

View File

@ -0,0 +1,48 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <IAEngine/Nodes/Node.hpp>
namespace ia::iae
{
class Engine;
class Scene
{
public:
Scene(IN Engine* engine);
~Scene();
VOID Draw();
VOID Update();
public:
VOID AddNode(IN RefPtr<Node> node);
VOID RemoveNode(IN RefPtr<Node> node);
public:
CONST Vector<RefPtr<Node>> &GetNodes() CONST
{
return m_nodes;
}
private:
Engine* CONST m_engine;
Vector<RefPtr<Node>> m_nodes;
};
} // namespace ia::iae

View File

@ -0,0 +1,54 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <IAEngine/Base.hpp>
namespace ia::iae
{
class Engine;
class Texture
{
public:
Texture(IN Engine* engine);
~Texture();
public:
VOID Draw(IN CONST iam::Vec3f& position, IN CONST iam::Vec3f& scale, IN FLOAT32 rotation, IN BOOL flipH, IN BOOL flipV, IN CONST iam::Vec4f& colorOverlay) CONST;
public:
INT32 GetWidth() CONST
{
return m_width;
}
INT32 GetHeight() CONST
{
return m_height;
}
private:
INT32 m_width;
INT32 m_height;
PVOID m_handle{};
Engine* CONST m_engine;
private:
friend class Engine;
};
} // namespace ia::iae

View File

@ -0,0 +1,64 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <IAEngine/Base.hpp>
namespace ia::iae
{
class TimePeriod
{
public:
TimePeriod()
{
}
TimePeriod(IN INT32 value, IN INT32 randomAdjustment)
{
SetValue(value);
SetRandomAdjustment(randomAdjustment);
}
VOID SetValue(IN INT32 value)
{
m_value = value;
}
VOID SetRandomAdjustment(IN INT32 adjustment)
{
m_randomAdjustment = adjustment;
}
FLOAT32 GetValue() CONST;
private:
INT32 m_value{};
INT32 m_randomAdjustment{};
};
class Time
{
public:
STATIC VOID Initialize();
STATIC VOID NextFrame();
STATIC INT64 GetCurrentTick();
STATIC INT64 GetCurrentSecond();
STATIC FLOAT32 GetFrameDeltaTime();
};
} // namespace ia::iae