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

12
.gitmodules vendored
View File

@ -1,3 +1,15 @@
[submodule "Dependencies/IACore"]
path = Dependencies/IACore
url = https://git.iasoft.dev/dev0/IACore
[submodule "Vendor/SDL"]
path = Vendor/SDL
url = https://github.com/libsdl-org/SDL
[submodule "Vendor/SDL_Mixer"]
path = Vendor/SDL_Mixer
url = https://github.com/libsdl-org/SDL_mixer
[submodule "Vendor/imgui"]
path = Vendor/imgui
url = https://github.com/ocornut/imgui
[submodule "Dependencies/IAMath"]
path = Dependencies/IAMath
url = https://git.iasoft.dev/dev0/IAMath

2
.vscode/launch.json vendored
View File

@ -11,7 +11,7 @@
"program": "${workspaceFolder}/build/bin/IAESandbox.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/",
"cwd": "${workspaceFolder}/Src/IAESandbox",
"environment": [],
"preLaunchTask": "build",
"console": "integratedTerminal"

View File

@ -9,7 +9,10 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
project(IAEngine)
add_subdirectory(Vendor/)
add_subdirectory(Dependencies/IACore)
add_subdirectory(Dependencies/IAMath)
add_subdirectory(Src/IAEngine)
add_subdirectory(Src/IAESandbox)

1
Dependencies/IAMath vendored Submodule

Submodule Dependencies/IAMath added at 536e9d0e58

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

4
Src/IAESandbox/imgui.ini Normal file
View File

@ -0,0 +1,4 @@
[Window][Debug##Default]
Pos=60,60
Size=400,400

View File

@ -1,7 +1,118 @@
#include <IAEngine/Components/SoundEmitter.hpp>
#include <IAEngine/Components/SpriteRenderer.hpp>
#include <IAEngine/IAEngine.hpp>
#include <IAEngine/Audio.hpp>
#include <iacore/file.hpp>
namespace ia
{
SIZE_T Find(IN CONST Vector<UINT8> &vec, IN SIZE_T start)
{
UINT8 Sig[5] = {0x89, 0x50, 0x4E, 0x47, 0x0D};
for (SIZE_T i = start; i < vec.size(); i++)
{
if (!memcmp(&vec[i], Sig, sizeof(Sig)))
return i;
}
return SIZE_MAX;
}
iae::Engine g_engine;
RefPtr<iae::Scene> scene;
RefPtr<iae::Node> sprite;
VOID InitializeGame()
{
auto s = iae::Audio::CreateSound(File::ReadToVector("Res/Audio/SFX/gunshot.wav"));
s.LoopTimes() = 0;
sprite = MakeRefPtr<iae::Node>();
const auto spriteRenderer = sprite->AddComponent<iae::SpriteRendererComponent>();
{
const auto data = File::ReadToVector("Res/Graphics/iae/010_1.iae");
IA_RELEASE_ASSERT(!memcmp(&data[0], "iae.", 4));
const auto frameCount = *reinterpret_cast<CONST UINT32 *>(&data[6]);
INT32 off = 10;
for (INT32 i = 0; i < frameCount; i++)
{
const auto t = g_engine.CreateTexture(&data[off], data.size() - off);
spriteRenderer->AddTexture(t);
off = Find(data, off + 1);
if (off == SIZE_MAX)
break;
}
}
iae::SpriteRendererComponent::Animation anim;
anim.ShouldLoop = false;
anim.Keys.pushBack(
iae::SpriteRendererComponent::AnimationKeyFrame{.Duration = 500,
.ColorOverlay = iam::Vec4f{0.25f, 0.0f, 0.0f, 1.0f},
.ShouldInterpolate = true,
.TextureHandle = 0});
anim.Keys.pushBack(
iae::SpriteRendererComponent::AnimationKeyFrame{.Duration = 500,
.ColorOverlay = iam::Vec4f{0.5f, 0.0f, 0.0f, 1.0f},
.ShouldInterpolate = true,
.TextureHandle = 1});
anim.Keys.pushBack(
iae::SpriteRendererComponent::AnimationKeyFrame{.Duration = 500,
.ColorOverlay = iam::Vec4f{0.75f, 0.0f, 0.0f, 1.0f},
.ShouldInterpolate = true,
.TextureHandle = 2});
anim.Keys.pushBack(
iae::SpriteRendererComponent::AnimationKeyFrame{.Duration = 500,
.ColorOverlay = iam::Vec4f{1.0f, 0.0f, 0.0f, 1.0f},
.ShouldInterpolate = true,
.TextureHandle = 3});
spriteRenderer->AddAnimation(anim);
spriteRenderer->BakeAnimations();
// const auto soundEmitter = sprite->AddComponent<iae::SoundEmitterComponent>();
// soundEmitter->SetSound(s);
sprite->SetLocalPosition({100, 100, 0});
scene = g_engine.CreateScene();
scene->AddNode(sprite);
g_engine.ChangeScene(scene);
}
VOID UpdateGame()
{
}
VOID TerminateGame()
{
}
} // namespace ia
int main(int argc, char *argv[])
{
ia::g_engine.Initialize({.GameName = "IAE Sandbox", .WindowWidth = 800, .WindowHeight = 600});
ia::InitializeGame();
while (!ia::g_engine.ShouldClose())
{
ia::g_engine.BeginFrame();
ia::UpdateGame();
ia::g_engine.EndFrame();
}
ia::TerminateGame();
ia::g_engine.Terminate();
return 0;
}

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

38
Vendor/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,38 @@
# -----------------------------------------------
# SDL3
# -----------------------------------------------
set(SDL_TEST_LIBRARY OFF)
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(SDL/)
# -----------------------------------------------
# SDL Mixer
# -----------------------------------------------
set(SDLMIXER_VENDORED OFF)
add_subdirectory(SDL_Mixer/)
# -----------------------------------------------
# IMGUI
# -----------------------------------------------
add_library(
ImGui STATIC
"imgui/imgui.cpp"
"imgui/imgui_demo.cpp"
"imgui/imgui_draw.cpp"
"imgui/imgui_tables.cpp"
"imgui/imgui_widgets.cpp"
"imgui/backends/imgui_impl_sdl3.cpp"
"imgui/backends/imgui_impl_sdlrenderer3.cpp"
)
target_include_directories(
ImGui PRIVATE
"SDL/include"
)
target_include_directories(
ImGui PUBLIC
"imgui/"
"imgui/backends"
)

1
Vendor/SDL vendored Submodule

Submodule Vendor/SDL added at 3572be3998

1
Vendor/SDL_Mixer vendored Submodule

Submodule Vendor/SDL_Mixer added at 1c49b45a0b

1
Vendor/imgui vendored Submodule

Submodule Vendor/imgui added at 02af06ea5f

4
imgui.ini Normal file
View File

@ -0,0 +1,4 @@
[Window][Debug##Default]
Pos=60,60
Size=400,400