Compare commits

...

4 Commits

Author SHA1 Message Date
28ca737c21 9.22 routine backup 2025-09-22 15:19:25 +05:30
6a6debb0db Initial RmlUI Integration 2025-09-20 20:25:26 +05:30
ece65b18da Restructure 2025-09-20 10:03:52 +05:30
7eecf8f968 Add modules Box2D & RmlUi 2025-09-19 14:55:09 +05:30
43 changed files with 1379 additions and 361 deletions

9
.gitmodules vendored
View File

@ -14,3 +14,12 @@
[submodule "Vendor/glm"]
path = Vendor/glm
url = https://github.com/g-truc/glm
[submodule "Vendor/RmlUi"]
path = Vendor/RmlUi
url = https://github.com/mikke89/RmlUi
[submodule "Vendor/box2d"]
path = Vendor/box2d
url = https://github.com/erincatto/box2d
[submodule "Vendor/freetype"]
path = Vendor/freetype
url = https://github.com/freetype/freetype

11
.vscode/settings.json vendored
View File

@ -19,6 +19,15 @@
"initializer_list": "cpp",
"random": "cpp",
"span": "cpp",
"xstring": "cpp"
"xstring": "cpp",
"cstddef": "cpp",
"algorithm": "cpp",
"expected": "cpp",
"optional": "cpp",
"regex": "cpp",
"system_error": "cpp",
"xlocmon": "cpp",
"xiosbase": "cpp",
"map": "cpp"
}
}

View File

@ -2,6 +2,8 @@
#include <IAEngine/Input.hpp>
#include <IAEngine/Rendering/Camera.hpp>
#include <IAEngine/Components/PhysicsBody2D.hpp>
#include <IAEngine/Components/BoxCollider2D.hpp>
#include <IAEngine/Components/SpriteRenderer.hpp>
#include <IACore/File.hpp>
@ -11,12 +13,12 @@ namespace ia::iae::game
RefPtr<Scene> scene;
RefPtr<Node> g_player;
PhysicsBody2DComponent* g_playerPhysicsBody{};
VOID Game::Initialize()
{
scene = Engine::CreateScene();
scene->YSortingEnabled() = true;
Engine::ChangeScene(scene.get());
g_player = MakeRefPtr<Node>();
{
@ -25,14 +27,27 @@ namespace ia::iae::game
.ShouldLoop = true,
.Keys = {
SpriteRendererComponent::AnimationKeyFrame {
.Scale = {0.1f, 0.1f, 0.1f},
.Texture = Engine::CreateTexture(File::ReadToVector("Graphics/green.png")),
.Scale = {0.25f, 0.25f},
.ColorOverlay = {0.0f, 0.75f, 0.0f, 1.0f}
}
},
});
t->BakeAnimations();
}
g_player->SetLocalPosition({200, 200, 0});
{
g_playerPhysicsBody = g_player->AddComponent<PhysicsBody2DComponent>();
g_playerPhysicsBody->IsDynamic() = true;
const auto collider = g_player->AddComponent<BoxCollider2DComponent>();
collider->Rect() = {
0,
0,
g_player->DrawnSize().x,
g_player->DrawnSize().y/2.0f,
};
collider->IsDebugDrawEnabled() = true;
}
g_player->Tags() = NODE_TAG_PLAYER;
g_player->SetLocalPosition({200, 200});
const auto obstacle = MakeRefPtr<Node>();
{
@ -41,18 +56,31 @@ namespace ia::iae::game
.ShouldLoop = true,
.Keys = {
SpriteRendererComponent::AnimationKeyFrame {
.Scale = {0.25f, 0.25f, 0.1f},
.Texture = Engine::CreateTexture(File::ReadToVector("Graphics/red.png")),
.Scale = {3.0f, 0.5f},
.ColorOverlay = {0.75f, 0.0f, 0.0f, 1.0f}
}
},
});
t->BakeAnimations();
}
obstacle->SortOffset() = 20;
obstacle->SetLocalPosition({150, 100, 0});
{
obstacle->AddComponent<PhysicsBody2DComponent>();
const auto collider = obstacle->AddComponent<BoxCollider2DComponent>();
collider->Rect() = {
0, 0,
obstacle->DrawnSize().x,
obstacle->DrawnSize().y
};
collider->IsDebugDrawEnabled() = true;
}
obstacle->Tags() = NODE_TAG_GROUND;
obstacle->SetLocalSortIndex(20);
obstacle->SetLocalPosition({200, 400});
scene->AddNode(g_player);
scene->AddNode(obstacle);
Engine::ChangeScene(scene.get());
}
VOID Game::Terminate()
@ -61,6 +89,8 @@ namespace ia::iae::game
VOID Game::Update()
{
g_player->SetLocalPosition(g_player->GetLocalPosition() + glm::vec3{Input::GetDirectionalInput(), 0.0f});
if(Input::WasKeyPressed(Input::KEY_SPACE))
g_playerPhysicsBody->Jump(200.0f);
g_playerPhysicsBody->SetVelocityX(Input::GetDirectionalInput().x * 100.0f);
}
}

View File

@ -7,14 +7,13 @@ set(IAEngine_Sources
imp/cpp/Scene.cpp
imp/cpp/Random.cpp
imp/cpp/Texture.cpp
imp/cpp/UI.cpp
imp/cpp/Rendering/Camera.cpp
imp/cpp/Rendering/Renderer.cpp
imp/cpp/Rendering/GPUBuffer.cpp
imp/cpp/Rendering/GPUTexture.cpp
imp/cpp/Rendering/Mesh/Quad.cpp
imp/cpp/Rendering/Pipeline/Pipeline.cpp
imp/cpp/Rendering/Pipeline/UnlitMesh.cpp
@ -30,7 +29,7 @@ set(IAEngine_Sources
imp/cpp/Components/SpriteRenderer.cpp
imp/cpp/Components/SoundEmitter.cpp
imp/cpp/Components/ParticleEmitter.cpp
imp/cpp/Components/PhysicsBody.cpp
imp/cpp/Components/PhysicsBody2D.cpp
imp/cpp/Components/TextureRenderer.cpp
)
@ -40,4 +39,4 @@ target_include_directories(IAEngine PUBLIC inc/)
target_include_directories(IAEngine PRIVATE imp/hpp)
target_link_libraries(IAEngine PUBLIC IACore ImGui glm::glm)
target_link_libraries(IAEngine PRIVATE SDL3::SDL3 SDL3_mixer::SDL3_mixer)
target_link_libraries(IAEngine PRIVATE SDL3::SDL3 SDL3_mixer::SDL3_mixer box2d RmlUi::RmlUi)

View File

@ -20,7 +20,7 @@
namespace ia::iae
{
AtlasRendererComponent::AtlasRendererComponent(IN Node *node) : IComponent(node)
AtlasRendererComponent::AtlasRendererComponent(IN Node *node) : TextureRendererComponent(node)
{
}
@ -40,12 +40,12 @@ namespace ia::iae
m_tileGrid.m_tileTextures.resize(m_tileGrid.TileCountX * m_tileGrid.TileCountY);
}
VOID AtlasRendererComponent::SetGridTileTexture(IN INT32 index, IN Texture texture)
VOID AtlasRendererComponent::SetGridTileTexture(IN INT32 index, IN class Texture texture)
{
m_tileGrid.m_tileTextures[index] = texture;
}
VOID AtlasRendererComponent::SetGridTileTexture(IN INT32 x, IN INT32 y, IN Texture texture)
VOID AtlasRendererComponent::SetGridTileTexture(IN INT32 x, IN INT32 y, IN class Texture texture)
{
m_tileGrid.m_tileTextures[x + (y * m_tileGrid.TileCountX)] = texture;
}
@ -59,7 +59,8 @@ namespace ia::iae
handles[i] = m_tileGrid.m_tileTextures[i].GetHandle();
m_bakedGPUTexture = GPUTexture::GridCombine(handles.data(), m_tileGrid.TileCountX, m_tileGrid.TileCountY,
m_tileGrid.TileWidth, m_tileGrid.TileHeight);
m_bakedTexture = Texture(m_bakedGPUTexture->GetHandle(), w, h);
CurrentTexture() = Texture(m_bakedGPUTexture->GetHandle(), w, h);
PositionOffset() = m_tileGrid.Position;
}
VOID AtlasRendererComponent::Update()
@ -68,7 +69,6 @@ namespace ia::iae
VOID AtlasRendererComponent::Draw()
{
m_bakedTexture.Draw(m_node->SortOffset(), m_node->GetPosition() + m_tileGrid.Position, {1.0f, 1.0f, 1.0f}, 0.0f,
false, false, {1.0f, 1.0f, 1.0f, 1.0f});
TextureRendererComponent::Draw();
}
} // namespace ia::iae

View File

@ -14,38 +14,45 @@
// 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/Nodes/Node.hpp>
#include <IAEngine/Physics/Physics.hpp>
#include <IAEngine/Components/BoxCollider2D.hpp>
#include <IAEngine/IAEngine.hpp>
#include <IACore/File.hpp>
#include <IAEngine/Components/PhysicsBody2D.hpp>
#include <IAEngine/Components/TextureRenderer.hpp>
#include <IAEngine/Rendering/Renderer.hpp>
#include <IAEngine/Physics/Physics.hpp>
#include <IAEngine/Nodes/Node.hpp>
namespace ia::iae
{
Texture g_debugBoxTexture{};
EXTERN Texture g_whiteStrokeTexture;
BoxCollider2DComponent::BoxCollider2DComponent(IN Node *node) : IComponent(node)
{
Physics::AddCollider(this);
g_debugBoxTexture = Engine::CreateTexture(File::ReadToVector("Resources/Engine/debug_box.png"));
IA_RELEASE_ASSERT(m_body = node->GetComponent<PhysicsBody2DComponent>());
m_physicsHandle = Physics::AddColliderToBody(m_body->PhysicsHandle(), this);
}
VOID BoxCollider2DComponent::Draw()
{
//g_debugBoxTexture.Draw(
// 0,
// m_node->GetPosition() + glm::vec3{m_shape.x, m_shape.y, 0},
// {m_shape.z/256.0f, m_shape.w/256.0f, 1},
// m_node->GetRotation().z, false, false, glm::vec4{1.0f, 1.0f, 1.0f, 1.0f});
if (!m_isDebugDrawEnabled)
return;
Renderer::Draw(Renderer::GetMesh_Quad(), g_whiteStrokeTexture.GetHandle(), m_node->GetPosition() + glm::vec2{m_rect.x, m_rect.y},
glm::vec2{m_rect.z, m_rect.w}, 0, Renderer::MAX_LAYER_INDEX,
0, {1.0f, 1.0f, 1.0f, 1.0f});
}
VOID BoxCollider2DComponent::Update()
{
const auto pos = m_node->GetPosition();
m_absoluteShape.x = pos.x + m_shape.x;
m_absoluteShape.y = pos.y + m_shape.y;
m_absoluteShape.z = m_absoluteShape.x + m_shape.z;
m_absoluteShape.w = m_absoluteShape.y + m_shape.w;
}
VOID BoxCollider2DComponent::OnCollisionEnter(IN Node *other)
{
if (other->HasTag(NODE_TAG_GROUND))
m_body->IsGrounded() = true;
}
VOID BoxCollider2DComponent::OnCollisionExit(IN Node *other)
{
if (other->HasTag(NODE_TAG_GROUND))
m_body->IsGrounded() = false;
}
} // namespace ia::iae

View File

@ -1,22 +0,0 @@
// 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/>.
#include <IAEngine/Components/PhysicsBody.hpp>
namespace ia::iae
{
}

View File

@ -0,0 +1,66 @@
// 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/>.
#include <IAEngine/Components/BoxCollider2D.hpp>
#include <IAEngine/Components/PhysicsBody2D.hpp>
#include <IAEngine/Nodes/Node.hpp>
#include <IAEngine/Physics/Physics.hpp>
namespace ia::iae
{
PhysicsBody2DComponent::PhysicsBody2DComponent(IN Node *node) : IComponent(node)
{
m_physicsHandle = Physics::AddBody(this);
}
VOID PhysicsBody2DComponent::Draw()
{
}
VOID PhysicsBody2DComponent::Update()
{
if (m_isDynamic)
m_node->SetLocalPosition(Physics::GetBodyPosition(m_physicsHandle));
if (!m_isRotationLocked)
m_node->SetLocalRotation(Physics::GetBodyRotation(m_physicsHandle));
}
VOID PhysicsBody2DComponent::SetVelocity(IN glm::vec2 v)
{
Physics::SetBodyVelocity(m_physicsHandle, v);
}
VOID PhysicsBody2DComponent::SetVelocityX(IN FLOAT32 v)
{
Physics::SetBodyVelocityX(m_physicsHandle, v);
}
VOID PhysicsBody2DComponent::SetVelocityY(IN FLOAT32 v)
{
Physics::SetBodyVelocityY(m_physicsHandle, v);
}
VOID PhysicsBody2DComponent::ApplyForce(IN glm::vec2 force)
{
Physics::ApplyBodyForce(m_physicsHandle, force);
}
VOID PhysicsBody2DComponent::Jump(IN FLOAT32 velocity)
{
if(m_isGrounded)
Physics::SetBodyVelocityY(m_physicsHandle, -velocity);
}
} // namespace ia::iae

View File

@ -29,7 +29,7 @@ namespace ia::iae
return m_reverseAnimation ? m_activeAnimation.Keys[m_activeAnimation.Keys.size() - 1 - index] : m_activeAnimation.Keys[index];
}
SpriteRendererComponent::SpriteRendererComponent(IN Node *node) : IComponent(node)
SpriteRendererComponent::SpriteRendererComponent(IN Node *node) : TextureRendererComponent(node)
{
}
@ -60,6 +60,9 @@ namespace ia::iae
m_currentAnimationState = m_prevAnimationKeyFrame;
m_activeAnimationHandle = animation;
CurrentTexture() = m_currentAnimationState.Texture;
m_node->DrawnSize() = m_node->GetScale() * m_currentAnimationState.Texture.GetExtent() * m_currentAnimationState.Scale;
}
VOID SpriteRendererComponent::Update()
@ -69,11 +72,13 @@ namespace ia::iae
VOID SpriteRendererComponent::Draw()
{
Renderer::SetParallaxFactor(m_parallaxFactor);
const auto &animFrame = m_currentAnimationState;
animFrame.Texture.Draw(m_node->SortOffset() + m_sortOffset, m_node->GetPosition() + animFrame.Position,
m_node->GetScale() * animFrame.Scale, m_node->GetRotation().z + animFrame.Rotation.z,
m_isFlippedH, m_isFlippedV, animFrame.ColorOverlay);
PositionOffset() = animFrame.Position;
ScaleOffset() = animFrame.Scale;
RotationOffset() = animFrame.Rotation;
ColorOverlay() = animFrame.ColorOverlay;
CurrentTexture() = animFrame.Texture;
TextureRendererComponent::Draw();
}
VOID SpriteRendererComponent::UpdateAnimation()

View File

@ -17,6 +17,7 @@
#include <IAEngine/Components/TextureRenderer.hpp>
#include <IAEngine/Nodes/Node.hpp>
#include <IAEngine/Rendering/Renderer.hpp>
namespace ia::iae
{
@ -30,8 +31,27 @@ namespace ia::iae
VOID TextureRendererComponent::Draw()
{
m_texture.Draw(m_node->SortOffset(), m_node->GetPosition() + m_position,
m_node->GetScale(), m_node->GetRotation().z,
false, false, glm::vec4{1.0f, 1.0f, 1.0f, 1.0f});
m_node->DrawnSize() = m_node->GetScale() * m_texture.GetExtent() * m_scaleOffset;
Renderer::SetState_FlippedH(m_isFlippedH);
Renderer::SetState_FlippedV(m_isFlippedV);
Renderer::SetState_CameraRelative(m_isCameraRelative);
Renderer::SetState_TextureOffset(m_textureOffset.x, m_textureOffset.y);
if (m_shouldDrawOutline)
{
const auto outlineFactor = 1.2f;
Renderer::Draw(Renderer::GetMesh_Quad(), m_texture.GetHandle(),
m_node->GetPosition() + m_positionOffset -
glm::vec2{m_node->DrawnSize().x * (outlineFactor - 1.0f) / 2.0f,
m_node->DrawnSize().y * (outlineFactor - 1.0f) / 2.0f},
m_node->DrawnSize() * glm::vec2{outlineFactor, outlineFactor},
m_node->GetRotation() + m_rotationOffset, m_node->LayerIndex(), m_node->GetSortIndex(),
m_outlineColor);
}
Renderer::Draw(Renderer::GetMesh_Quad(), m_texture.GetHandle(), m_node->GetPosition() + m_positionOffset,
m_node->DrawnSize(), m_node->GetRotation() + m_rotationOffset, m_node->LayerIndex(),
m_node->GetSortIndex(), m_colorOverlay);
}
} // namespace ia::iae

View File

@ -22,6 +22,7 @@
#include <IAEngine/Physics/Physics.hpp>
#include <IAEngine/Random.hpp>
#include <IAEngine/Time.hpp>
#include <IAEngine/UI.hpp>
#include <SDL3/SDL.h>
@ -71,6 +72,7 @@ namespace ia::iae
Input::Initialize();
Audio::Initialize();
Physics::Initialize();
UI::Initialize(config.WindowWidth, config.WindowHeight);
return true;
}
@ -82,9 +84,10 @@ namespace ia::iae
for (auto &t : g_gpuTextureRefs)
t.reset();
Renderer::Terminate();
Audio::Terminate();
UI::Terminate();
Physics::Terminate();
Audio::Terminate();
Renderer::Terminate();
SDL_DestroyWindow(g_windowHandle);
@ -106,6 +109,7 @@ namespace ia::iae
}
Renderer::BeginFrame();
RenderGame();
UI::Draw();
}
VOID Engine::EndFrame()
@ -123,10 +127,12 @@ namespace ia::iae
{
ImGui_ImplSDL3_ProcessEvent(&g_event);
Input::OnEvent(&g_event);
UI::OnEvent(&g_event);
}
VOID Engine::UpdateGame()
{
UI::Update();
Physics::Update();
if B_LIKELY (g_activeScene)
@ -141,7 +147,10 @@ namespace ia::iae
VOID Engine::ChangeScene(IN Scene *scene)
{
if(g_activeScene)
g_activeScene->OnDeactivate();
g_activeScene = scene;
g_activeScene->OnActivate();
}
Scene *Engine::GetActiveScene()

View File

@ -56,6 +56,7 @@ namespace ia::iae
VOID Input::OnEvent(IN PVOID _event)
{
memcpy(s_prevKeys, s_keys, sizeof(s_prevKeys));
const auto event = (SDL_Event *) _event;
if (event->type == SDL_EVENT_KEY_DOWN)
{
@ -65,7 +66,6 @@ namespace ia::iae
{
s_keys[event->key.scancode] = false;
}
memcpy(s_prevKeys, s_keys, sizeof(s_prevKeys));
}
glm::vec2 Input::GetDirectionalInput()

View File

@ -14,56 +14,174 @@
// 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 <IAEngine/Physics/Physics.hpp>
#include <IAEngine/Nodes/Node.hpp>
#include <box2d/box2d.h>
#include <map>
namespace ia::iae
{
Vector<BoxCollider2DComponent *> g_colliders;
struct Body
{
struct Collider
{
b2ShapeId ShapeId{};
BoxCollider2DComponent *ColliderComponent{};
};
b2BodyId BodyId{.world0 = 0xFFFF};
PhysicsBody2DComponent *BodyComponent;
Vector<Collider> Colliders;
};
b2WorldId g_worldId{};
Vector<Body> g_bodies;
std::map<Handle, BoxCollider2DComponent*> g_shapeColliders;
INLINE Handle ShapeIdToHandle(IN b2ShapeId id)
{
return *reinterpret_cast<Handle*>(&id.index1);
}
VOID Physics::Initialize()
{
auto worldDef = b2DefaultWorldDef();
worldDef.gravity = b2Vec2{0.0f, 1000.0f};
g_worldId = b2CreateWorld(&worldDef);
}
VOID Physics::Terminate()
{
}
INLINE BOOL IsIntersectingH(IN CONST glm::vec4 &shape, IN FLOAT32 x)
{
return (x >= shape.x) && (x <= shape.z);
}
INLINE BOOL IsIntersectingV(IN CONST glm::vec4 &shape, IN FLOAT32 y)
{
return (y >= shape.y) && (y <= shape.w);
b2DestroyWorld(g_worldId);
}
VOID Physics::Update()
{
for (SIZE_T i = 0; i < g_colliders.size(); i++)
CONSTEXPR FLOAT32 TIME_STEP = 1.0f / 60.0f;
CONSTEXPR INT32 SUB_STEP_COUNT = 4;
b2World_Step(g_worldId, TIME_STEP, SUB_STEP_COUNT);
// Process contact events
const auto contactEvents = b2World_GetContactEvents(g_worldId);
for(INT32 i = 0; i < contactEvents.beginCount; i++)
{
for (SIZE_T j = i + 1; j < g_colliders.size(); j++)
const auto& shapeA = g_shapeColliders[ShapeIdToHandle(contactEvents.beginEvents[i].shapeIdA)];
const auto& shapeB = g_shapeColliders[ShapeIdToHandle(contactEvents.beginEvents[i].shapeIdB)];
shapeA->OnCollisionEnter(shapeB->GetNode());
shapeB->OnCollisionEnter(shapeA->GetNode());
}
for(INT32 i = 0; i < contactEvents.endCount; i++)
{
const auto& shapeA = g_shapeColliders[ShapeIdToHandle(contactEvents.endEvents[i].shapeIdA)];
const auto& shapeB = g_shapeColliders[ShapeIdToHandle(contactEvents.endEvents[i].shapeIdB)];
shapeA->OnCollisionExit(shapeB->GetNode());
shapeB->OnCollisionExit(shapeA->GetNode());
}
// Process sensor events
const auto sensorEvents = b2World_GetSensorEvents(g_worldId);
for(INT32 i = 0; i < sensorEvents.beginCount; i++)
{
const auto& shapeA = g_shapeColliders[ShapeIdToHandle(sensorEvents.beginEvents[i].sensorShapeId)];
const auto& shapeB = g_shapeColliders[ShapeIdToHandle(sensorEvents.beginEvents[i].visitorShapeId)];
shapeA->OnCollisionEnter(shapeB->GetNode());
shapeB->OnCollisionEnter(shapeA->GetNode());
}
for(INT32 i = 0; i < sensorEvents.endCount; i++)
{
const auto& shapeA = g_shapeColliders[ShapeIdToHandle(sensorEvents.endEvents[i].sensorShapeId)];
const auto& shapeB = g_shapeColliders[ShapeIdToHandle(sensorEvents.endEvents[i].visitorShapeId)];
shapeA->OnCollisionExit(shapeB->GetNode());
shapeB->OnCollisionExit(shapeA->GetNode());
}
}
VOID Physics::Bake()
{
for (auto &b : g_bodies)
{
const auto pos = b.BodyComponent->GetNode()->GetPosition();
auto bodyDef = b2DefaultBodyDef();
if (b.BodyComponent->IsDynamic())
bodyDef.type = b2_dynamicBody;
bodyDef.position = b2Vec2{pos.x, pos.y};
if (b.BodyId.world0 != 0xFFFF)
b2DestroyBody(b.BodyId);
b.BodyId = b2CreateBody(g_worldId, &bodyDef);
for (auto &c : b.Colliders)
{
const auto boxA = g_colliders[i]->AbsoluteShape();
const auto boxB = g_colliders[j]->AbsoluteShape();
const auto nodeA = g_colliders[i]->GetNode();
if(IsIntersectingH(boxB, boxA.z) && (IsIntersectingV(boxB, boxA.y) || IsIntersectingV(boxB, boxA.w)))
nodeA->SetLocalPosition(nodeA->GetLocalPosition() + glm::vec3(-boxA.z + boxB.x, 0, 0));
else if(IsIntersectingH(boxB, boxA.x) && (IsIntersectingV(boxB, boxA.y) || IsIntersectingV(boxB, boxA.w)))
nodeA->SetLocalPosition(nodeA->GetLocalPosition() + glm::vec3(-boxA.x + boxB.z, 0, 0));
else if(IsIntersectingV(boxB, boxA.w) && (IsIntersectingH(boxB, boxA.x) || IsIntersectingH(boxB, boxA.z)))
nodeA->SetLocalPosition(nodeA->GetLocalPosition() + glm::vec3(0, -boxA.w + boxB.y, 0));
else if(IsIntersectingV(boxB, boxA.y) && (IsIntersectingH(boxB, boxA.x) || IsIntersectingH(boxB, boxA.z)))
nodeA->SetLocalPosition(nodeA->GetLocalPosition() + glm::vec3(0, -boxA.y + boxB.w, 0));
const auto rect = c.ColliderComponent->Rect();
const auto halfW = rect.z/2.0f;
const auto halfH = rect.w/2.0f;
const auto box = b2MakeOffsetBox(halfW, halfH, {rect.x, rect.y - halfH/2.0f}, b2MakeRot(0));
auto boxShapeDef = b2DefaultShapeDef();
boxShapeDef.density = 1.0f;
boxShapeDef.isSensor = c.ColliderComponent->IsTrigger();
boxShapeDef.enableContactEvents = c.ColliderComponent->CollisionsEnabled();
boxShapeDef.enableSensorEvents = boxShapeDef.enableContactEvents;
c.ShapeId = b2CreatePolygonShape(b.BodyId, &boxShapeDef, &box);
g_shapeColliders[ShapeIdToHandle(c.ShapeId)] = c.ColliderComponent;
}
}
}
VOID Physics::AddCollider(IN BoxCollider2DComponent *collider)
Handle Physics::AddBody(IN PhysicsBody2DComponent *body)
{
g_colliders.pushBack(collider);
g_bodies.pushBack(Body{
.BodyComponent = body,
});
return g_bodies.size() - 1;
}
Handle Physics::AddColliderToBody(IN Handle bodyHandle, IN BoxCollider2DComponent *collider)
{
auto &b = g_bodies[bodyHandle];
b.Colliders.pushBack(Body::Collider{
.ColliderComponent = collider,
});
return b.Colliders.size() - 1;
}
FLOAT32 Physics::GetBodyRotation(IN Handle handle)
{
const auto &b = g_bodies[handle];
return acosf(b2Body_GetRotation(b.BodyId).c);
}
glm::vec2 Physics::GetBodyPosition(IN Handle handle)
{
const auto &b = g_bodies[handle];
const auto v = b2Body_GetPosition(b.BodyId);
return {v.x, v.y};
}
VOID Physics::ApplyBodyForce(IN Handle handle, IN glm::vec2 force)
{
const auto &b = g_bodies[handle];
b2Body_ApplyForce(b.BodyId, {force.x, force.y}, b2Body_GetLocalCenterOfMass(b.BodyId), true);
}
VOID Physics::SetBodyVelocity(IN Handle handle, IN glm::vec2 v)
{
const auto &b = g_bodies[handle];
b2Body_SetLinearVelocity(b.BodyId, {v.x, v.y});
}
VOID Physics::SetBodyVelocityX(IN Handle handle, IN FLOAT32 v)
{
const auto &b = g_bodies[handle];
b2Body_SetLinearVelocity(b.BodyId, {v, b2Body_GetLinearVelocity(b.BodyId).y});
}
VOID Physics::SetBodyVelocityY(IN Handle handle, IN FLOAT32 v)
{
const auto &b = g_bodies[handle];
b2Body_SetLinearVelocity(b.BodyId, {b2Body_GetLinearVelocity(b.BodyId).x, v});
}
} // namespace ia::iae

View File

@ -78,6 +78,7 @@ namespace ia::iae
SDL_EndGPUCopyPass(copyPass);
SDL_SubmitGPUCommandBuffer(cmdBuffer);
SDL_WaitForGPUIdle(g_gpuDevice);
}
res->m_handle = (Handle) handle;

View File

@ -83,6 +83,7 @@ namespace ia::iae
SDL_UploadToGPUTexture(copyPass, &transferInfo, &region, false);
SDL_EndGPUCopyPass(copyPass);
SDL_SubmitGPUCommandBuffer(cmdBuffer);
SDL_WaitForGPUIdle(g_gpuDevice);
SDL_ReleaseGPUTransferBuffer(g_gpuDevice, stagingBuffer);
res->m_handle = (Handle) handle;
@ -125,6 +126,7 @@ namespace ia::iae
SDL_EndGPUCopyPass(copyPass);
SDL_SubmitGPUCommandBuffer(cmdBuffer);
SDL_WaitForGPUIdle(g_gpuDevice);
res->m_handle = (Handle) handle;

View File

@ -1,51 +0,0 @@
// 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/>.
#include <IAEngine/Rendering/GPUBuffer.hpp>
#include <IAEngine/Rendering/Mesh/Quad.hpp>
#include <IAEngine/Rendering/Renderer.hpp>
#include <IAEngine/Rendering/Types.hpp>
namespace ia::iae
{
RefPtr<GPUBuffer> g_quadMeshVertexBuffer{};
VOID QuadMesh::Initialize()
{
Vertex_Mesh vertices[6] = {
{glm::vec3{0, 1, 0}, glm::vec2{0, 1}},
{glm::vec3{1, 1, 0}, glm::vec2{1, 1}},
{glm::vec3{1, 0, 0}, glm::vec2{1, 0}},
{glm::vec3{1, 0, 0}, glm::vec2{1, 0}},
{glm::vec3{0, 0, 0}, glm::vec2{0, 0}},
{glm::vec3{0, 1, 0}, glm::vec2{0, 1}},
};
g_quadMeshVertexBuffer = GPUBuffer::Create(GPUBuffer::Usage::VERTEX, &vertices, sizeof(vertices));
}
VOID QuadMesh::Terminate()
{
if (g_quadMeshVertexBuffer)
g_quadMeshVertexBuffer.reset();
}
VOID QuadMesh::Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale,
IN FLOAT32 rotation)
{
Renderer::Draw(sortOffset, position, scale, rotation, g_quadMeshVertexBuffer->GetHandle(), 6);
}
} // namespace ia::iae

View File

@ -38,14 +38,14 @@ namespace ia::iae
const auto vertexShader = LoadShaderFromMemory(ShaderStage::VERTEX, SHADER_SOURCE_UNLITMESH_VERT,
sizeof(SHADER_SOURCE_UNLITMESH_VERT), 0, 3, 0, 0);
const auto pixelShader = LoadShaderFromMemory(ShaderStage::PIXEL, SHADER_SOURCE_UNLITMESH_FRAG,
sizeof(SHADER_SOURCE_UNLITMESH_FRAG), 1, 2, 0, 0);
sizeof(SHADER_SOURCE_UNLITMESH_FRAG), 1, 1, 0, 0);
SDL_GPUColorTargetDescription colorTargets[] = {{
.format = SDL_GetGPUSwapchainTextureFormat(g_gpuDevice, g_windowHandle),
}};
SDL_GPUVertexBufferDescription vertexBuffers[] = {{
.slot = 0,
.pitch = sizeof(Vertex_Mesh),
.pitch = sizeof(MeshVertex),
.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX,
.instance_step_rate = 0,
}};
@ -54,7 +54,12 @@ namespace ia::iae
{.location = 1,
.buffer_slot = 0,
.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2,
.offset = sizeof(glm::vec3)}};
.offset = sizeof(glm::vec3)},
{.location = 2,
.buffer_slot = 0,
.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4,
.offset = sizeof(glm::vec3) + sizeof(glm::vec2)},
};
SDL_GPUGraphicsPipelineCreateInfo createInfo = {
.vertex_shader = (SDL_GPUShader *) vertexShader,
.fragment_shader = (SDL_GPUShader *) pixelShader,

View File

@ -18,7 +18,6 @@
#include <IAEngine/Rendering/Camera.hpp>
#include <IAEngine/Rendering/GPUBuffer.hpp>
#include <IAEngine/Rendering/GPUTexture.hpp>
#include <IAEngine/Rendering/Mesh/Quad.hpp>
#include <IAEngine/Rendering/Pipeline/UnlitMesh.hpp>
#include <IAEngine/Rendering/Renderer.hpp>
@ -27,6 +26,29 @@
#include <backends/imgui_impl_sdl3.h>
#include <backends/imgui_impl_sdlgpu3.h>
namespace ia::iae
{
struct Mesh
{
INT32 IndexCount{};
RefPtr<GPUBuffer> IndexBuffer;
RefPtr<GPUBuffer> VertexBuffer;
};
struct RenderState
{
BOOL FlippedH{false};
BOOL FlippedV{false};
BOOL CameraRelative{true};
BOOL ScissorEnabled{false};
glm::vec2 TextureOffset{0.0f, 0.0f};
SDL_Rect Scissor{0, 0, 0, 0};
};
Vector<Mesh*> g_meshes{};
RenderState g_renderState{};
} // namespace ia::iae
namespace ia::iae
{
EXTERN SDL_Window *g_windowHandle;
@ -55,9 +77,14 @@ namespace ia::iae
glm::mat4 matView{1.0f};
glm::mat4 matModel{1.0f};
BOOL g_flipH{false};
BOOL g_flipV{false};
FLOAT32 g_parallaxFactor{0.0f};
glm::mat4 matIdentity{1.0f};
SDL_Rect g_defaultScissor{};
Handle g_meshHandleQuad{};
Texture g_whiteFillTexture{};
Texture g_whiteStrokeTexture{};
BOOL Renderer::Initialize()
{
@ -117,10 +144,44 @@ namespace ia::iae
g_pipelineUnlitMesh = Pipeline_UnlitMesh::Create();
QuadMesh::Initialize();
matProjection = glm::orthoLH(0.0f, (FLOAT32) s_width, (FLOAT32) s_height, 0.0f, -1000.0f, 1000.0f);
g_defaultScissor.x = 0;
g_defaultScissor.y = 0;
g_defaultScissor.w = s_width;
g_defaultScissor.h = s_height;
{ // Generate default textures
constexpr auto size = 100;
constexpr auto strokeWeight = 5;
const auto data = new UINT32[size * size];
memset(data, 0, size * size * 4);
for (SIZE_T i = 0; i < size; i++)
{
for (SIZE_T j = 0; j < strokeWeight; j++)
{
data[i + (size * j)] = 0xFFFFFFFF;
data[j + (size * i)] = 0xFFFFFFFF;
data[i + (size * (size - 1 - j))] = 0xFFFFFFFF;
data[(size - 1 - j) + (size * i)] = 0xFFFFFFFF;
}
}
g_whiteStrokeTexture = Engine::CreateTexture((PCUINT8) data, size, size);
for (SIZE_T i = 0; i < size * size; i++)
data[i] = 0xFFFFFFFF;
g_whiteFillTexture = Engine::CreateTexture((PCUINT8) data, size, size);
delete[] data;
}
g_meshHandleQuad = CreateMesh(
{
{glm::vec3{-0.5, 0.5, 0}, glm::vec2{0, 1}, glm::vec4{1.0f, 1.0f, 1.0f, 1.0f}},
{glm::vec3{0.5, 0.5, 0}, glm::vec2{1, 1}, glm::vec4{1.0f, 1.0f, 1.0f, 1.0f}},
{glm::vec3{0.5, -0.5, 0}, glm::vec2{1, 0}, glm::vec4{1.0f, 1.0f, 1.0f, 1.0f}},
{glm::vec3{-0.5, -0.5, 0}, glm::vec2{0, 0}, glm::vec4{1.0f, 1.0f, 1.0f, 1.0f}},
},
{0, 1, 2, 2, 3, 0});
return true;
}
@ -128,9 +189,14 @@ namespace ia::iae
{
SDL_WaitForGPUIdle(g_gpuDevice);
g_pipelineUnlitMesh.reset();
for (auto &mesh : g_meshes)
{
mesh->VertexBuffer.reset();
mesh->IndexBuffer.reset();
delete mesh;
}
QuadMesh::Terminate();
g_pipelineUnlitMesh.reset();
GPUTexture::Terminate();
@ -221,85 +287,127 @@ namespace ia::iae
SDL_SubmitGPUCommandBuffer(g_cmdBuffer);
}
VOID Renderer::SetFlipH(IN BOOL value)
VOID Renderer::SetState_FlippedH(IN BOOL value)
{
g_flipH = value;
g_renderState.FlippedH = value;
}
VOID Renderer::SetFlipV(IN BOOL value)
VOID Renderer::SetState_FlippedV(IN BOOL value)
{
g_flipV = value;
g_renderState.FlippedV = value;
}
VOID Renderer::BindTexture(IN Handle handle, IN CONST glm::vec4 &colorOverlay)
VOID Renderer::SetState_CameraRelative(IN BOOL value)
{
SDL_GPUTextureSamplerBinding binding{.texture = (SDL_GPUTexture *) handle,
.sampler = (SDL_GPUSampler *) GPUTexture::GetDefaultSampler()};
SDL_BindGPUFragmentSamplers(g_renderPass, 0, &binding, 1);
SDL_PushGPUFragmentUniformData(g_cmdBuffer, 0, &colorOverlay, sizeof(colorOverlay));
g_renderState.CameraRelative = value;
}
VOID SetModelTransformMatrix(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale,
IN FLOAT32 rotation)
VOID Renderer::SetState_ScissorEnabled(IN BOOL value)
{
if (g_parallaxFactor <= 0.0f)
matView = g_camera.GetViewMatrix();
else
matView = glm::mat4(1.0f);
SDL_PushGPUVertexUniformData(g_cmdBuffer, 1, &matView, sizeof(matView));
FLOAT32 shaderParallaxFactor = g_parallaxFactor * g_camera.Position().x / 640.0f;
SDL_PushGPUFragmentUniformData(g_cmdBuffer, 1, &shaderParallaxFactor, sizeof(shaderParallaxFactor));
matModel = glm::mat4(1.0f);
glm::vec3 poff = {0.0f, 0.0f, 0.0f};
const auto f = 1.0f;
if(g_flipH)
{
poff.x = scale.x * f;
}
// [IATODO]: IMPL Flip V
const auto depthTestOffset = sortOffset + (Engine::GetActiveScene()->YSortingEnabled() ? position.y : 0);
matModel =
glm::translate(matModel, {position.x + poff.x, position.y,
position.z + depthTestOffset + (g_parallaxFactor < 0.0f ? 0.0f : -100.0f)});
matModel = glm::rotate(matModel, rotation, glm::vec3(0.0f, 0.0f, 1.0f));
matModel = glm::scale(matModel, scale * (g_flipH ? glm::vec3{-1.0f, 1.0f, 1.0f} : glm::vec3{1.0f, 1.0f, 1.0f}));
SDL_PushGPUVertexUniformData(g_cmdBuffer, 2, &matModel, sizeof(matModel));
g_renderState.ScissorEnabled = value;
}
VOID Renderer::Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale,
IN FLOAT32 rotation, IN Handle vertexBufferHandle, IN INT32 vertexCount)
VOID Renderer::SetState_Scissor(IN CONST glm::vec4 &region)
{
SetModelTransformMatrix(sortOffset, position, scale, rotation);
SDL_GPUBufferBinding bindings[] = {{.buffer = (SDL_GPUBuffer *) vertexBufferHandle, .offset = 0}};
SDL_BindGPUVertexBuffers(g_renderPass, 0, bindings, 1);
SDL_DrawGPUPrimitives(g_renderPass, vertexCount, 1, 0, 0);
g_renderState.Scissor.x = region.x;
g_renderState.Scissor.y = region.y;
g_renderState.Scissor.w = region.z;
g_renderState.Scissor.h = region.w;
}
VOID Renderer::Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale,
IN FLOAT32 rotation, IN Handle vertexBufferHandle, IN Handle indexBufferHandle,
IN INT32 indexCount)
VOID Renderer::SetState_TextureOffset(IN FLOAT32 u, IN FLOAT32 v)
{
SetModelTransformMatrix(sortOffset, position, scale, rotation);
SDL_GPUBufferBinding bindings[] = {{.buffer = (SDL_GPUBuffer *) vertexBufferHandle, .offset = 0},
{.buffer = (SDL_GPUBuffer *) indexBufferHandle, .offset = 0}};
SDL_BindGPUVertexBuffers(g_renderPass, 0, bindings, 1);
SDL_BindGPUIndexBuffer(g_renderPass, &bindings[1], SDL_GPU_INDEXELEMENTSIZE_32BIT);
SDL_DrawGPUIndexedPrimitives(g_renderPass, indexCount, 1, 0, 0, 0);
g_renderState.TextureOffset = {u, v};
}
Camera2D *Renderer::GetCamera()
{
return &g_camera;
}
VOID Renderer::SetParallaxFactor(IN FLOAT32 value)
{
g_parallaxFactor = value;
}
} // namespace ia::iae
namespace ia::iae
{
Handle Renderer::GetMesh_Quad()
{
return g_meshHandleQuad;
}
Handle Renderer::CreateMesh(IN CONST Vector<MeshVertex> &vertices, IN CONST Vector<INT32> &indices)
{
const auto mesh = CreateUnmanagedMesh(vertices, indices);
g_meshes.pushBack((Mesh*)mesh);
return mesh;
}
Handle Renderer::CreateUnmanagedMesh(IN CONST Vector<MeshVertex> &vertices, IN CONST Vector<INT32> &indices)
{
const auto mesh = new Mesh();
mesh->VertexBuffer = GPUBuffer::Create(GPUBuffer::Usage::VERTEX, vertices.data(),
static_cast<UINT32>(vertices.size() * sizeof(vertices[0])));
mesh->IndexBuffer = GPUBuffer::Create(GPUBuffer::Usage::INDEX, indices.data(),
static_cast<UINT32>(indices.size() * sizeof(indices[0])));
mesh->IndexCount = static_cast<UINT32>(indices.size());
return (Handle)mesh;
}
VOID Renderer::DestroyUnmanagedMesh(IN Handle handle)
{
delete ((Mesh*)handle);
}
VOID Renderer::Draw(IN Handle meshHandle, IN Handle textureHandle, IN CONST glm::vec2 &position,
IN CONST glm::vec2 &scale, IN FLOAT32 rotation, IN UINT8 layerIndex, IN INT16 sortIndex,
IN CONST glm::vec4 &colorOverlay)
{
IA_ASSERT(sortIndex <= 0x1FFF);
#pragma pack(push, 1)
STATIC struct
{
INT32 FlippedH{false};
INT32 FlippedV{false};
glm::vec2 TextureOffset{0.0f, 0.0f};
glm::vec4 ColorOverlay{1.0f, 1.0f, 1.0f, 1.0f};
} s_fragmentUniform{};
#pragma pack(pop)
if (g_renderState.CameraRelative)
SDL_PushGPUVertexUniformData(g_cmdBuffer, 1, &matView, sizeof(matView));
else
SDL_PushGPUVertexUniformData(g_cmdBuffer, 1, &matIdentity, sizeof(matIdentity));
if (Engine::GetActiveScene()->YSortingEnabled())
sortIndex += static_cast<INT16>(position.y);
matModel =
glm::translate(glm::mat4(1.0f), glm::vec3{position.x, position.y,
static_cast<FLOAT32>((layerIndex << 13) | (sortIndex & 0x1FFF))});
matModel = glm::rotate(matModel, rotation, glm::vec3(0.0f, 0.0f, 1.0f));
matModel = glm::scale(matModel, glm::vec3{scale.x, scale.y, 1.0f});
SDL_PushGPUVertexUniformData(g_cmdBuffer, 2, &matModel, sizeof(matModel));
s_fragmentUniform.ColorOverlay = colorOverlay;
s_fragmentUniform.FlippedH = g_renderState.FlippedH;
s_fragmentUniform.FlippedV = g_renderState.FlippedV;
s_fragmentUniform.TextureOffset = g_renderState.TextureOffset;
SDL_GPUTextureSamplerBinding textureBinding{
.texture = (SDL_GPUTexture *) (textureHandle ? textureHandle : g_whiteFillTexture.GetHandle()),
.sampler = (SDL_GPUSampler *) GPUTexture::GetDefaultSampler()};
SDL_BindGPUFragmentSamplers(g_renderPass, 0, &textureBinding, 1);
SDL_PushGPUFragmentUniformData(g_cmdBuffer, 0, &s_fragmentUniform, sizeof(s_fragmentUniform));
if (g_renderState.ScissorEnabled)
SDL_SetGPUScissor(g_renderPass, &g_renderState.Scissor);
else
SDL_SetGPUScissor(g_renderPass, &g_defaultScissor);
const auto mesh = (Mesh*)meshHandle;
SDL_GPUBufferBinding bufferBindings[] = {
{.buffer = (SDL_GPUBuffer *) mesh->VertexBuffer->GetHandle(), .offset = 0},
{.buffer = (SDL_GPUBuffer *) mesh->IndexBuffer->GetHandle(), .offset = 0}};
SDL_BindGPUVertexBuffers(g_renderPass, 0, bufferBindings, 1);
SDL_BindGPUIndexBuffer(g_renderPass, &bufferBindings[1], SDL_GPU_INDEXELEMENTSIZE_32BIT);
SDL_DrawGPUIndexedPrimitives(g_renderPass, mesh->IndexCount, 1, 0, 0, 0);
}
} // namespace ia::iae

View File

@ -15,6 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <IAEngine/Scene.hpp>
#include <IAEngine/Physics/Physics.hpp>
namespace ia::iae
{
@ -24,6 +25,15 @@ namespace ia::iae
n.reset();
}
VOID Scene::OnActivate()
{
Physics::Bake();
}
VOID Scene::OnDeactivate()
{
}
VOID Scene::Draw()
{
for (auto &n : m_nodes)

View File

@ -17,30 +17,20 @@
#include <IAEngine/IAEngine.hpp>
#include <IAEngine/Texture.hpp>
#include <IAEngine/Rendering/Mesh/Quad.hpp>
#include <IAEngine/Rendering/Renderer.hpp>
namespace ia::iae
{
Texture::Texture()
Texture::Texture() : m_handle(0), m_extent({100.0f, 100.0f})
{
}
Texture::Texture(IN Handle handle, IN INT32 width, IN INT32 height)
: m_handle(handle), m_size({(FLOAT32) width, (FLOAT32) height, 1.0f})
: m_handle(handle), m_extent({(FLOAT32) width, (FLOAT32) height})
{
}
Texture::~Texture()
{
}
VOID Texture::Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation, IN BOOL flipH,
IN BOOL flipV, IN CONST glm::vec4 &colorOverlay) CONST
{
Renderer::SetFlipH(flipH);
Renderer::SetFlipV(flipV);
Renderer::BindTexture(m_handle, colorOverlay);
QuadMesh::Draw(sortOffset, position, m_size * scale, rotation);
}
} // namespace ia::iae

431
Src/IAEngine/imp/cpp/UI.cpp Normal file
View File

@ -0,0 +1,431 @@
// 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/>.
#include <IAEngine/IAEngine.hpp>
#include <IAEngine/Rendering/GPUTexture.hpp>
#include <IAEngine/Rendering/Renderer.hpp>
#include <IAEngine/UI.hpp>
#include <RmlUi/Core.h>
#include <RmlUi/Debugger.h>
#include <SDL3/SDL_events.h>
#include <IACore/File.hpp>
namespace ia::iae
{
class RmlUIRenderInterface : public Rml::RenderInterface
{
public:
Rml::CompiledGeometryHandle CompileGeometry(Rml::Span<const Rml::Vertex> vertices,
Rml::Span<const int> indices);
void RenderGeometry(Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation,
Rml::TextureHandle texture);
void ReleaseGeometry(Rml::CompiledGeometryHandle geometry);
Rml::TextureHandle LoadTexture(Rml::Vector2i &texture_dimensions, const Rml::String &source);
Rml::TextureHandle GenerateTexture(Rml::Span<const Rml::byte> source, Rml::Vector2i source_dimensions);
void ReleaseTexture(Rml::TextureHandle texture);
void EnableScissorRegion(bool enable);
void SetScissorRegion(Rml::Rectanglei region);
};
Vector<Texture> g_textures;
RmlUIRenderInterface g_rmlUIRenderInterface{};
/* Taken from https://github.com/mikke89/RmlUi/blob/master/Backends/RmlUi_Platform_SDL.cpp */
INT32 GetKeyModifierState()
{
SDL_Keymod sdl_mods = SDL_GetModState();
INT32 keymods = 0;
if (sdl_mods & SDL_KMOD_CTRL)
keymods |= Rml::Input::KM_CTRL;
if (sdl_mods & SDL_KMOD_SHIFT)
keymods |= Rml::Input::KM_SHIFT;
if (sdl_mods & SDL_KMOD_ALT)
keymods |= Rml::Input::KM_ALT;
if (sdl_mods & SDL_KMOD_NUM)
keymods |= Rml::Input::KM_NUMLOCK;
if (sdl_mods & SDL_KMOD_CAPS)
keymods |= Rml::Input::KM_CAPSLOCK;
return keymods;
}
/* Taken from https://github.com/mikke89/RmlUi/blob/master/Backends/RmlUi_Platform_SDL.cpp */
INT32 SDLMouseButtonToRml(IN INT32 button)
{
switch (button)
{
case SDL_BUTTON_LEFT:
return 0;
case SDL_BUTTON_RIGHT:
return 1;
case SDL_BUTTON_MIDDLE:
return 2;
default:
break;
}
return 3;
}
/* Taken from https://github.com/mikke89/RmlUi/blob/master/Backends/RmlUi_Platform_SDL.cpp */
Rml::Input::KeyIdentifier SDLKeyToRml(int sdlkey)
{
switch (sdlkey)
{
case SDLK_UNKNOWN:
return Rml::Input::KI_UNKNOWN;
case SDLK_ESCAPE:
return Rml::Input::KI_ESCAPE;
case SDLK_SPACE:
return Rml::Input::KI_SPACE;
case SDLK_0:
return Rml::Input::KI_0;
case SDLK_1:
return Rml::Input::KI_1;
case SDLK_2:
return Rml::Input::KI_2;
case SDLK_3:
return Rml::Input::KI_3;
case SDLK_4:
return Rml::Input::KI_4;
case SDLK_5:
return Rml::Input::KI_5;
case SDLK_6:
return Rml::Input::KI_6;
case SDLK_7:
return Rml::Input::KI_7;
case SDLK_8:
return Rml::Input::KI_8;
case SDLK_9:
return Rml::Input::KI_9;
case SDLK_A:
return Rml::Input::KI_A;
case SDLK_B:
return Rml::Input::KI_B;
case SDLK_C:
return Rml::Input::KI_C;
case SDLK_D:
return Rml::Input::KI_D;
case SDLK_E:
return Rml::Input::KI_E;
case SDLK_F:
return Rml::Input::KI_F;
case SDLK_G:
return Rml::Input::KI_G;
case SDLK_H:
return Rml::Input::KI_H;
case SDLK_I:
return Rml::Input::KI_I;
case SDLK_J:
return Rml::Input::KI_J;
case SDLK_K:
return Rml::Input::KI_K;
case SDLK_L:
return Rml::Input::KI_L;
case SDLK_M:
return Rml::Input::KI_M;
case SDLK_N:
return Rml::Input::KI_N;
case SDLK_O:
return Rml::Input::KI_O;
case SDLK_P:
return Rml::Input::KI_P;
case SDLK_Q:
return Rml::Input::KI_Q;
case SDLK_R:
return Rml::Input::KI_R;
case SDLK_S:
return Rml::Input::KI_S;
case SDLK_T:
return Rml::Input::KI_T;
case SDLK_U:
return Rml::Input::KI_U;
case SDLK_V:
return Rml::Input::KI_V;
case SDLK_W:
return Rml::Input::KI_W;
case SDLK_X:
return Rml::Input::KI_X;
case SDLK_Y:
return Rml::Input::KI_Y;
case SDLK_Z:
return Rml::Input::KI_Z;
case SDLK_SEMICOLON:
return Rml::Input::KI_OEM_1;
case SDLK_PLUS:
return Rml::Input::KI_OEM_PLUS;
case SDLK_COMMA:
return Rml::Input::KI_OEM_COMMA;
case SDLK_MINUS:
return Rml::Input::KI_OEM_MINUS;
case SDLK_PERIOD:
return Rml::Input::KI_OEM_PERIOD;
case SDLK_SLASH:
return Rml::Input::KI_OEM_2;
case SDLK_GRAVE:
return Rml::Input::KI_OEM_3;
case SDLK_LEFTBRACKET:
return Rml::Input::KI_OEM_4;
case SDLK_BACKSLASH:
return Rml::Input::KI_OEM_5;
case SDLK_RIGHTBRACKET:
return Rml::Input::KI_OEM_6;
case SDLK_DBLAPOSTROPHE:
return Rml::Input::KI_OEM_7;
case SDLK_KP_0:
return Rml::Input::KI_NUMPAD0;
case SDLK_KP_1:
return Rml::Input::KI_NUMPAD1;
case SDLK_KP_2:
return Rml::Input::KI_NUMPAD2;
case SDLK_KP_3:
return Rml::Input::KI_NUMPAD3;
case SDLK_KP_4:
return Rml::Input::KI_NUMPAD4;
case SDLK_KP_5:
return Rml::Input::KI_NUMPAD5;
case SDLK_KP_6:
return Rml::Input::KI_NUMPAD6;
case SDLK_KP_7:
return Rml::Input::KI_NUMPAD7;
case SDLK_KP_8:
return Rml::Input::KI_NUMPAD8;
case SDLK_KP_9:
return Rml::Input::KI_NUMPAD9;
case SDLK_KP_ENTER:
return Rml::Input::KI_NUMPADENTER;
case SDLK_KP_MULTIPLY:
return Rml::Input::KI_MULTIPLY;
case SDLK_KP_PLUS:
return Rml::Input::KI_ADD;
case SDLK_KP_MINUS:
return Rml::Input::KI_SUBTRACT;
case SDLK_KP_PERIOD:
return Rml::Input::KI_DECIMAL;
case SDLK_KP_DIVIDE:
return Rml::Input::KI_DIVIDE;
case SDLK_KP_EQUALS:
return Rml::Input::KI_OEM_NEC_EQUAL;
case SDLK_BACKSPACE:
return Rml::Input::KI_BACK;
case SDLK_TAB:
return Rml::Input::KI_TAB;
case SDLK_CLEAR:
return Rml::Input::KI_CLEAR;
case SDLK_RETURN:
return Rml::Input::KI_RETURN;
case SDLK_PAUSE:
return Rml::Input::KI_PAUSE;
case SDLK_CAPSLOCK:
return Rml::Input::KI_CAPITAL;
case SDLK_PAGEUP:
return Rml::Input::KI_PRIOR;
case SDLK_PAGEDOWN:
return Rml::Input::KI_NEXT;
case SDLK_END:
return Rml::Input::KI_END;
case SDLK_HOME:
return Rml::Input::KI_HOME;
case SDLK_LEFT:
return Rml::Input::KI_LEFT;
case SDLK_UP:
return Rml::Input::KI_UP;
case SDLK_RIGHT:
return Rml::Input::KI_RIGHT;
case SDLK_DOWN:
return Rml::Input::KI_DOWN;
case SDLK_INSERT:
return Rml::Input::KI_INSERT;
case SDLK_DELETE:
return Rml::Input::KI_DELETE;
case SDLK_HELP:
return Rml::Input::KI_HELP;
case SDLK_F1:
return Rml::Input::KI_F1;
case SDLK_F2:
return Rml::Input::KI_F2;
case SDLK_F3:
return Rml::Input::KI_F3;
case SDLK_F4:
return Rml::Input::KI_F4;
case SDLK_F5:
return Rml::Input::KI_F5;
case SDLK_F6:
return Rml::Input::KI_F6;
case SDLK_F7:
return Rml::Input::KI_F7;
case SDLK_F8:
return Rml::Input::KI_F8;
case SDLK_F9:
return Rml::Input::KI_F9;
case SDLK_F10:
return Rml::Input::KI_F10;
case SDLK_F11:
return Rml::Input::KI_F11;
case SDLK_F12:
return Rml::Input::KI_F12;
case SDLK_F13:
return Rml::Input::KI_F13;
case SDLK_F14:
return Rml::Input::KI_F14;
case SDLK_F15:
return Rml::Input::KI_F15;
case SDLK_NUMLOCKCLEAR:
return Rml::Input::KI_NUMLOCK;
case SDLK_SCROLLLOCK:
return Rml::Input::KI_SCROLL;
case SDLK_LSHIFT:
return Rml::Input::KI_LSHIFT;
case SDLK_RSHIFT:
return Rml::Input::KI_RSHIFT;
case SDLK_LCTRL:
return Rml::Input::KI_LCONTROL;
case SDLK_RCTRL:
return Rml::Input::KI_RCONTROL;
case SDLK_LALT:
return Rml::Input::KI_LMENU;
case SDLK_RALT:
return Rml::Input::KI_RMENU;
case SDLK_LGUI:
return Rml::Input::KI_LMETA;
case SDLK_RGUI:
return Rml::Input::KI_RMETA;
default:
break;
}
return Rml::Input::KI_UNKNOWN;
}
} // namespace ia::iae
namespace ia::iae
{
Rml::Context *g_context{};
VOID UI::Initialize(IN INT32 width, IN INT32 height)
{
Rml::SetRenderInterface(&g_rmlUIRenderInterface);
Rml::Initialise();
g_context = Rml::CreateContext("main", Rml::Vector2i(width, height));
//Rml::Debugger::Initialise(g_context);
//Rml::Debugger::SetVisible(true);
//Rml::LoadFontFace("Roboto-Black.ttf");
}
VOID UI::Terminate()
{
Rml::Shutdown();
}
VOID UI::Update()
{
g_context->Update();
}
VOID UI::Draw()
{
g_context->Render();
}
VOID UI::OnEvent(IN PVOID _event)
{
const auto keymods = GetKeyModifierState();
const auto event = (SDL_Event *) _event;
switch (event->type)
{
case SDL_EventType::SDL_EVENT_MOUSE_MOTION:
g_context->ProcessMouseMove(event->motion.x, event->motion.y, keymods);
break;
case SDL_EventType::SDL_EVENT_MOUSE_BUTTON_UP:
g_context->ProcessMouseButtonUp(SDLMouseButtonToRml(event->button.button), keymods);
break;
case SDL_EventType::SDL_EVENT_MOUSE_BUTTON_DOWN:
g_context->ProcessMouseButtonDown(SDLMouseButtonToRml(event->button.button), keymods);
break;
case SDL_EventType::SDL_EVENT_KEY_DOWN:
g_context->ProcessKeyDown(SDLKeyToRml(event->key.key), keymods);
break;
case SDL_EventType::SDL_EVENT_KEY_UP:
g_context->ProcessKeyUp(SDLKeyToRml(event->key.key), keymods);
break;
}
}
VOID UI::OnResize(IN INT32 width, IN INT32 height)
{
}
} // namespace ia::iae
namespace ia::iae
{
Rml::CompiledGeometryHandle RmlUIRenderInterface::CompileGeometry(Rml::Span<const Rml::Vertex> vertices,
Rml::Span<const int> indices)
{
Vector<MeshVertex> _vertices;
Vector<INT32> _indices;
for (const auto &v : vertices)
_vertices.pushBack({glm::vec3{v.position.x, v.position.y, 0.0f}, glm::vec2{v.tex_coord.x, v.tex_coord.y},
glm::vec4{v.colour.red / 255.0f, v.colour.green / 255.0f, v.colour.blue / 255.0f,
v.colour.alpha / 255.0f}});
for (const auto &v : indices)
_indices.pushBack(v);
return Renderer::CreateMesh(_vertices, _indices);
}
void RmlUIRenderInterface::RenderGeometry(Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation,
Rml::TextureHandle texture)
{
Renderer::SetState_FlippedH(false);
Renderer::SetState_FlippedV(false);
Renderer::SetState_CameraRelative(false);
Renderer::SetState_TextureOffset(0, 0);
if (texture)
Renderer::Draw(geometry, g_textures[texture - 1].GetHandle(), {translation.x, translation.y}, {1.0f, 1.0f});
else
Renderer::Draw(geometry, 0, {translation.x, translation.y}, {1.0f, 1.0f});
}
void RmlUIRenderInterface::ReleaseGeometry(Rml::CompiledGeometryHandle geometry)
{
}
Rml::TextureHandle RmlUIRenderInterface::LoadTexture(Rml::Vector2i &texture_dimensions, const Rml::String &source)
{
return 0;
}
Rml::TextureHandle RmlUIRenderInterface::GenerateTexture(Rml::Span<const Rml::byte> source,
Rml::Vector2i source_dimensions)
{
g_textures.pushBack(
Engine::CreateTexture((PCUINT8) source.data(), (INT32) source_dimensions.x, (INT32) source_dimensions.y));
return g_textures.size();
}
void RmlUIRenderInterface::ReleaseTexture(Rml::TextureHandle texture)
{
}
void RmlUIRenderInterface::EnableScissorRegion(bool enable)
{
Renderer::SetState_ScissorEnabled(enable);
}
void RmlUIRenderInterface::SetScissorRegion(Rml::Rectanglei region)
{
Renderer::SetState_Scissor({region.Left(), region.Top(), region.Right(), region.Bottom()});
}
} // namespace ia::iae

View File

@ -2,22 +2,25 @@
#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;
layout(set = 3, binding = 0) uniform UniformBufferObject {
bool flippedH;
bool flippedV;
vec2 uvOffset;
vec4 colorOverlay;
} ubo;
layout(set = 3, binding = 1) uniform UniformBufferObject2 {
float parallaxFactor;
} ubo2;
void main()
{
vec2 uv = inTexCoord;
uv.x += ubo2.parallaxFactor;
outColor = texture(texSampler, uv) * ubo.colorOverlay;
uv += ubo.uvOffset;
if(ubo.flippedH) uv.x = 1.0 - uv.x;
if(ubo.flippedV) uv.y = 1.0 - uv.y;
outColor = texture(texSampler, uv) * inVertexColor * ubo.colorOverlay;
if(outColor.w < 0.1)
discard;
}

View File

@ -3,8 +3,10 @@
layout (location = 0) in vec3 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;
layout(set = 1, binding = 0) uniform UBO_Vertex_PerScene {
mat4 projection;
@ -20,4 +22,5 @@ void main()
{
gl_Position = uboPerScene.projection * uboPerFrame.view * uboPerDraw.model * vec4(inPosition, 1.0f);
outTexCoord = inTexCoord;
outVertexColor = inVertexColor;
}

File diff suppressed because one or more lines are too long

View File

@ -16,13 +16,13 @@
#pragma once
#include <IAEngine/Components/Component.hpp>
#include <IAEngine/Components/TextureRenderer.hpp>
#include <IAEngine/Rendering/GPUTexture.hpp>
#include <IAEngine/Texture.hpp>
namespace ia::iae
{
class AtlasRendererComponent : public IComponent
class AtlasRendererComponent : public TextureRendererComponent
{
public:
struct TileGrid
@ -35,7 +35,7 @@ namespace ia::iae
INT32 TileCountY{};
private:
Vector<Texture> m_tileTextures{};
Vector<class Texture> m_tileTextures{};
friend class AtlasRendererComponent;
};
@ -46,8 +46,8 @@ namespace ia::iae
public:
VOID SetGrid(IN INT32 tileWidth, IN INT32 tileHeight, IN INT32 tileCountX, IN INT32 tileCountY);
VOID SetGridTileTexture(IN INT32 index, IN Texture texture);
VOID SetGridTileTexture(IN INT32 x, IN INT32 y, IN Texture texture);
VOID SetGridTileTexture(IN INT32 index, IN class Texture texture);
VOID SetGridTileTexture(IN INT32 x, IN INT32 y, IN class Texture texture);
VOID BakeGrid();
public:
@ -56,7 +56,6 @@ namespace ia::iae
private:
TileGrid m_tileGrid{};
Texture m_bakedTexture{};
RefPtr<GPUTexture> m_bakedGPUTexture{};
};
} // namespace ia::iae

View File

@ -20,21 +20,41 @@
namespace ia::iae
{
class Physics;
class PhysicsBody2DComponent;
class BoxCollider2DComponent : public IComponent
{
public:
BoxCollider2DComponent(IN Node *node);
BOOL& IsDynamic(){return m_isDynamic;}
VOID OnCollisionEnter(IN Node *other);
VOID OnCollisionExit(IN Node *other);
glm::vec4& Shape()
public:
BOOL &IsDebugDrawEnabled()
{
return m_shape;
return m_isDebugDrawEnabled;
}
CONST glm::vec4& AbsoluteShape() CONST
BOOL &CollisionsEnabled()
{
return m_absoluteShape;
return m_collisionsEnabled;
}
BOOL &IsTrigger()
{
return m_isTrigger;
}
glm::vec4 &Rect()
{
return m_rect;
}
Handle PhysicsHandle() CONST
{
return m_physicsHandle;
}
public:
@ -42,8 +62,11 @@ namespace ia::iae
VOID Update();
private:
BOOL m_isDynamic{};
glm::vec4 m_shape{};
glm::vec4 m_absoluteShape{};
glm::vec4 m_rect{};
BOOL m_isTrigger{false};
BOOL m_isDebugDrawEnabled{false};
BOOL m_collisionsEnabled{true};
Handle m_physicsHandle{INVALID_HANDLE};
PhysicsBody2DComponent *m_body{};
};
} // namespace ia::iae

View File

@ -1,24 +0,0 @@
// 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>
namespace ia::iae
{
}

View File

@ -0,0 +1,66 @@
// 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>
namespace ia::iae
{
class PhysicsBody2DComponent : public IComponent
{
public:
PhysicsBody2DComponent(IN Node *node);
VOID SetVelocity(IN glm::vec2 v);
VOID SetVelocityX(IN FLOAT32 v);
VOID SetVelocityY(IN FLOAT32 v);
VOID ApplyForce(IN glm::vec2 force);
VOID Jump(IN FLOAT32 velocity);
public:
BOOL &IsGrounded()
{
return m_isGrounded;
}
BOOL &IsDynamic()
{
return m_isDynamic;
}
BOOL &IsRotationLocked()
{
return m_isRotationLocked;
}
Handle PhysicsHandle() CONST
{
return m_physicsHandle;
}
public:
VOID Draw();
VOID Update();
private:
BOOL m_isDynamic{};
BOOL m_isGrounded{};
BOOL m_isRotationLocked{};
Handle m_physicsHandle{INVALID_HANDLE};
};
} // namespace ia::iae

View File

@ -16,23 +16,24 @@
#pragma once
#include <IAEngine/Components/Component.hpp>
#include <IAEngine/Components/TextureRenderer.hpp>
#include <IAEngine/Texture.hpp>
namespace ia::iae
{
class SpriteRendererComponent : public IComponent
class SpriteRendererComponent : public TextureRendererComponent
{
public:
struct AnimationKeyFrame
{
INT32 Duration{100};
glm::vec3 Position{};
glm::vec3 Rotation{};
glm::vec3 Scale{1.0f, 1.0f, 1.0f};
glm::vec4 ColorOverlay{1.0f, 1.0f, 1.0f, 1.0f};
BOOL ShouldInterpolate{};
Texture Texture;
INT32 Duration{100};
BOOL ShouldInterpolate{};
FLOAT32 Rotation{};
glm::vec2 Position{};
glm::vec2 Scale{1.0f, 1.0f};
glm::vec4 ColorOverlay{1.0f, 1.0f, 1.0f, 1.0f};
};
struct Animation
@ -61,26 +62,6 @@ namespace ia::iae
return m_animations;
}
BOOL &IsFlippedV()
{
return m_isFlippedV;
}
BOOL &IsFlippedH()
{
return m_isFlippedH;
}
FLOAT32 &SortOffset()
{
return m_sortOffset;
}
FLOAT32 &ParallaxFactor()
{
return m_parallaxFactor;
}
BOOL &ReverseAnimation()
{
return m_reverseAnimation;
@ -97,10 +78,6 @@ namespace ia::iae
private:
BOOL m_reverseAnimation{};
FLOAT32 m_parallaxFactor{0.0f};
FLOAT32 m_sortOffset{};
BOOL m_isFlippedV{false};
BOOL m_isFlippedH{false};
FLOAT32 m_timelinePosition{};
Animation m_activeAnimation{};
Handle m_activeAnimationHandle{INVALID_HANDLE};

View File

@ -26,14 +26,59 @@ namespace ia::iae
public:
TextureRendererComponent(IN Node *node);
Texture &Texture()
Texture &CurrentTexture()
{
return m_texture;
}
glm::vec3 &Position()
glm::vec2 &PositionOffset()
{
return m_position;
return m_positionOffset;
}
glm::vec2 &ScaleOffset()
{
return m_scaleOffset;
}
FLOAT32 &RotationOffset()
{
return m_rotationOffset;
}
glm::vec2 &TextureOffset()
{
return m_textureOffset;
}
glm::vec4 &ColorOverlay()
{
return m_colorOverlay;
}
BOOL &IsFlippedH()
{
return m_isFlippedH;
}
BOOL &IsFlippedV()
{
return m_isFlippedV;
}
BOOL &IsCameraRelative()
{
return m_isCameraRelative;
}
BOOL& ShouldDrawOutline()
{
return m_shouldDrawOutline;
}
glm::vec4& OutlineColor()
{
return m_outlineColor;
}
public:
@ -41,7 +86,16 @@ namespace ia::iae
VOID Update();
private:
BOOL m_isFlippedH{};
BOOL m_isFlippedV{};
BOOL m_shouldDrawOutline{};
glm::vec4 m_outlineColor{0.25f, 0.25f, 0.25f, 0.75f};
BOOL m_isCameraRelative{};
glm::vec2 m_positionOffset{};
glm::vec2 m_scaleOffset{};
FLOAT32 m_rotationOffset{};
glm::vec2 m_textureOffset{};
glm::vec4 m_colorOverlay{1.0f, 1.0f, 1.0f, 1.0f};
class Texture m_texture;
glm::vec3 m_position;
};
} // namespace ia::iae

View File

@ -18,6 +18,7 @@
#include <IAEngine/Components/Component.hpp>
#include <IAEngine/Nodes/Transform.hpp>
#include <IAEngine/Nodes/Tag.hpp>
#include <IAEngine/Texture.hpp>
namespace ia::iae
@ -49,16 +50,43 @@ namespace ia::iae
return m_components;
}
public:
FLOAT32 &SortOffset()
UINT8 GetLayerIndex() CONST
{
return m_sortOffset;
return m_layerIndex;
}
UINT8 &LayerIndex()
{
return m_layerIndex;
}
glm::vec2 &DrawnSize()
{
return m_drawnSize;
}
String &Name()
{
return m_name;
}
NodeTags& Tags()
{
return m_tags;
}
BOOL HasTag(IN NodeTags tag) CONST
{
return m_tags & tag;
}
protected:
Scene *m_scene{};
FLOAT32 m_sortOffset{};
UINT8 m_layerIndex{};
BOOL m_isEnabled{true};
glm::vec2 m_drawnSize{};
String m_name;
NodeTags m_tags;
protected:
Vector<RefPtr<IComponent>> m_components;

View File

@ -0,0 +1,69 @@
// 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
{
using NodeTags = UINT64;
CONSTEXPR NodeTags NODE_TAG_PLAYER = (((NodeTags) 1) << 0);
CONSTEXPR NodeTags NODE_TAG_ENEMY = (((NodeTags) 1) << 1);
CONSTEXPR NodeTags NODE_TAG_GROUND = (((NodeTags) 1) << 2);
CONSTEXPR NodeTags NODE_TAG_OBSTACLE = (((NodeTags) 1) << 3);
CONSTEXPR NodeTags NODE_TAG_PICKUP = (((NodeTags) 1) << 4);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_1 = (((NodeTags) 1) << 5);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_2 = (((NodeTags) 1) << 6);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_3 = (((NodeTags) 1) << 7);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_4 = (((NodeTags) 1) << 8);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_5 = (((NodeTags) 1) << 9);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_6 = (((NodeTags) 1) << 10);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_7 = (((NodeTags) 1) << 11);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_8 = (((NodeTags) 1) << 12);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_9 = (((NodeTags) 1) << 13);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_10 = (((NodeTags) 1) << 14);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_11 = (((NodeTags) 1) << 15);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_12 = (((NodeTags) 1) << 16);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_13 = (((NodeTags) 1) << 17);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_14 = (((NodeTags) 1) << 18);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_15 = (((NodeTags) 1) << 19);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_16 = (((NodeTags) 1) << 20);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_17 = (((NodeTags) 1) << 21);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_18 = (((NodeTags) 1) << 22);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_19 = (((NodeTags) 1) << 23);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_20 = (((NodeTags) 1) << 24);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_21 = (((NodeTags) 1) << 25);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_22 = (((NodeTags) 1) << 26);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_23 = (((NodeTags) 1) << 27);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_24 = (((NodeTags) 1) << 28);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_25 = (((NodeTags) 1) << 29);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_26 = (((NodeTags) 1) << 30);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_27 = (((NodeTags) 1) << 31);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_28 = (((NodeTags) 1) << 32);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_29 = (((NodeTags) 1) << 33);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_30 = (((NodeTags) 1) << 34);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_31 = (((NodeTags) 1) << 35);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_32 = (((NodeTags) 1) << 36);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_33 = (((NodeTags) 1) << 37);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_34 = (((NodeTags) 1) << 38);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_35 = (((NodeTags) 1) << 39);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_36 = (((NodeTags) 1) << 40);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_37 = (((NodeTags) 1) << 41);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_38 = (((NodeTags) 1) << 42);
CONSTEXPR NodeTags NODE_TAG_CUSTOM_39 = (((NodeTags) 1) << 43);
} // namespace ia::iae

View File

@ -20,55 +20,70 @@
namespace ia::iae
{
template<typename _node_type>
class Transform
template<typename _node_type> class Transform
{
public:
VOID SetLocalPosition(IN CONST glm::vec3 &v)
VOID SetLocalSortIndex(IN INT16 index)
{
m_local.SortIndex = index;
RecalculateSortIndex();
}
VOID SetLocalPosition(IN CONST glm::vec2 &v)
{
m_local.Position = v;
RecalculatePosition();
}
VOID SetLocalScale(IN CONST glm::vec3 &v)
VOID SetLocalScale(IN CONST glm::vec2 &v)
{
m_local.Scale = v;
RecalculateScale();
}
VOID SetLocalRotation(IN CONST glm::vec3 &v)
VOID SetLocalRotation(IN FLOAT32 v)
{
m_local.Rotation = v;
RecalculateRotation();
}
public:
CONST glm::vec3 &GetPosition() CONST
INT16 GetSortIndex() CONST
{
return m_global.SortIndex;
}
CONST glm::vec2 &GetPosition() CONST
{
return m_global.Position;
}
CONST glm::vec3 &GetScale() CONST
CONST glm::vec2 &GetScale() CONST
{
return m_global.Scale;
}
CONST glm::vec3 &GetRotation() CONST
CONST FLOAT32 &GetRotation() CONST
{
return m_global.Rotation;
}
CONST glm::vec3 &GetLocalPosition() CONST
INT16 GetLocalSortIndex() CONST
{
return m_local.SortIndex;
}
CONST glm::vec2 &GetLocalPosition() CONST
{
return m_local.Position;
}
CONST glm::vec3 &GetLocalScale() CONST
CONST glm::vec2 &GetLocalScale() CONST
{
return m_local.Scale;
}
CONST glm::vec3 &GetLocalRotation() CONST
CONST FLOAT32 &GetLocalRotation() CONST
{
return m_local.Rotation;
}
@ -78,23 +93,30 @@ namespace ia::iae
Vector<RefPtr<_node_type>> m_children{};
protected:
VOID RecalculateSortIndex()
{
m_global.SortIndex = (m_parent ? m_parent->GetSortIndex() : 0) + m_local.SortIndex;
for (auto &c : m_children)
c->RecalculateSortIndex();
}
VOID RecalculatePosition()
{
m_global.Position = (m_parent ? m_parent->GetPosition() : glm::vec3{}) + m_local.Position;
m_global.Position = (m_parent ? m_parent->GetPosition() : glm::vec2{}) + m_local.Position;
for (auto &c : m_children)
c->RecalculatePosition();
}
VOID RecalculateRotation()
{
m_global.Rotation = (m_parent ? m_parent->GetRotation() : glm::vec3{}) + m_local.Rotation;
m_global.Rotation = (m_parent ? m_parent->GetRotation() : 0) + m_local.Rotation;
for (auto &c : m_children)
c->RecalculateRotation();
}
VOID RecalculateScale()
{
m_global.Scale = (m_parent ? m_parent->GetScale() : glm::vec3{}) + m_local.Scale;
m_global.Scale = (m_parent ? m_parent->GetScale() : glm::vec2{}) + m_local.Scale;
for (auto &c : m_children)
c->RecalculateScale();
}
@ -102,9 +124,10 @@ namespace ia::iae
private:
struct
{
glm::vec3 Position{0.0f, 0.0f, 0.0f};
glm::vec3 Rotation{0.0f, 0.0f, 0.0f};
glm::vec3 Scale{1.0f, 1.0f, 1.0f};
INT16 SortIndex{};
FLOAT32 Rotation{0.0f};
glm::vec2 Position{0.0f, 0.0f};
glm::vec2 Scale{1.0f, 1.0f};
} m_local{}, m_global{};
};
} // namespace ia::iae

View File

@ -17,6 +17,7 @@
#pragma once
#include <IAEngine/Components/BoxCollider2D.hpp>
#include <IAEngine/Components/PhysicsBody2D.hpp>
namespace ia::iae
{
@ -26,8 +27,18 @@ namespace ia::iae
STATIC VOID Initialize();
STATIC VOID Terminate();
STATIC VOID Bake();
STATIC VOID Update();
STATIC VOID AddCollider(IN BoxCollider2DComponent* collider);
STATIC Handle AddBody(IN PhysicsBody2DComponent* body);
STATIC Handle AddColliderToBody(IN Handle bodyHandle, IN BoxCollider2DComponent* collider);
STATIC FLOAT32 GetBodyRotation(IN Handle handle);
STATIC glm::vec2 GetBodyPosition(IN Handle handle);
STATIC VOID SetBodyVelocity(IN Handle handle, IN glm::vec2 v);
STATIC VOID SetBodyVelocityX(IN Handle handle, IN FLOAT32 v);
STATIC VOID SetBodyVelocityY(IN Handle handle, IN FLOAT32 v);
STATIC VOID ApplyBodyForce(IN Handle handle, IN glm::vec2 force);
};
}

View File

@ -16,7 +16,7 @@
#pragma once
#include <IAEngine/Base.hpp>
#include <IAEngine/Rendering/Types.hpp>
namespace ia::iae
{
@ -32,6 +32,9 @@ namespace ia::iae
std::function<VOID()> ContentDrawCallback{};
};
public:
STATIC CONSTEXPR UINT8 MAX_LAYER_INDEX = 255;
public:
STATIC BOOL Initialize();
STATIC VOID Terminate();
@ -44,16 +47,26 @@ namespace ia::iae
STATIC VOID BeginFrame();
STATIC VOID EndFrame();
STATIC VOID BindTexture(IN Handle handle, IN CONST glm::vec4& colorOverlay);
STATIC VOID SetState_FlippedH(IN BOOL value);
STATIC VOID SetState_FlippedV(IN BOOL value);
STATIC VOID SetState_CameraRelative(IN BOOL value);
STATIC VOID SetState_ScissorEnabled(IN BOOL value);
STATIC VOID SetState_Scissor(IN CONST glm::vec4 &region);
STATIC VOID SetState_TextureOffset(IN FLOAT32 u, IN FLOAT32 v);
STATIC VOID Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation, IN Handle vertexBufferHandle, IN INT32 vertexCount);
STATIC VOID Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation, IN Handle vertexBufferHandle, IN Handle indexBufferHandle, IN INT32 indexCount);
public:
STATIC Handle GetMesh_Quad();
STATIC Camera2D* GetCamera();
STATIC Handle CreateMesh(IN CONST Vector<MeshVertex> &vertices, IN CONST Vector<INT32> &indices);
STATIC Handle CreateUnmanagedMesh(IN CONST Vector<MeshVertex> &vertices, IN CONST Vector<INT32> &indices);
STATIC VOID DestroyUnmanagedMesh(IN Handle handle);
STATIC VOID SetFlipH(IN BOOL value);
STATIC VOID SetFlipV(IN BOOL value);
STATIC VOID SetParallaxFactor(IN FLOAT32 value);
STATIC VOID Draw(IN Handle meshHandle, IN Handle textureHandle, IN CONST glm::vec2 &position,
IN CONST glm::vec2 &scale, IN FLOAT32 rotation = 0, IN UINT8 layerIndex = 0,
IN INT16 sortIndex = 0, IN CONST glm::vec4 &colorOverlay = {1.0f, 1.0f, 1.0f, 1.0f});
public:
STATIC Camera2D *GetCamera();
public:
STATIC INT32 Width()

View File

@ -20,11 +20,10 @@
namespace ia::iae
{
struct Vertex_Mesh
struct MeshVertex
{
glm::vec3 Position{};
glm::vec2 UV{};
glm::vec4 Color{};
};
}

View File

@ -24,6 +24,9 @@ namespace ia::iae
{
public:
~Scene();
VOID OnActivate();
VOID OnDeactivate();
VOID Draw();
VOID Update();

View File

@ -23,14 +23,10 @@ namespace ia::iae
class Texture
{
public:
Texture(IN Handle handle, IN INT32 width, IN INT32 height);
Texture();
Texture(IN Handle handle, IN INT32 width, IN INT32 height);
~Texture();
public:
VOID Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation,
IN BOOL flipH, IN BOOL flipV, IN CONST glm::vec4 &colorOverlay) CONST;
public:
Handle GetHandle() CONST
{
@ -39,16 +35,21 @@ namespace ia::iae
INT32 GetWidth() CONST
{
return (INT32) m_size.x;
return (INT32) m_extent.x;
}
INT32 GetHeight() CONST
{
return (INT32) m_size.y;
return (INT32) m_extent.y;
}
CONST glm::vec2 &GetExtent() CONST
{
return m_extent;
}
private:
Handle m_handle{INVALID_HANDLE};
glm::vec3 m_size{};
glm::vec2 m_extent{};
};
} // namespace ia::iae

View File

@ -20,12 +20,16 @@
namespace ia::iae
{
class QuadMesh
class UI
{
public:
STATIC VOID Initialize();
public:
STATIC VOID Initialize(IN INT32 width, IN INT32 height);
STATIC VOID Terminate();
STATIC VOID Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation);
STATIC VOID Draw();
STATIC VOID Update();
STATIC VOID OnEvent(IN PVOID event);
STATIC VOID OnResize(IN INT32 width, IN INT32 height);
};
} // namespace ia::iae
}

19
Vendor/CMakeLists.txt vendored
View File

@ -1,4 +1,4 @@
# -----------------------------------------------
#-----------------------------------------------
# SDL3
# -----------------------------------------------
set(SDL_TEST_LIBRARY OFF)
@ -41,3 +41,20 @@ target_include_directories(
# GLM
# -----------------------------------------------
add_subdirectory(glm/)
# -----------------------------------------------
# FreeType
# -----------------------------------------------
add_subdirectory(freetype/)
add_library(Freetype::Freetype ALIAS freetype)
# -----------------------------------------------
# RmlUI
# -----------------------------------------------
add_subdirectory(RmlUI/)
# -----------------------------------------------
# Box2D
# -----------------------------------------------
add_subdirectory(box2d/)

1
Vendor/RmlUi vendored Submodule

Submodule Vendor/RmlUi added at 5556ac74c5

1
Vendor/box2d vendored Submodule

Submodule Vendor/box2d added at f86d1827eb

1
Vendor/freetype vendored Submodule

Submodule Vendor/freetype added at 41eab7e66d