Batch Renderer 1/2

This commit is contained in:
Isuru Samarathunga
2025-11-02 02:45:13 +05:30
parent ab484b4016
commit 0bffbbf66c
50 changed files with 21627 additions and 18 deletions

6
.gitmodules vendored
View File

@ -14,3 +14,9 @@
[submodule "Vendor/SDL"]
path = Vendor/SDL
url = https://github.com/I-A-S/SDL
[submodule "Vendor/glm"]
path = Vendor/glm
url = https://github.com/g-truc/glm
[submodule "Vendor/pugixml"]
path = Vendor/pugixml
url = https://github.com/zeux/pugixml

20
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,20 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(Windows) Launch",
"type": "cppvsdbg",
"request": "launch",
"args": [],
"stopAtEntry": false,
"environment": [],
"console": "integratedTerminal",
"program": "${workspaceFolder}/Build/Windows/bin/IAERPG.exe",
"cwd": "${workspaceFolder}/Samples/RPG",
"preLaunchTask": "build",
}
]
}

14
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,14 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "build",
"command": "python3 Tools/Scripts/Builder/Build.py",
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View File

@ -0,0 +1,16 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec2 inTexCoord;
layout(location = 1) in vec4 inVertexColor;
layout(location = 0) out vec4 outColor;
layout(set = 2, binding = 0) uniform sampler2D texSampler;
void main()
{
outColor = inVertexColor;
if(outColor.w < 0.1)
discard;
}

View File

@ -0,0 +1,34 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
struct SpriteInstanceData
{
mat4 Transform;
vec4 TexCoords;
vec4 Color;
};
layout (location = 0) in vec2 inPosition;
layout (location = 1) in vec2 inTexCoord;
layout(location = 0) out vec2 outTexCoord;
layout(location = 1) out vec4 outVertexColor;
layout(set = 0, binding = 0) readonly buffer SBO_SpriteData {
SpriteInstanceData data[];
} sboSpriteData;
layout(set = 1, binding = 0) uniform UBO_Vertex_PerScene {
mat4 projection;
} uboPerScene;
layout(set = 1, binding = 1) uniform UBO_Vertex_PerFrame {
mat4 view;
} uboPerFrame;
void main()
{
SpriteInstanceData spriteData = sboSpriteData.data[gl_InstanceIndex];
gl_Position = uboPerScene.projection * uboPerFrame.view * spriteData.Transform * vec4(inPosition, 0.0f, 1.0f);
outTexCoord = inTexCoord;
outVertexColor = spriteData.Color;
}

View File

@ -3,7 +3,6 @@
layout (location = 0) in vec2 inPosition;
layout (location = 1) in vec2 inTexCoord;
layout (location = 2) in vec4 inVertexColor;
layout(location = 0) out vec2 outTexCoord;
layout(location = 1) out vec4 outVertexColor;
@ -22,5 +21,5 @@ void main()
{
gl_Position = uboPerScene.projection * uboPerFrame.view * uboPerDraw.model * vec4(inPosition, 0.0f, 1.0f);
outTexCoord = inTexCoord;
outVertexColor = inVertexColor;
//outVertexColor = inVertexColor;
}

View File

@ -1,5 +1,6 @@
set(SRC_FILES
"Src/imp/cpp/Main.cpp"
"Src/imp/cpp/Game.cpp"
)
add_executable(IAERPG ${SRC_FILES})

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

View File

@ -0,0 +1,3 @@
This asset pack is taken from here https://kenmi-art.itch.io/cute-fantasy-rpg
This is the free version

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,10 @@
Hello! Thank you for downloading the Cute Fantasy asset pack.
This project will be getting updates over time. This version of the asset pack is not final and there will be few more additional sprites.
License - Free Version
- You can use these assets in non-commercial projects.
- You can modify the assets.
- You can not redistribute or resale, even if modified
If you like the asset pack leave a comment. It helps to support the asset pack and get more people to see it. Thanks!

View File

@ -0,0 +1,55 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IASoft (PVT) LTD (oss@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/>.
#include <Game.hpp>
namespace ia::iae::rpg
{
GameRequestedConfig *RequestEngineConfig()
{
STATIC GameRequestedConfig EngineConfig{
.DesignWidth = 800,
.DesignHeight = 600,
.WindowWidth = 800,
.WindowHeight = 600,
};
return &EngineConfig;
}
VOID OnInitialize()
{
}
VOID OnTerminate()
{
}
VOID OnDebugDraw()
{
}
VOID OnFixedUpdate()
{
}
VOID OnUpdate(IN FLOAT32 deltaTime)
{
}
VOID OnResize(IN INT32 newWidth, IN INT32 newHeight)
{
}
} // namespace ia::iae::rpg

View File

@ -1,6 +1,54 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IASoft (PVT) LTD (oss@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/>.
int main(int agrc, char** argv)
#include <Game.hpp>
C_DECL(GameRequestedConfig *Game_GetConfigRequest())
{
return 0;
}
return rpg::RequestEngineConfig();
}
C_DECL(VOID Game_OnInitialize())
{
rpg::OnInitialize();
}
C_DECL(VOID Game_OnTerminate())
{
rpg::OnTerminate();
}
C_DECL(VOID Game_OnDebugDraw())
{
rpg::OnDebugDraw();
}
C_DECL(VOID Game_OnFixedUpdate())
{
rpg::OnFixedUpdate();
}
C_DECL(VOID Game_OnUpdate(IN FLOAT32 deltaTime))
{
rpg::OnUpdate(deltaTime);
}
C_DECL(VOID Game_OnResize(IN INT32 newWidth, IN INT32 newHeight))
{
rpg::OnResize(newWidth, newHeight);
}
IAENGINE_RUN("IAE RPG", "com.iasoft.iae.sample.rpg", "IASoft (PVT) LTD", "IASoft (PVT) LTD", 1, 0, 0);

View File

@ -0,0 +1,24 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IASoft (PVT) LTD (oss@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/LibInterface.hpp>
namespace ia::iae::rpg
{
}

View File

@ -0,0 +1,31 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IASoft (PVT) LTD (oss@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 <Base.hpp>
namespace ia::iae::rpg
{
GameRequestedConfig* RequestEngineConfig();
VOID OnInitialize();
VOID OnTerminate();
VOID OnDebugDraw();
VOID OnFixedUpdate();
VOID OnUpdate(IN FLOAT32 deltaTime);
VOID OnResize(IN INT32 newWidth, IN INT32 newHeight);
}

View File

@ -2,6 +2,8 @@ set(SRC_FILES
"imp/cpp/IAEngine.cpp"
"imp/cpp/EmbeddedResources.cpp"
"imp/cpp/Renderer.cpp"
)
add_library(IAEngine STATIC ${SRC_FILES})
@ -9,5 +11,5 @@ add_library(IAEngine STATIC ${SRC_FILES})
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 pugixml::pugixml glm::glm)
target_link_libraries(IAEngine PRIVATE ZLIB::ZLIBSTATIC SDL3::SDL3 SDL3_mixer::SDL3_mixer Freetype::Freetype)

File diff suppressed because one or more lines are too long

View File

@ -14,17 +14,105 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <IAEngine/IAEngine.hpp>
#include <EmbeddedResources.hpp>
#include <IAEngine/LibInterface.hpp>
#include <Renderer.hpp>
namespace ia::iae
{
IAEngine::IAEngine()
String g_gameName;
String g_gamePackageName;
String g_gameDeveloperName;
String g_gamePublisherName;
IA_VERSION_TYPE g_gameVersion{};
Vec2 g_designViewport;
SDL_Window *g_windowHandle{};
#if defined(__IA_DEBUG) && __IA_DEBUG
BOOL g_isDebugMode = true;
#else
BOOL g_isDebugMode = false;
#endif
INT32 Run(IN CONST String &name, IN CONST String &packageName, IN CONST String &developerName,
IN CONST String &publisherName, IN IA_VERSION_TYPE version)
{
g_gameName = name;
g_gameVersion = version;
g_gamePackageName = packageName;
g_gameDeveloperName = developerName;
g_gamePublisherName = publisherName;
const auto config = Game_GetConfigRequest();
g_designViewport.x = config->DesignWidth;
g_designViewport.y = config->DesignHeight;
IAE_LOG_INFO("Booting IAEngine for ", g_gameName);
SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeRight");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD))
THROW_UNKNOWN("Failed to intialize SDL: ", SDL_GetError());
if (!(g_windowHandle = SDL_CreateWindow(g_gameName.c_str(), config->WindowWidth, config->WindowHeight,
SDL_WINDOW_RESIZABLE)))
THROW_UNKNOWN("Failed to create the SDL window: ", SDL_GetError());
#if __ANDROID__
SDL_SetWindowFullscreen(g_windowHandle, true);
#endif
const auto gameVersion = g_gameVersion;
SDL_SetAppMetadata(g_gameName.c_str(), IA_STRINGIFY_VERSION(gameVersion).c_str(), g_gamePackageName.c_str());
EmbeddedResources::Initialize();
IAEngine::Initialize();
SDL_Event event{};
while (true)
{
SDL_PollEvent(&event);
if (event.type == SDL_EVENT_QUIT)
break;
IAEngine::ProcessEvent(&event);
IAEngine::Update();
}
IAEngine::Terminate();
EmbeddedResources::Terminate();
SDL_DestroyWindow(g_windowHandle);
IAE_LOG_INFO("Shutting down IAEngine");
return 0;
}
IAEngine::~IAEngine()
} // namespace ia::iae
namespace ia::iae
{
VOID IAEngine::Initialize()
{
Renderer::Initialize(IVec2{g_designViewport.x, g_designViewport.y});
}
}
VOID IAEngine::Terminate()
{
Renderer::Terminate();
}
VOID IAEngine::Update()
{
Renderer::Update();
}
VOID IAEngine::ProcessEvent(IN PVOID _event)
{
const auto event = (SDL_Event *) _event;
}
} // namespace ia::iae

View File

@ -0,0 +1,685 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IASoft (PVT) LTD (oss@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/>.
#include <Renderer.hpp>
#include <EmbeddedResources.hpp>
namespace ia::iae
{
STATIC CONSTEXPR INT32 MAX_SPRITE_COUNT = 100000;
#pragma pack(push, 1)
struct SpriteInstanceData
{
Mat4 Transform{1.0f};
Vec4 TexCoords{};
Vec4 Color{1.0f, 1.0f, 1.0f, 1.0f};
};
#pragma pack(pop)
struct TextureData
{
PUINT8 Pixels{};
INT32 Width{};
INT32 Height{};
INT32 TileWidth{};
INT32 TileHeight{};
INT32 TileCountX{};
INT32 TileCountY{};
SDL_GPUTexture *BakedHandle{};
};
} // namespace ia::iae
namespace ia::iae
{
IVec2 g_screenExtent{};
SDL_GPUDevice *g_gpuDevice{};
RenderPipeline *g_debugPipeline{};
RenderPipeline *g_geometryPipeline{};
SDL_GPUSampler *g_linearClampSampler{};
SDL_GPUSampler *g_linearRepeatSampler{};
Mat4 g_viewMatrix{1.0f};
Vec2 g_cameraPosition{};
Mat4 g_projectionMatrix{1.0f};
Geometry *g_quadGeometry;
Vector<TextureData> g_texureData;
Vector<SpriteInstanceData> g_staticSprites;
Vector<SpriteInstanceData> g_dynamicSprites;
SDL_GPUBuffer *g_staticSpriteDataBuffer{};
SDL_GPUBuffer *g_dynamicSpriteDataBuffer{};
SDL_GPUTransferBuffer *g_spriteDataStagingBuffer{};
SDL_GPUTexture *g_defaultTexture{};
SDL_GPUTexture *g_activeTextureAtlas{};
Map<Handle, Vec4> g_activeTextureAtlasUVMap;
EXTERN BOOL g_isDebugMode;
EXTERN Vec2 g_designViewport;
EXTERN SDL_Window *g_windowHandle;
} // namespace ia::iae
namespace ia::iae
{
VOID Renderer::Initialize(IN IVec2 screenExtent)
{
g_screenExtent = screenExtent;
InitializeGPU();
InitializeSampler();
InitializePipelines();
InitializeGeometries();
InitializeDrawData();
InitializeTextures();
}
VOID Renderer::Terminate()
{
SDL_WaitForGPUIdle(g_gpuDevice);
delete g_debugPipeline;
delete g_geometryPipeline;
if (g_defaultTexture)
DestroyTexture(g_defaultTexture);
if (g_activeTextureAtlas && (g_activeTextureAtlas != g_defaultTexture))
DestroyTexture(g_activeTextureAtlas);
DestroyBuffer(g_staticSpriteDataBuffer);
DestroyBuffer(g_dynamicSpriteDataBuffer);
SDL_ReleaseGPUTransferBuffer(g_gpuDevice, g_spriteDataStagingBuffer);
SDL_ReleaseGPUSampler(g_gpuDevice, g_linearClampSampler);
SDL_ReleaseGPUSampler(g_gpuDevice, g_linearRepeatSampler);
DestroyGeometry(g_quadGeometry);
SDL_ReleaseWindowFromGPUDevice(g_gpuDevice, g_windowHandle);
SDL_DestroyGPUDevice(g_gpuDevice);
}
VOID Renderer::Update()
{
DrawStaticSpriteTopLeft(0, 0, {10.0f, 0.0f}, {100.0f, 100.0f}, 0.0f);
DrawStaticSpriteTopLeft(0, 0, {200.0f, 300.0f}, {100.0f, 100.0f}, 0.0f);
Render();
}
VOID Renderer::Render()
{
STATIC SDL_GPURenderPass *ActiveRenderPass{};
STATIC SDL_GPUCommandBuffer *ActiveCommandBuffer{};
STATIC SDL_GPUColorTargetInfo ActiveColorTargetInfo{.clear_color = SDL_FColor{0.0f, 0.0f, 0.0f, 1.0f},
.load_op = SDL_GPU_LOADOP_CLEAR,
.store_op = SDL_GPU_STOREOP_STORE};
if (!(ActiveCommandBuffer = SDL_AcquireGPUCommandBuffer(g_gpuDevice)))
THROW_UNKNOWN("Failed to acquire SDL GPU command buffer: ", SDL_GetError());
SDL_GPUTexture *swapChainTexture{};
if (!SDL_WaitAndAcquireGPUSwapchainTexture(ActiveCommandBuffer, g_windowHandle, &swapChainTexture, nullptr,
nullptr))
THROW_UNKNOWN("Failed to acquire SDL GPU Swapchain texture: ", SDL_GetError());
if (!swapChainTexture)
return;
ActiveColorTargetInfo.texture = swapChainTexture;
ActiveColorTargetInfo.clear_color = SDL_FColor{0.3f, 0.3f, 0.3f, 1.0f};
ActiveRenderPass = SDL_BeginGPURenderPass(ActiveCommandBuffer, &ActiveColorTargetInfo, 1, nullptr);
SDL_BindGPUGraphicsPipeline(ActiveRenderPass, g_debugPipeline->GetHandle());
SDL_PushGPUVertexUniformData(ActiveCommandBuffer, 0, &g_projectionMatrix, sizeof(Mat4));
SDL_PushGPUVertexUniformData(ActiveCommandBuffer, 1, &g_viewMatrix, sizeof(Mat4));
SDL_GPUBufferBinding bufferBindings[] = {{.buffer = ((Geometry *) g_quadGeometry)->VertexBuffer, .offset = 0},
{.buffer = ((Geometry *) g_quadGeometry)->IndexBuffer, .offset = 0}};
SDL_BindGPUVertexBuffers(ActiveRenderPass, 0, &bufferBindings[0], 1);
SDL_BindGPUIndexBuffer(ActiveRenderPass, &bufferBindings[1], SDL_GPU_INDEXELEMENTSIZE_32BIT);
SDL_GPUTextureSamplerBinding textureBinding{.texture = g_activeTextureAtlas,
.sampler = GetSampler_LinearRepeat()};
SDL_BindGPUFragmentSamplers(ActiveRenderPass, 0, &textureBinding, 1);
if (g_staticSprites.size())
{
CopyToDeviceLocalBuffer(g_spriteDataStagingBuffer, g_staticSpriteDataBuffer, g_staticSprites.data(),
g_staticSprites.size() * sizeof(SpriteInstanceData));
SDL_BindGPUVertexStorageBuffers(ActiveRenderPass, 0, &g_staticSpriteDataBuffer, 1);
SDL_DrawGPUIndexedPrimitives(ActiveRenderPass, g_quadGeometry->IndexCount, g_staticSprites.size(), 0, 0, 0);
}
if (g_dynamicSprites.size())
{
CopyToDeviceLocalBuffer(g_spriteDataStagingBuffer, g_dynamicSpriteDataBuffer, g_dynamicSprites.data(),
g_dynamicSprites.size() * sizeof(SpriteInstanceData));
SDL_BindGPUVertexStorageBuffers(ActiveRenderPass, 0, &g_dynamicSpriteDataBuffer, 1);
SDL_DrawGPUIndexedPrimitives(ActiveRenderPass, g_quadGeometry->IndexCount, g_dynamicSprites.size(), 0, 0,
0);
}
g_staticSprites.resize(0);
g_dynamicSprites.resize(0);
SDL_EndGPURenderPass(ActiveRenderPass);
SDL_SubmitGPUCommandBuffer(ActiveCommandBuffer);
}
Vec2 Renderer::GetCameraPosition()
{
return g_cameraPosition;
}
VOID Renderer::SetCameraPosition(IN Vec2 position)
{
g_cameraPosition = position;
g_viewMatrix = glm::lookAtLH(glm::vec3{g_cameraPosition, -1.0f}, {g_cameraPosition, 0.0f}, {0.0f, 1.0f, 0.0f});
}
} // namespace ia::iae
namespace ia::iae
{
VOID Renderer::DrawStaticSpriteTopLeft(IN Handle texture, IN INT32 tileIndex, IN Vec2 position, IN Vec2 scale,
IN FLOAT32 rotation)
{
Mat4 transform = glm::translate(glm::mat4(1.0f), glm::vec3{position.x, position.y, 0});
transform = glm::rotate(transform, rotation, glm::vec3(0.0f, 0.0f, 1.0f));
transform = glm::scale(transform, glm::vec3{scale.x, scale.y, 1.0f});
g_staticSprites.pushBack({.Transform = transform,
.TexCoords = GetTextureAtlasCoordinates(texture, tileIndex),
.Color = {1.0f, 1.0f, 1.0f, 1.0f}});
}
VOID Renderer::DrawDynamicSpriteTopLeft(IN Handle texture, IN INT32 tileIndex, IN Vec2 position, IN Vec2 scale,
IN FLOAT32 rotation)
{
Mat4 transform = glm::translate(glm::mat4(1.0f), glm::vec3{position.x, position.y, 0});
transform = glm::rotate(transform, rotation, glm::vec3(0.0f, 0.0f, 1.0f));
transform = glm::scale(transform, glm::vec3{scale.x, scale.y, 1.0f});
g_dynamicSprites.pushBack({.Transform = transform,
.TexCoords = GetTextureAtlasCoordinates(texture, tileIndex),
.Color = {1.0f, 1.0f, 1.0f, 1.0f}});
}
Geometry *Renderer::CreateGeometry(IN CONST Vector<GeometryVertex> &vertices, IN CONST Vector<INT32> &indices)
{
const auto mesh = new Geometry();
mesh->VertexBuffer = CreateDeviceLocalBuffer(SDL_GPU_BUFFERUSAGE_VERTEX, vertices.data(),
static_cast<UINT32>(vertices.size() * sizeof(vertices[0])));
mesh->IndexBuffer = CreateDeviceLocalBuffer(SDL_GPU_BUFFERUSAGE_INDEX, indices.data(),
static_cast<UINT32>(indices.size() * sizeof(indices[0])));
mesh->IndexCount = static_cast<UINT32>(indices.size());
return mesh;
}
VOID Renderer::DestroyGeometry(IN Geometry *geometry)
{
DestroyBuffer(geometry->VertexBuffer);
DestroyBuffer(geometry->IndexBuffer);
delete geometry;
}
} // namespace ia::iae
namespace ia::iae
{
Handle Renderer::CreateTexture(IN PCUINT8 rgbaData, IN INT32 width, IN INT32 height, IN INT32 tileCountX,
IN INT32 tileCountY)
{
const auto pixelDataSize = width * height * 4;
g_texureData.pushBack({.Pixels = new UINT8[pixelDataSize],
.Width = width,
.Height = height,
.TileWidth = width / tileCountX,
.TileHeight = height / tileCountY,
.TileCountX = tileCountX,
.TileCountY = tileCountY,
.BakedHandle = nullptr});
ia_memcpy(g_texureData.back().Pixels, rgbaData, pixelDataSize);
return g_texureData.size() - 1;
}
VOID Renderer::DestroyTexture(IN Handle texture)
{
auto &t = g_texureData[texture];
if (t.Pixels)
delete[] t.Pixels;
if (t.BakedHandle)
DestroyTexture(t.BakedHandle);
t.Pixels = nullptr;
t.BakedHandle = nullptr;
}
VOID Renderer::BakeTexture(IN Handle texture)
{
auto &t = g_texureData[texture];
t.BakedHandle = CreateTexture(SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET, t.Width,
t.Height, t.Width, t.Pixels);
}
VOID Renderer::SetTextureAtlas(IN CONST Vector<Handle> textures)
{
g_activeTextureAtlasUVMap = Map<Handle, Vec4>();
if (g_activeTextureAtlas)
DestroyTexture(g_activeTextureAtlas);
INT32 atlasWidth{0}, atlasHeight{0};
for (const auto &t : textures)
{
const auto &d = g_texureData[t];
atlasWidth += d.Width;
if (d.Height > atlasHeight)
atlasHeight = d.Height;
}
const auto pixels = new UINT8[atlasWidth * atlasHeight * 4];
INT32 atlasCursor{0};
for (const auto &t : textures)
{
const auto &d = g_texureData[t];
for (INT32 y = 0; y < d.Height; y++)
ia_memcpy(&pixels[atlasCursor + (y * atlasWidth)], d.Pixels, d.Width * 4);
g_activeTextureAtlasUVMap[t] =
Vec4(((FLOAT32) atlasCursor) / ((FLOAT32) atlasWidth), 0.0f,
((FLOAT32) d.Width) / ((FLOAT32) atlasWidth), ((FLOAT32) d.Height) / ((FLOAT32) atlasHeight));
atlasCursor += d.Width;
}
g_activeTextureAtlas = CreateTexture(SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET,
atlasWidth, atlasHeight, atlasWidth, pixels);
delete[] pixels;
}
Vec4 Renderer::GetTextureAtlasCoordinates(IN Handle texture, IN INT32 tileIndex)
{
const auto &d = g_texureData[texture];
const auto &t = g_activeTextureAtlasUVMap[texture];
return t;
}
} // namespace ia::iae
namespace ia::iae
{
SDL_GPUTexture *Renderer::CreateTexture(IN SDL_GPUTextureUsageFlags usage, IN INT32 width, IN INT32 height,
IN INT32 stride, IN PCUINT8 rgbaData, IN SDL_GPUTextureFormat format,
IN BOOL generateMipmaps)
{
const auto mipLevels =
generateMipmaps ? ia_max((UINT32) (floor(log2(ia_max(width, height))) + 1), (UINT32) 1) : (UINT32) 1;
STATIC Vector<UINT8> TMP_COLOR_BUFFER;
SDL_GPUTextureCreateInfo createInfo{.type = SDL_GPU_TEXTURETYPE_2D,
.format = format,
.usage = usage,
.width = (UINT32) width,
.height = (UINT32) height,
.layer_count_or_depth = 1,
.num_levels = (UINT32) mipLevels,
.sample_count = SDL_GPU_SAMPLECOUNT_1};
const auto result = SDL_CreateGPUTexture(g_gpuDevice, &createInfo);
if (!result)
{
THROW_UNKNOWN("Failed to create a SDL GPU Texture: ", SDL_GetError());
return nullptr;
}
if (rgbaData)
{
TMP_COLOR_BUFFER.resize(width * height * 4);
if (stride == width)
{
for (SIZE_T i = 0; i < TMP_COLOR_BUFFER.size() >> 2; i++)
{
const auto a = static_cast<FLOAT32>(rgbaData[i * 4 + 3]) / 255.0f;
TMP_COLOR_BUFFER[i * 4 + 0] = static_cast<UINT8>(rgbaData[i * 4 + 0] * a);
TMP_COLOR_BUFFER[i * 4 + 1] = static_cast<UINT8>(rgbaData[i * 4 + 1] * a);
TMP_COLOR_BUFFER[i * 4 + 2] = static_cast<UINT8>(rgbaData[i * 4 + 2] * a);
TMP_COLOR_BUFFER[i * 4 + 3] = rgbaData[i * 4 + 3];
}
}
else
{
for (INT32 y = 0; y < height; y++)
{
for (INT32 x = 0; x < width; x++)
{
const auto p = &rgbaData[(x + y * stride) * 4];
const auto a = static_cast<FLOAT32>(p[3]) / 255.0f;
TMP_COLOR_BUFFER[(x + y * width) * 4 + 0] = static_cast<UINT8>(p[0] * a);
TMP_COLOR_BUFFER[(x + y * width) * 4 + 1] = static_cast<UINT8>(p[1] * a);
TMP_COLOR_BUFFER[(x + y * width) * 4 + 2] = static_cast<UINT8>(p[2] * a);
TMP_COLOR_BUFFER[(x + y * width) * 4 + 3] = p[3];
}
}
}
SDL_GPUTransferBufferCreateInfo stagingBufferCreateInfo{.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
.size = (UINT32) width * (UINT32) height * 4};
const auto stagingBuffer = SDL_CreateGPUTransferBuffer(g_gpuDevice, &stagingBufferCreateInfo);
const auto mappedPtr = SDL_MapGPUTransferBuffer(g_gpuDevice, stagingBuffer, false);
SDL_memcpy(mappedPtr, TMP_COLOR_BUFFER.data(), width * height * 4);
SDL_UnmapGPUTransferBuffer(g_gpuDevice, stagingBuffer);
auto cmdBuffer = SDL_AcquireGPUCommandBuffer(g_gpuDevice);
const auto copyPass = SDL_BeginGPUCopyPass(cmdBuffer);
SDL_GPUTextureTransferInfo transferInfo{.transfer_buffer = stagingBuffer, .offset = 0};
SDL_GPUTextureRegion region{.texture = result, .w = (UINT32) width, .h = (UINT32) height, .d = 1};
SDL_UploadToGPUTexture(copyPass, &transferInfo, &region, false);
SDL_EndGPUCopyPass(copyPass);
SDL_SubmitGPUCommandBuffer(cmdBuffer);
SDL_WaitForGPUIdle(g_gpuDevice);
SDL_ReleaseGPUTransferBuffer(g_gpuDevice, stagingBuffer);
if (mipLevels > 1)
{
cmdBuffer = SDL_AcquireGPUCommandBuffer(g_gpuDevice);
SDL_GenerateMipmapsForGPUTexture(cmdBuffer, result);
SDL_SubmitGPUCommandBuffer(cmdBuffer);
SDL_WaitForGPUIdle(g_gpuDevice);
}
}
return result;
}
SDL_GPUBuffer *Renderer::CreateDeviceLocalBuffer(IN SDL_GPUBufferUsageFlags usage, IN PCVOID data,
IN UINT32 dataSize)
{
SDL_GPUBufferCreateInfo createInfo{.usage = usage, .size = dataSize};
const auto result = SDL_CreateGPUBuffer(g_gpuDevice, &createInfo);
if (!result)
{
THROW_UNKNOWN("Failed to create a SDL GPU Buffer: ", SDL_GetError());
return nullptr;
}
if (data && dataSize)
CopyToDeviceLocalBuffer(result, data, dataSize);
return result;
}
VOID Renderer::CopyToDeviceLocalBuffer(IN SDL_GPUBuffer *buffer, IN PCVOID data, IN UINT32 dataSize)
{
SDL_GPUTransferBufferCreateInfo stagingBufferCreateInfo{.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
.size = dataSize};
const auto stagingBuffer = SDL_CreateGPUTransferBuffer(g_gpuDevice, &stagingBufferCreateInfo);
CopyToDeviceLocalBuffer(stagingBuffer, buffer, data, dataSize);
SDL_ReleaseGPUTransferBuffer(g_gpuDevice, stagingBuffer);
}
VOID Renderer::CopyToDeviceLocalBuffer(IN SDL_GPUTransferBuffer *stagingBuffer, IN SDL_GPUBuffer *buffer,
IN PCVOID data, IN UINT32 dataSize)
{
const auto mappedPtr = SDL_MapGPUTransferBuffer(g_gpuDevice, stagingBuffer, false);
SDL_memcpy(mappedPtr, data, dataSize);
SDL_UnmapGPUTransferBuffer(g_gpuDevice, stagingBuffer);
const auto cmdBuffer = SDL_AcquireGPUCommandBuffer(g_gpuDevice);
const auto copyPass = SDL_BeginGPUCopyPass(cmdBuffer);
SDL_GPUTransferBufferLocation src{.transfer_buffer = stagingBuffer, .offset = 0};
SDL_GPUBufferRegion dst{.buffer = buffer, .offset = 0, .size = dataSize};
SDL_UploadToGPUBuffer(copyPass, &src, &dst, false);
SDL_EndGPUCopyPass(copyPass);
SDL_SubmitGPUCommandBuffer(cmdBuffer);
SDL_WaitForGPUIdle(g_gpuDevice);
}
VOID Renderer::DestroyTexture(IN SDL_GPUTexture *handle)
{
if (!handle)
return;
SDL_ReleaseGPUTexture(g_gpuDevice, handle);
}
VOID Renderer::DestroyBuffer(IN SDL_GPUBuffer *handle)
{
if (!handle)
return;
SDL_ReleaseGPUBuffer(g_gpuDevice, handle);
}
SDL_GPUSampler *Renderer::GetSampler_LinearClamp()
{
return g_linearClampSampler;
}
SDL_GPUSampler *Renderer::GetSampler_LinearRepeat()
{
return g_linearRepeatSampler;
}
} // namespace ia::iae
namespace ia::iae
{
VOID Renderer::InitializePipelines()
{
g_debugPipeline = new RenderPipeline(
RenderPipeline::StageDesc{
.SourceData = EmbeddedResources::GetResource("Shaders/Debug.vert"),
.SamplerCount = 0,
.UniformBufferCount = 2,
.StorageBufferCount = 1,
},
RenderPipeline::StageDesc{
.SourceData = EmbeddedResources::GetResource("Shaders/Debug.frag"),
.SamplerCount = 1,
.UniformBufferCount = 0,
.StorageBufferCount = 0,
},
true);
g_geometryPipeline = new RenderPipeline(
RenderPipeline::StageDesc{
.SourceData = EmbeddedResources::GetResource("Shaders/Geometry.vert"),
.SamplerCount = 0,
.UniformBufferCount = 3,
.StorageBufferCount = 0,
},
RenderPipeline::StageDesc{
.SourceData = EmbeddedResources::GetResource("Shaders/Geometry.frag"),
.SamplerCount = 1,
.UniformBufferCount = 1,
.StorageBufferCount = 0,
},
true);
}
RenderPipeline::RenderPipeline(IN CONST StageDesc &vertexStageDesc, IN CONST StageDesc &pixelStageDesc,
IN BOOL enableVertexBuffer)
{
SDL_GPUShader *vertexShader{};
SDL_GPUShader *pixelShader{};
SDL_GPUShaderCreateInfo shaderCreateInfo = {
.entrypoint = "main",
.format = SDL_GPU_SHADERFORMAT_SPIRV,
.num_storage_textures = 0,
.num_storage_buffers = 0,
};
shaderCreateInfo.stage = SDL_GPU_SHADERSTAGE_VERTEX;
shaderCreateInfo.code = vertexStageDesc.SourceData.data();
shaderCreateInfo.code_size = vertexStageDesc.SourceData.size();
shaderCreateInfo.num_samplers = vertexStageDesc.SamplerCount;
shaderCreateInfo.num_uniform_buffers = vertexStageDesc.UniformBufferCount;
shaderCreateInfo.num_storage_buffers = vertexStageDesc.StorageBufferCount;
if (!(vertexShader = SDL_CreateGPUShader(g_gpuDevice, &shaderCreateInfo)))
THROW_UNKNOWN("Failed to create a SDL shader: ", SDL_GetError());
shaderCreateInfo.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
shaderCreateInfo.code = pixelStageDesc.SourceData.data();
shaderCreateInfo.code_size = pixelStageDesc.SourceData.size();
shaderCreateInfo.num_samplers = pixelStageDesc.SamplerCount;
shaderCreateInfo.num_uniform_buffers = pixelStageDesc.UniformBufferCount;
shaderCreateInfo.num_storage_buffers = pixelStageDesc.StorageBufferCount;
if (!(pixelShader = SDL_CreateGPUShader(g_gpuDevice, &shaderCreateInfo)))
THROW_UNKNOWN("Failed to create a SDL shader: ", SDL_GetError());
SDL_GPUColorTargetDescription colorTargetDesc = {
.format = SDL_GetGPUSwapchainTextureFormat(g_gpuDevice, g_windowHandle),
.blend_state = {.src_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE,
.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
.color_blend_op = SDL_GPU_BLENDOP_ADD,
.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE,
.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
.alpha_blend_op = SDL_GPU_BLENDOP_ADD,
.enable_blend = true,
.enable_color_write_mask = false}};
SDL_GPUVertexBufferDescription vertexBufferDesc = {
.slot = 0,
.pitch = sizeof(GeometryVertex),
.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX,
.instance_step_rate = 0,
};
SDL_GPUVertexAttribute vertexAttributes[] = {
{.location = 0, .buffer_slot = 0, .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, .offset = 0},
{.location = 1, .buffer_slot = 0, .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, .offset = sizeof(Vec2)}};
SDL_GPUGraphicsPipelineCreateInfo createInfo = {
.vertex_shader = vertexShader,
.fragment_shader = pixelShader,
.vertex_input_state = SDL_GPUVertexInputState{.vertex_buffer_descriptions = &vertexBufferDesc,
.num_vertex_buffers = enableVertexBuffer ? (UINT32) 1 : 0,
.vertex_attributes = vertexAttributes,
.num_vertex_attributes = enableVertexBuffer ? (UINT32) 2 : 0},
.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
.rasterizer_state = SDL_GPURasterizerState{.fill_mode = SDL_GPU_FILLMODE_FILL,
.cull_mode = SDL_GPU_CULLMODE_NONE,
.front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE,
.enable_depth_clip = true},
.target_info = {.color_target_descriptions = &colorTargetDesc,
.num_color_targets = 1,
.has_depth_stencil_target = false},
};
if (!(m_handle = SDL_CreateGPUGraphicsPipeline(g_gpuDevice, &createInfo)))
THROW_UNKNOWN("Failed to create a SDL graphics pipeline: ", SDL_GetError());
SDL_ReleaseGPUShader(g_gpuDevice, pixelShader);
SDL_ReleaseGPUShader(g_gpuDevice, vertexShader);
}
RenderPipeline::~RenderPipeline()
{
SDL_ReleaseGPUGraphicsPipeline(g_gpuDevice, m_handle);
}
} // namespace ia::iae
namespace ia::iae
{
VOID Renderer::InitializeGPU()
{
SDL_PropertiesID deviceCreateProps = SDL_CreateProperties();
SDL_SetStringProperty(deviceCreateProps, SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING, nullptr);
SDL_SetBooleanProperty(deviceCreateProps, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, true);
SDL_SetBooleanProperty(deviceCreateProps, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, g_isDebugMode);
SDL_SetBooleanProperty(deviceCreateProps, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_DEPTH_CLAMPING_BOOLEAN, false);
if (!(g_gpuDevice = SDL_CreateGPUDeviceWithProperties(deviceCreateProps)))
THROW_UNKNOWN("Failed to create the SDL GPU Device: ", SDL_GetError());
SDL_DestroyProperties(deviceCreateProps);
if (!SDL_ClaimWindowForGPUDevice(g_gpuDevice, g_windowHandle))
THROW_UNKNOWN("Failed to initialize SDL GPU for the window: ", SDL_GetError());
SDL_SetGPUSwapchainParameters(g_gpuDevice, g_windowHandle, SDL_GPU_SWAPCHAINCOMPOSITION_SDR,
SDL_GPU_PRESENTMODE_VSYNC);
}
VOID Renderer::InitializeSampler()
{
SDL_GPUSamplerCreateInfo createInfo{.min_filter = SDL_GPU_FILTER_NEAREST,
.mag_filter = SDL_GPU_FILTER_NEAREST,
.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR,
.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
.enable_anisotropy = false};
g_linearClampSampler = SDL_CreateGPUSampler(g_gpuDevice, &createInfo);
createInfo.min_filter = SDL_GPU_FILTER_NEAREST;
createInfo.mag_filter = SDL_GPU_FILTER_NEAREST;
createInfo.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR;
createInfo.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_REPEAT,
createInfo.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_REPEAT,
createInfo.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_REPEAT,
g_linearRepeatSampler = SDL_CreateGPUSampler(g_gpuDevice, &createInfo);
}
VOID Renderer::InitializeGeometries()
{
g_quadGeometry = Renderer::CreateGeometry(
{
{glm::vec2{0, 1}, glm::vec2{0, 1}},
{glm::vec2{1, 1}, glm::vec2{1, 1}},
{glm::vec2{1, 0}, glm::vec2{1, 0}},
{glm::vec2{0, 0}, glm::vec2{0, 0}},
},
{0, 1, 2, 2, 3, 0});
}
VOID Renderer::InitializeDrawData()
{
g_staticSpriteDataBuffer = CreateDeviceLocalBuffer(SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ, nullptr,
sizeof(SpriteInstanceData) * MAX_SPRITE_COUNT);
g_dynamicSpriteDataBuffer = CreateDeviceLocalBuffer(SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ, nullptr,
sizeof(SpriteInstanceData) * MAX_SPRITE_COUNT);
SDL_GPUTransferBufferCreateInfo stagingBufferCreateInfo{.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
.size = sizeof(SpriteInstanceData) * MAX_SPRITE_COUNT};
g_spriteDataStagingBuffer = SDL_CreateGPUTransferBuffer(g_gpuDevice, &stagingBufferCreateInfo);
g_projectionMatrix =
glm::orthoLH(0.0f, (FLOAT32) g_screenExtent.x, (FLOAT32) g_screenExtent.y, 0.0f, -1.0f, 1.0f);
SetCameraPosition({});
}
VOID Renderer::InitializeTextures()
{
{ // Create Default Texture
const auto pixels = new UINT8[100 * 100 * 4];
ia_memset(pixels, 0xFF, 100 * 100 * 4);
const auto t = CreateTexture(pixels, 100, 100);
BakeTexture(t);
g_defaultTexture = g_texureData[t].BakedHandle;
delete[] pixels;
}
g_activeTextureAtlas = g_defaultTexture;
}
} // namespace ia::iae

View File

@ -0,0 +1,137 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IASoft (PVT) LTD (oss@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>
#include <SDL3/SDL.h>
namespace ia::iae
{
struct GeometryVertex
{
Vec2 Position;
Vec2 TexCoords;
};
struct Geometry
{
INT32 IndexCount{};
SDL_GPUBuffer *IndexBuffer;
SDL_GPUBuffer *VertexBuffer;
};
class RenderPipeline
{
public:
struct StageDesc
{
Vector<UINT8> SourceData{};
UINT32 SamplerCount{};
UINT32 UniformBufferCount{};
UINT32 StorageBufferCount{};
};
public:
RenderPipeline(IN CONST StageDesc &vertexStageDesc, IN CONST StageDesc &pixelStageDesc,
IN BOOL enableVertexBuffer);
~RenderPipeline();
SDL_GPUGraphicsPipeline *GetHandle() CONST
{
return m_handle;
}
private:
SDL_GPUGraphicsPipeline *m_handle{};
};
class Renderer
{
public:
STATIC Vec2 GetCameraPosition();
STATIC VOID SetCameraPosition(IN Vec2 position);
public:
STATIC VOID DrawStaticSpriteTopLeft(IN Handle texture, IN INT32 tileIndex, IN Vec2 position, IN Vec2 scale, IN FLOAT32 rotation);
STATIC INLINE VOID DrawStaticSpriteCentered(IN Handle texture, IN INT32 tileIndex, IN Vec2 position, IN Vec2 scale,
IN FLOAT32 rotation);
STATIC VOID DrawDynamicSpriteTopLeft(IN Handle texture, IN INT32 tileIndex, IN Vec2 position, IN Vec2 scale, IN FLOAT32 rotation);
STATIC INLINE VOID DrawDynamicSpriteCentered(IN Handle texture, IN INT32 tileIndex, IN Vec2 position, IN Vec2 scale,
IN FLOAT32 rotation);
STATIC VOID ResizeScreen(IN IVec2 newSize);
public:
STATIC Handle CreateTexture(IN PCUINT8 rgbaData, IN INT32 width, IN INT32 height, IN INT32 tileCountX = 1, IN INT32 tileCountY = 1);
STATIC VOID DestroyTexture(IN Handle texture);
STATIC VOID BakeTexture(IN Handle texture);
STATIC VOID SetTextureAtlas(IN CONST Vector<Handle> textures);
private:
STATIC SDL_GPUTexture *CreateTexture(IN SDL_GPUTextureUsageFlags usage, IN INT32 width, IN INT32 height,
IN INT32 stride, IN PCUINT8 rgbaData = nullptr,
IN SDL_GPUTextureFormat format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM,
IN BOOL generateMipmaps = false);
STATIC SDL_GPUBuffer *CreateDeviceLocalBuffer(IN SDL_GPUBufferUsageFlags usage, IN PCVOID data,
IN UINT32 dataSize);
STATIC VOID DestroyTexture(IN SDL_GPUTexture *handle);
STATIC VOID DestroyBuffer(IN SDL_GPUBuffer *handle);
STATIC VOID CopyToDeviceLocalBuffer(IN SDL_GPUBuffer *buffer, IN PCVOID data, IN UINT32 dataSize);
STATIC VOID CopyToDeviceLocalBuffer(IN SDL_GPUTransferBuffer *stagingBuffer, IN SDL_GPUBuffer *buffer,
IN PCVOID data, IN UINT32 dataSize);
STATIC SDL_GPUSampler *GetSampler_LinearClamp();
STATIC SDL_GPUSampler *GetSampler_LinearRepeat();
private:
STATIC VOID InitializeGPU();
STATIC VOID InitializeSampler();
STATIC VOID InitializePipelines();
STATIC VOID InitializeGeometries();
STATIC VOID InitializeDrawData();
STATIC VOID InitializeTextures();
STATIC Geometry *CreateGeometry(IN CONST Vector<GeometryVertex> &vertices, IN CONST Vector<INT32> &indices);
STATIC VOID DestroyGeometry(IN Geometry *geometry);
STATIC VOID Render();
STATIC Vec4 GetTextureAtlasCoordinates(IN Handle texture, IN INT32 tileIndex);
private:
STATIC VOID Initialize(IN IVec2 screenExtent);
STATIC VOID Terminate();
STATIC VOID Update();
friend class IAEngine;
};
VOID Renderer::DrawStaticSpriteCentered(IN Handle texture,IN INT32 tileIndex, IN Vec2 position, IN Vec2 scale, IN FLOAT32 rotation)
{
DrawStaticSpriteTopLeft(texture, tileIndex, position - scale / 2.0f, scale, rotation);
}
VOID Renderer::DrawDynamicSpriteCentered(IN Handle texture,IN INT32 tileIndex, IN Vec2 position, IN Vec2 scale, IN FLOAT32 rotation)
{
DrawDynamicSpriteTopLeft(texture, tileIndex, position - scale / 2.0f, scale, rotation);
}
} // namespace ia::iae

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,14 @@
#include <IACore/String.hpp>
#include <IACore/Vector.hpp>
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/ext/scalar_constants.hpp>
#include <glm/mat4x4.hpp>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#define IAE_LOG_TAG "IAE"
#define IAE_LOG_INFO(...) ia::Logger::Info(IAE_LOG_TAG, __VA_ARGS__)
@ -34,4 +42,12 @@ namespace ia::iae
{
using Handle = INT64;
STATIC CONSTEXPR Handle INVALID_HANDLE = -1;
using Vec2 = glm::vec2;
using Vec3 = glm::vec3;
using Vec4 = glm::vec4;
using IVec2 = glm::ivec2;
using IVec3 = glm::ivec3;
using IVec4 = glm::ivec4;
using Mat4 = glm::mat4;
}

View File

@ -23,9 +23,14 @@ namespace ia::iae
class IAEngine
{
public:
IAEngine();
~IAEngine();
private:
STATIC VOID Initialize();
STATIC VOID Terminate();
STATIC VOID Update();
STATIC VOID ProcessEvent(IN PVOID event);
friend INT32 Run(IN CONST String &name, IN CONST String &packageName, IN CONST String &developerName,
IN CONST String &publisherName, IN IA_VERSION_TYPE version);
};
}

View File

@ -0,0 +1,49 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IASoft (PVT) LTD (oss@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/IAEngine.hpp>
using namespace ia;
using namespace ia::iae;
struct GameRequestedConfig
{
INT32 DesignWidth{};
INT32 DesignHeight{};
INT32 WindowWidth{};
INT32 WindowHeight{};
};
C_DECL(GameRequestedConfig* Game_GetConfigRequest());
C_DECL(VOID Game_OnInitialize());
C_DECL(VOID Game_OnTerminate());
C_DECL(VOID Game_OnDebugDraw());
C_DECL(VOID Game_OnFixedUpdate());
C_DECL(VOID Game_OnUpdate(IN FLOAT32 deltaTime));
C_DECL(VOID Game_OnResize(IN INT32 newWidth, IN INT32 newHeight));
namespace ia::iae
{
INT32 Run(IN CONST String& name, IN CONST String& packageName, IN CONST String& developerName, IN CONST String& publisherName, IN IA_VERSION_TYPE version);
} // namespace ia::iae
#if defined(__ANDROID__)
#define IAENGINE_RUN(name, packageName, developerName, publisherName, versionMajor, versionMinor, versionPatch) extern "C" int SDL_main(int argc, char *argv[]) { return ia::iae::Run(name, packageName, developerName, publisherName, IA_MAKE_VERSION(versionMajor, versionMinor, versionPatch)); }
#else
#define IAENGINE_RUN(name, packageName, developerName, publisherName, versionMajor, versionMinor, versionPatch) int main(int argc, char *argv[]) { return ia::iae::Run(name, packageName, developerName, publisherName, IA_MAKE_VERSION(versionMajor, versionMinor, versionPatch)); }
#endif

11
Vendor/CMakeLists.txt vendored
View File

@ -31,3 +31,14 @@ set(ZLIB_BUILD_SHARED OFF)
set(ZLIB_BUILD_MINIZIP OFF)
set(ZLIB_INSTALL OFF)
add_subdirectory(zlib/)
# -----------------------------------------------
# PugiXML
# -----------------------------------------------
add_subdirectory(pugixml/)
# -----------------------------------------------
# glm
# -----------------------------------------------
add_subdirectory(glm/)

1
Vendor/glm vendored Submodule

Submodule Vendor/glm added at d34c19ecd0

1
Vendor/pugixml vendored Submodule

Submodule Vendor/pugixml added at 61c944895e