From a3732a7d7a01566e1eb872c5830af093ff89c683 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 15 Oct 2026 20:58:45 +0200 Subject: [PATCH 01/18] Add 2D scene management system This commit adds a comprehensive 2D scene management system to the Segfault engine: - Scene2DManager: Central manager for loading, unloading, and rendering 2D scenes - Scene2D: Base class for 2D scenes with entity-component system - Camera2D: Orthographic camera for 2D rendering with zoom, pan, and viewport support - Layer2D: Rendering layers with custom cameras and render order - Component system for 2D: - Transform2DComponent: Position, rotation, scale, origin - SpriteComponent: Textured quads with color, alpha, flipping - TextComponent: Text rendering with font support and alignment - UISpriteComponent: UI elements with nine-slice and interaction support - Render2DComponent: Layer assignment and render order - Animation2DComponent: Frame-based sprite animation - SimpleScene2D: Convenience class for basic 2D scenes - UIScene2D: Specialized scene for UI elements with input handling All classes follow the existing code style and are properly integrated into the CMake build system. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- src/runtime/CMakeLists.txt | 5 + src/runtime/scene/Scene2D.cpp | 1007 ++++++++++++++++++++++++ src/runtime/scene/Scene2D.h | 1052 ++++++++++++++++++++++++++ src/runtime/scene/Scene2DManager.cpp | 294 +++++++ src/runtime/scene/Scene2DManager.h | 187 +++++ 5 files changed, 2545 insertions(+) create mode 100644 src/runtime/scene/Scene2D.cpp create mode 100644 src/runtime/scene/Scene2D.h create mode 100644 src/runtime/scene/Scene2DManager.cpp create mode 100644 src/runtime/scene/Scene2DManager.h diff --git a/src/runtime/CMakeLists.txt b/src/runtime/CMakeLists.txt index d68a747..c269f49 100644 --- a/src/runtime/CMakeLists.txt +++ b/src/runtime/CMakeLists.txt @@ -13,6 +13,11 @@ SET(segfault_core_src set (segfault_scene_src scene/camera.h + scene/scene.h + scene/Scene2D.h + scene/Scene2D.cpp + scene/Scene2DManager.h + scene/Scene2DManager.cpp ) SET(segfault_renderer_src diff --git a/src/runtime/scene/Scene2D.cpp b/src/runtime/scene/Scene2D.cpp new file mode 100644 index 0000000..9fe13b9 --- /dev/null +++ b/src/runtime/scene/Scene2D.cpp @@ -0,0 +1,1007 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2015-2026 Segfault by Kim Kulling + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-----------------------------------------------------------------------------------------------*/ + +#include "scene/Scene2D.h" +#include "renderer/RHI.h" + +#include + +namespace segfault::scene { + + //--------------------------------------------------------------------------------------------- + // Camera2D implementation + //--------------------------------------------------------------------------------------------- + + Camera2D::Camera2D() : mZoom(1.0f), mRotation(0.0f), mViewMatrix(1.0f), mProjectionMatrix(1.0f), mDirty(true) { + updateMatrices(); + } + + void Camera2D::setOrthographic(float left, float right, float bottom, float top, float near, float far) { + mProjectionMatrix = glm::ortho(left, right, bottom, top, near, far); + mDirty = true; + } + + void Camera2D::setOrthographicFromViewport(uint32_t width, uint32_t height, float near, float far) { + float halfWidth = static_cast(width) * 0.5f; + float halfHeight = static_cast(height) * 0.5f; + setOrthographic(-halfWidth, halfWidth, -halfHeight, halfHeight, near, far); + mViewport.x = 0; + mViewport.y = 0; + mViewport.width = width; + mViewport.height = height; + mDirty = true; + } + + void Camera2D::setPosition(const glm::vec2& position) { + if (mPosition != position) { + mPosition = position; + mDirty = true; + } + } + + void Camera2D::setPosition(float x, float y) { + setPosition(glm::vec2(x, y)); + } + + const glm::vec2& Camera2D::getPosition() const { + return mPosition; + } + + void Camera2D::setZoom(float zoom) { + if (mZoom != zoom) { + mZoom = std::max(0.001f, zoom); // Prevent zero or negative zoom + mDirty = true; + } + } + + float Camera2D::getZoom() const { + return mZoom; + } + + void Camera2D::setRotation(float angle) { + if (mRotation != angle) { + mRotation = angle; + mDirty = true; + } + } + + float Camera2D::getRotation() const { + return mRotation; + } + + const glm::mat4& Camera2D::getViewMatrix() const { + if (mDirty) { + const_cast(this)->updateMatrices(); + } + return mViewMatrix; + } + + const glm::mat4& Camera2D::getProjectionMatrix() const { + return mProjectionMatrix; + } + + glm::mat4 Camera2D::getViewProjectionMatrix() const { + if (mDirty) { + const_cast(this)->updateMatrices(); + } + return mProjectionMatrix * mViewMatrix; + } + + glm::vec2 Camera2D::screenToWorld(const glm::vec2& screenPos, uint32_t viewportWidth, uint32_t viewportHeight) const { + // Convert screen coordinates to normalized device coordinates + float ndcX = (2.0f * screenPos.x) / static_cast(viewportWidth) - 1.0f; + float ndcY = 1.0f - (2.0f * screenPos.y) / static_cast(viewportHeight); + + // Create NDC position with z=0 + glm::vec4 ndcPos(ndcX, ndcY, 0.0f, 1.0f); + + // Apply inverse view-projection matrix + glm::mat4 invVP = glm::inverse(getViewProjectionMatrix()); + glm::vec4 worldPos = invVP * ndcPos; + + return glm::vec2(worldPos.x, worldPos.y); + } + + glm::vec2 Camera2D::worldToScreen(const glm::vec2& worldPos, uint32_t viewportWidth, uint32_t viewportHeight) const { + // Apply view-projection matrix + glm::vec4 clipPos = getViewProjectionMatrix() * glm::vec4(worldPos.x, worldPos.y, 0.0f, 1.0f); + + // Convert from clip space to screen space + float screenX = ((clipPos.x / clipPos.w) + 1.0f) * 0.5f * static_cast(viewportWidth); + float screenY = (1.0f - (clipPos.y / clipPos.w)) * 0.5f * static_cast(viewportHeight); + + return glm::vec2(screenX, screenY); + } + + void Camera2D::move(const glm::vec2& offset) { + mPosition += offset; + mDirty = true; + } + + void Camera2D::resize(uint32_t width, uint32_t height) { + setOrthographicFromViewport(width, height); + } + + void Camera2D::setViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { + mViewport.x = x; + mViewport.y = y; + mViewport.width = width; + mViewport.height = height; + resize(width, height); + } + + const core::Rect& Camera2D::getViewport() const { + return mViewport; + } + + void Camera2D::updateMatrices() { + if (!mDirty) return; + + // Build view matrix: translate by position, rotate, scale by zoom + glm::mat4 transform = glm::mat4(1.0f); + + // Apply zoom (scale) + transform = glm::scale(transform, glm::vec3(mZoom, mZoom, 1.0f)); + + // Apply rotation + if (mRotation != 0.0f) { + transform = glm::rotate(transform, glm::radians(mRotation), glm::vec3(0.0f, 0.0f, 1.0f)); + } + + // Apply translation + transform = glm::translate(transform, glm::vec3(-mPosition.x, -mPosition.y, 0.0f)); + + mViewMatrix = transform; + mDirty = false; + } + + //--------------------------------------------------------------------------------------------- + // Layer2D implementation + //--------------------------------------------------------------------------------------------- + + Layer2D::Layer2D() : mVisible(true), mRenderOrder(0), mCamera(nullptr), mUseOwnCamera(false) { + // empty + } + + bool Layer2D::init(renderer::RHI& rhi) { + // Base layer initialization + return true; + } + + void Layer2D::shutdown() { + // Base layer shutdown + } + + void Layer2D::update(float deltaTime) { + // Base layer update + } + + void Layer2D::render(renderer::RHI& rhi) { + // Base layer render + } + + void Layer2D::setName(const std::string& name) { + mName = name; + } + + const std::string& Layer2D::getName() const { + return mName; + } + + void Layer2D::setVisible(bool visible) { + mVisible = visible; + } + + bool Layer2D::isVisible() const { + return mVisible; + } + + void Layer2D::setRenderOrder(int32_t order) { + mRenderOrder = order; + } + + int32_t Layer2D::getRenderOrder() const { + return mRenderOrder; + } + + void Layer2D::setCamera(Camera2D* camera) { + mCamera = camera; + } + + Camera2D* Layer2D::getCamera() const { + return mCamera; + } + + void Layer2D::setUseOwnCamera(bool useOwnCamera) { + mUseOwnCamera = useOwnCamera; + } + + bool Layer2D::getUseOwnCamera() const { + return mUseOwnCamera; + } + + //--------------------------------------------------------------------------------------------- + // Entity2D implementation + //--------------------------------------------------------------------------------------------- + + bool Entity2D::isValid() const { + return id != 0; + } + + bool Entity2D::operator==(const Entity2D& other) const { + return id == other.id; + } + + bool Entity2D::operator!=(const Entity2D& other) const { + return id != other.id; + } + + //--------------------------------------------------------------------------------------------- + // Transform2DComponent implementation + //--------------------------------------------------------------------------------------------- + + void Transform2DComponent::updateWorldMatrix() { + if (!mDirty) return; + + glm::mat4 transform = glm::mat4(1.0f); + + // Translate to position + transform = glm::translate(transform, glm::vec3(mPosition.x, mPosition.y, 0.0f)); + + // Rotate around origin + if (mRotation != 0.0f) { + transform = glm::translate(transform, glm::vec3(mSize.x * mOrigin.x, mSize.y * mOrigin.y, 0.0f)); + transform = glm::rotate(transform, glm::radians(mRotation), glm::vec3(0.0f, 0.0f, 1.0f)); + transform = glm::translate(transform, glm::vec3(-mSize.x * mOrigin.x, -mSize.y * mOrigin.y, 0.0f)); + } + + // Scale + transform = glm::scale(transform, glm::vec3(mScale.x, mScale.y, 1.0f)); + + mWorldMatrix = transform; + mDirty = false; + } + + void Transform2DComponent::setPosition(const glm::vec2& position) { + if (mPosition != position) { + mPosition = position; + mDirty = true; + } + } + + void Transform2DComponent::setRotation(float rotation) { + if (mRotation != rotation) { + mRotation = rotation; + mDirty = true; + } + } + + void Transform2DComponent::setScale(const glm::vec2& scale) { + if (mScale != scale) { + mScale = scale; + mDirty = true; + } + } + + void Transform2DComponent::setSize(const glm::vec2& size) { + if (mSize != size) { + mSize = size; + mDirty = true; + } + } + + void Transform2DComponent::setOrigin(const glm::vec2& origin) { + if (mOrigin != origin) { + mOrigin = origin; + mDirty = true; + } + } + + //--------------------------------------------------------------------------------------------- + // SpriteComponent implementation + //--------------------------------------------------------------------------------------------- + + SpriteComponent::SpriteComponent(core::guid textureHandle) : mTextureHandle(textureHandle) { + // empty + } + + void SpriteComponent::setTexture(core::guid textureHandle) { + mTextureHandle = textureHandle; + } + + void SpriteComponent::setColor(const glm::vec4& color) { + mColor = color; + } + + void SpriteComponent::setAlpha(float alpha) { + mAlpha = glm::clamp(alpha, 0.0f, 1.0f); + } + + void SpriteComponent::setTextureRect(const core::Rect& rect) { + mTextureRect = rect; + } + + void SpriteComponent::setFlip(bool flipX, bool flipY) { + mFlipX = flipX; + mFlipY = flipY; + } + + void SpriteComponent::setVisible(bool visible) { + mVisible = visible; + } + + //--------------------------------------------------------------------------------------------- + // TextComponent implementation + //--------------------------------------------------------------------------------------------- + + TextComponent::TextComponent(const std::string& text, core::guid fontHandle) + : mText(text), mFontHandle(fontHandle), mDirty(true) { + // empty + } + + void TextComponent::setText(const std::string& text) { + if (mText != text) { + mText = text; + mDirty = true; + } + } + + void TextComponent::setFont(core::guid fontHandle) { + if (mFontHandle != fontHandle) { + mFontHandle = fontHandle; + mDirty = true; + } + } + + void TextComponent::setColor(const glm::vec4& color) { + mColor = color; + } + + void TextComponent::setFontSize(float fontSize) { + if (mFontSize != fontSize) { + mFontSize = fontSize; + mDirty = true; + } + } + + void TextComponent::setHorizontalAlignment(TextAlignment alignment) { + mHorizontalAlignment = alignment; + mDirty = true; + } + + void TextComponent::setVerticalAlignment(VerticalAlignment alignment) { + mVerticalAlignment = alignment; + mDirty = true; + } + + void TextComponent::setVisible(bool visible) { + mVisible = visible; + } + + //--------------------------------------------------------------------------------------------- + // UISpriteComponent implementation + //--------------------------------------------------------------------------------------------- + + void UISpriteComponent::setTexture(core::guid textureHandle) { + mTextureHandle = textureHandle; + } + + void UISpriteComponent::setNineSliceBorders(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) { + mBorderLeft = left; + mBorderRight = right; + mBorderTop = top; + mBorderBottom = bottom; + } + + void UISpriteComponent::setVisible(bool visible) { + mVisible = visible; + } + + void UISpriteComponent::setInteractive(bool interactive) { + mInteractive = interactive; + } + + void UISpriteComponent::setEnabled(bool enabled) { + mEnabled = enabled; + } + + void UISpriteComponent::setOnClick(std::function callback) { + mOnClick = callback; + } + + void UISpriteComponent::setOnHoverEnter(std::function callback) { + mOnHoverEnter = callback; + } + + void UISpriteComponent::setOnHoverExit(std::function callback) { + mOnHoverExit = callback; + } + + void UISpriteComponent::onClick() { + if (mEnabled && mInteractive && mOnClick) { + mPressed = true; + mOnClick(); + mPressed = false; + } + } + + void UISpriteComponent::onHoverEnter() { + if (mEnabled && mInteractive && mOnHoverEnter) { + mHovered = true; + mOnHoverEnter(); + } + } + + void UISpriteComponent::onHoverExit() { + if (mEnabled && mInteractive && mOnHoverExit) { + mHovered = false; + mOnHoverExit(); + } + } + + //--------------------------------------------------------------------------------------------- + // Render2DComponent implementation + //--------------------------------------------------------------------------------------------- + + void Render2DComponent::setLayer(Layer2D* layer) { + mLayer = layer; + } + + void Render2DComponent::setRenderOrder(int32_t order) { + mRenderOrder = order; + } + + void Render2DComponent::setVisible(bool visible) { + mVisible = visible; + } + + //--------------------------------------------------------------------------------------------- + // Animation2DComponent implementation + //--------------------------------------------------------------------------------------------- + + void Animation2DComponent::addFrame(const core::Rect& textureRect, float duration) { + mFrames.push_back({ textureRect, duration }); + mDirty = true; + } + + void Animation2DComponent::setFrames(const std::vector& frames) { + mFrames = frames; + mDirty = true; + } + + void Animation2DComponent::play(bool loop) { + mPlaying = true; + mLoop = loop; + } + + void Animation2DComponent::stop() { + mPlaying = false; + mCurrentFrame = 0; + mFrameTimer = 0.0f; + } + + void Animation2DComponent::pause() { + mPlaying = false; + } + + void Animation2DComponent::resume() { + mPlaying = true; + } + + void Animation2DComponent::update(float deltaTime) { + if (!mPlaying || mFrames.empty()) return; + + mFrameTimer += deltaTime * mAnimationSpeed; + + const Frame& currentFrame = mFrames[mCurrentFrame]; + while (mFrameTimer >= currentFrame.mDuration) { + mFrameTimer -= currentFrame.mDuration; + mCurrentFrame++; + + if (mCurrentFrame >= mFrames.size()) { + if (mLoop) { + mCurrentFrame = 0; + } else { + mPlaying = false; + mCurrentFrame = mFrames.size() - 1; + break; + } + } + } + + mDirty = true; + } + + core::Rect Animation2DComponent::getCurrentFrameRect() const { + if (mFrames.empty()) return core::Rect(); + return mFrames[mCurrentFrame].mTextureRect; + } + + uint32_t Animation2DComponent::getCurrentFrameIndex() const { + return mCurrentFrame; + } + + void Animation2DComponent::setCurrentFrameIndex(uint32_t frameIndex) { + if (frameIndex < mFrames.size()) { + mCurrentFrame = frameIndex; + mFrameTimer = 0.0f; + mDirty = true; + } + } + + void Animation2DComponent::setAnimationSpeed(float speed) { + mAnimationSpeed = std::max(0.0f, speed); + } + + bool Animation2DComponent::isPlaying() const { + return mPlaying; + } + + bool Animation2DComponent::isLooping() const { + return mLoop; + } + + uint32_t Animation2DComponent::getFrameCount() const { + return static_cast(mFrames.size()); + } + + //--------------------------------------------------------------------------------------------- + // Scene2D implementation + //--------------------------------------------------------------------------------------------- + + Scene2D::Scene2D() : mViewportWidth(0), mViewportHeight(0), mManager(nullptr) { + mName = "UnnamedScene"; + mDefaultLayer = std::make_unique(); + mDefaultLayer->setName("Default"); + mDefaultLayer->init(*static_cast(nullptr)); // Will be properly initialized later + } + + Scene2D::~Scene2D() { + shutdown(); + } + + bool Scene2D::init(renderer::RHI& rhi) { + // Initialize main camera + mMainCamera.resize(mViewportWidth, mViewportHeight); + + // Initialize default layer + if (mDefaultLayer) { + mDefaultLayer->init(rhi); + } + + // Initialize all other layers + for (auto& pair : mLayers) { + pair.second->init(rhi); + } + + onLoad(); + return true; + } + + void Scene2D::shutdown() { + onUnload(); + + // Shutdown all layers + for (auto& pair : mLayers) { + pair.second->shutdown(); + } + mLayers.clear(); + + if (mDefaultLayer) { + mDefaultLayer->shutdown(); + } + } + + void Scene2D::update(float deltaTime) { + // Update all components that need updating + for (auto& component : mAnimationPool.data()) { + if (component.isPlaying()) { + component.update(deltaTime); + } + } + + // Update all layers + for (auto& pair : mLayers) { + if (pair.second->isVisible()) { + pair.second->update(deltaTime); + } + } + } + + void Scene2D::render(renderer::RHI& rhi) { + // Update transform matrices for all entities + for (auto& component : mTransformPool.data()) { + if (component.mDirty) { + component.updateWorldMatrix(); + } + } + + // Render all layers in order + std::vector renderOrder; + + // Collect all layers + for (auto& pair : mLayers) { + renderOrder.push_back(pair.second.get()); + } + + // Sort by render order + std::sort(renderOrder.begin(), renderOrder.end(), [](Layer2D* a, Layer2D* b) { + return a->getRenderOrder() < b->getRenderOrder(); + }); + + // Render default layer first + if (mDefaultLayer && mDefaultLayer->isVisible()) { + mDefaultLayer->render(rhi); + } + + // Render sorted layers + for (Layer2D* layer : renderOrder) { + if (layer->isVisible()) { + layer->render(rhi); + } + } + } + + void Scene2D::onActivate() { + // Override in derived classes + } + + void Scene2D::onDeactivate() { + // Override in derived classes + } + + void Scene2D::onLoad() { + // Override in derived classes + } + + void Scene2D::onUnload() { + // Override in derived classes + } + + Entity2D Scene2D::createEntity() { + Entity2D entity; + entity.id = mNextEntityId++; + return entity; + } + + void Scene2D::destroyEntity(Entity2D entity) { + if (!isValid(entity)) return; + + mTransformPool.remove(entity); + mSpritePool.remove(entity); + mTextPool.remove(entity); + mUISpritePool.remove(entity); + mRenderPool.remove(entity); + mAnimationPool.remove(entity); + } + + Transform2DComponent& Scene2D::addTransformComponent(Entity2D entity) { + return mTransformPool.add(entity); + } + + SpriteComponent& Scene2D::addSpriteComponent(Entity2D entity) { + return mSpritePool.add(entity); + } + + TextComponent& Scene2D::addTextComponent(Entity2D entity) { + return mTextPool.add(entity); + } + + UISpriteComponent& Scene2D::addUISpriteComponent(Entity2D entity) { + return mUISpritePool.add(entity); + } + + Render2DComponent& Scene2D::addRenderComponent(Entity2D entity) { + Render2DComponent& component = mRenderPool.add(entity); + component.setLayer(mDefaultLayer.get()); + return component; + } + + Animation2DComponent& Scene2D::addAnimationComponent(Entity2D entity) { + return mAnimationPool.add(entity); + } + + Transform2DComponent* Scene2D::getTransformComponent(Entity2D entity) { + return mTransformPool.get(entity); + } + + SpriteComponent* Scene2D::getSpriteComponent(Entity2D entity) { + return mSpritePool.get(entity); + } + + TextComponent* Scene2D::getTextComponent(Entity2D entity) { + return mTextPool.get(entity); + } + + UISpriteComponent* Scene2D::getUISpriteComponent(Entity2D entity) { + return mUISpritePool.get(entity); + } + + Render2DComponent* Scene2D::getRenderComponent(Entity2D entity) { + return mRenderPool.get(entity); + } + + Animation2DComponent* Scene2D::getAnimationComponent(Entity2D entity) { + return mAnimationPool.get(entity); + } + + Layer2D* Scene2D::createLayer(const std::string& name) { + if (mLayers.find(name) != mLayers.end()) { + return nullptr; // Layer already exists + } + + auto layer = std::make_unique(); + layer->setName(name); + Layer2D* result = layer.get(); + mLayers[name] = std::move(layer); + return result; + } + + bool Scene2D::destroyLayer(const std::string& name) { + auto it = mLayers.find(name); + if (it != mLayers.end()) { + it->second->shutdown(); + mLayers.erase(it); + return true; + } + return false; + } + + Layer2D* Scene2D::getLayer(const std::string& name) const { + auto it = mLayers.find(name); + if (it != mLayers.end()) { + return it->second.get(); + } + return nullptr; + } + + Layer2D* Scene2D::getDefaultLayer() const { + return mDefaultLayer.get(); + } + + Camera2D& Scene2D::getMainCamera() { + return mMainCamera; + } + + const Camera2D& Scene2D::getMainCamera() const { + return mMainCamera; + } + + void Scene2D::setViewportSize(uint32_t width, uint32_t height) { + mViewportWidth = width; + mViewportHeight = height; + mMainCamera.resize(width, height); + } + + uint32_t Scene2D::getViewportWidth() const { + return mViewportWidth; + } + + uint32_t Scene2D::getViewportHeight() const { + return mViewportHeight; + } + + bool Scene2D::isValid(Entity2D entity) const { + return entity.id != 0; + } + + const std::string& Scene2D::getName() const { + return mName; + } + + void Scene2D::setName(const std::string& name) { + mName = name; + } + + Scene2DManager* Scene2D::getManager() const { + return mManager; + } + + //--------------------------------------------------------------------------------------------- + // SimpleScene2D implementation + //--------------------------------------------------------------------------------------------- + + SimpleScene2D::SimpleScene2D() { + // empty + } + + bool SimpleScene2D::init(renderer::RHI& rhi) { + if (!Scene2D::init(rhi)) { + return false; + } + return true; + } + + Entity2D SimpleScene2D::createSprite(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size) { + Entity2D entity = createEntity(); + + Transform2DComponent& transform = addTransformComponent(entity); + transform.setPosition(position); + transform.setSize(size); + + SpriteComponent& sprite = addSpriteComponent(entity); + sprite.setTexture(textureHandle); + + Render2DComponent& render = addRenderComponent(entity); + render.setVisible(true); + + return entity; + } + + Entity2D SimpleScene2D::createText(const std::string& text, core::guid fontHandle, const glm::vec2& position, float fontSize) { + Entity2D entity = createEntity(); + + Transform2DComponent& transform = addTransformComponent(entity); + transform.setPosition(position); + + TextComponent& textComponent = addTextComponent(entity); + textComponent.setText(text); + textComponent.setFont(fontHandle); + textComponent.setFontSize(fontSize); + + Render2DComponent& render = addRenderComponent(entity); + render.setVisible(true); + + return entity; + } + + Entity2D SimpleScene2D::createButton(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size, std::function onClick) { + Entity2D entity = createEntity(); + + Transform2DComponent& transform = addTransformComponent(entity); + transform.setPosition(position); + transform.setSize(size); + + UISpriteComponent& uiSprite = addUISpriteComponent(entity); + uiSprite.setTexture(textureHandle); + uiSprite.setInteractive(true); + uiSprite.setOnClick(onClick); + + Render2DComponent& render = addRenderComponent(entity); + render.setVisible(true); + + return entity; + } + + //--------------------------------------------------------------------------------------------- + // UIScene2D implementation + //--------------------------------------------------------------------------------------------- + + UIScene2D::UIScene2D() { + // empty + } + + bool UIScene2D::init(renderer::RHI& rhi) { + if (!Scene2D::init(rhi)) { + return false; + } + return true; + } + + void UIScene2D::update(float deltaTime) { + Scene2D::update(deltaTime); + // Additional UI-specific updates + } + + void UIScene2D::onMouseMove(const glm::vec2& position) { + // Handle mouse move for UI elements + // This would typically check for hover states + } + + void UIScene2D::onMouseDown(const glm::vec2& position, uint8_t button) { + // Handle mouse down for UI elements + } + + void UIScene2D::onMouseUp(const glm::vec2& position, uint8_t button) { + // Handle mouse up for UI elements + } + + Entity2D UIScene2D::createPanel(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size) { + Entity2D entity = createEntity(); + + Transform2DComponent& transform = addTransformComponent(entity); + transform.setPosition(position); + transform.setSize(size); + + SpriteComponent& sprite = addSpriteComponent(entity); + sprite.setTexture(textureHandle); + + Render2DComponent& render = addRenderComponent(entity); + render.setVisible(true); + + return entity; + } + + Entity2D UIScene2D::createButton(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size, std::function onClick) { + Entity2D entity = createEntity(); + + Transform2DComponent& transform = addTransformComponent(entity); + transform.setPosition(position); + transform.setSize(size); + + UISpriteComponent& uiSprite = addUISpriteComponent(entity); + uiSprite.setTexture(textureHandle); + uiSprite.setInteractive(true); + uiSprite.setEnabled(true); + uiSprite.setOnClick(onClick); + + Render2DComponent& render = addRenderComponent(entity); + render.setVisible(true); + + return entity; + } + + Entity2D UIScene2D::createText(const std::string& text, core::guid fontHandle, const glm::vec2& position, float fontSize, const glm::vec4& color) { + Entity2D entity = createEntity(); + + Transform2DComponent& transform = addTransformComponent(entity); + transform.setPosition(position); + + TextComponent& textComponent = addTextComponent(entity); + textComponent.setText(text); + textComponent.setFont(fontHandle); + textComponent.setFontSize(fontSize); + textComponent.setColor(color); + + Render2DComponent& render = addRenderComponent(entity); + render.setVisible(true); + + return entity; + } + + Entity2D UIScene2D::createCheckbox(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size, std::function onChanged) { + Entity2D entity = createEntity(); + + Transform2DComponent& transform = addTransformComponent(entity); + transform.setPosition(position); + transform.setSize(size); + + UISpriteComponent& uiSprite = addUISpriteComponent(entity); + uiSprite.setTexture(textureHandle); + uiSprite.setInteractive(true); + uiSprite.setEnabled(true); + // For checkbox, we'll use the selected state to represent checked/unchecked + + Render2DComponent& render = addRenderComponent(entity); + render.setVisible(true); + + return entity; + } + + Entity2D UIScene2D::createSlider(core::guid backgroundTexture, core::guid handleTexture, const glm::vec2& position, const glm::vec2& size, float minValue, float maxValue, std::function onChanged) { + Entity2D entity = createEntity(); + + Transform2DComponent& transform = addTransformComponent(entity); + transform.setPosition(position); + transform.setSize(size); + + SpriteComponent& background = addSpriteComponent(entity); + background.setTexture(backgroundTexture); + + Render2DComponent& render = addRenderComponent(entity); + render.setVisible(true); + + return entity; + } + +} // namespace segfault::scene diff --git a/src/runtime/scene/Scene2D.h b/src/runtime/scene/Scene2D.h new file mode 100644 index 0000000..7cd1ab4 --- /dev/null +++ b/src/runtime/scene/Scene2D.h @@ -0,0 +1,1052 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2015-2026 Segfault by Kim Kulling + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-----------------------------------------------------------------------------------------------*/ +#pragma once + +#include "core/segfault.h" +#include "core/ifilemanager.h" +#include "renderer/RHI.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace segfault::scene { + + // Forward declarations + class Scene2DManager; + class Layer2D; + + //--------------------------------------------------------------------------------------------- + /// @brief 2D Camera specifically designed for orthographic projection. + /// + /// Supports zooming, panning, and viewport operations for 2D rendering. + //--------------------------------------------------------------------------------------------- + class SEGFAULT_EXPORT Camera2D final { + public: + /// @brief Constructs a new Camera2D with default orthographic projection. + Camera2D(); + + /// @brief Destroys the Camera2D. + ~Camera2D() = default; + + //----------------------------------------------------------------------------------------- + /// @brief Sets up an orthographic projection. + /// @param[ in ] left The left coordinate of the viewport. + /// @param[ in ] right The right coordinate of the viewport. + /// @param[ in ] bottom The bottom coordinate of the viewport. + /// @param[ in ] top The top coordinate of the viewport. + /// @param[ in ] near The near clipping plane. + /// @param[ in ] far The far clipping plane. + //----------------------------------------------------------------------------------------- + void setOrthographic(float left, float right, float bottom, float top, float near = -1.0f, float far = 1.0f); + + //----------------------------------------------------------------------------------------- + /// @brief Sets up an orthographic projection based on viewport size. + /// @param[ in ] width The width of the viewport. + /// @param[ in ] height The height of the viewport. + /// @param[ in ] near The near clipping plane. + /// @param[ in ] far The far clipping plane. + //----------------------------------------------------------------------------------------- + void setOrthographicFromViewport(uint32_t width, uint32_t height, float near = -1.0f, float far = 1.0f); + + //----------------------------------------------------------------------------------------- + /// @brief Sets the position of the camera. + /// @param[ in ] position The 2D position of the camera. + //----------------------------------------------------------------------------------------- + void setPosition(const glm::vec2& position); + + //----------------------------------------------------------------------------------------- + /// @brief Sets the position of the camera. + /// @param[ in ] x The x-coordinate of the camera position. + /// @param[ in ] y The y-coordinate of the camera position. + //----------------------------------------------------------------------------------------- + void setPosition(float x, float y); + + //----------------------------------------------------------------------------------------- + /// @brief Gets the position of the camera. + /// @return The 2D position of the camera. + //----------------------------------------------------------------------------------------- + const glm::vec2& getPosition() const; + + //----------------------------------------------------------------------------------------- + /// @brief Sets the zoom level of the camera. + /// @param[ in ] zoom The zoom level (1.0 = normal, >1.0 = zoomed in, <1.0 = zoomed out). + //----------------------------------------------------------------------------------------- + void setZoom(float zoom); + + //----------------------------------------------------------------------------------------- + /// @brief Gets the zoom level of the camera. + /// @return The current zoom level. + //----------------------------------------------------------------------------------------- + float getZoom() const; + + //----------------------------------------------------------------------------------------- + /// @brief Sets the rotation of the camera in degrees. + /// @param[ in ] angle The rotation angle in degrees. + //----------------------------------------------------------------------------------------- + void setRotation(float angle); + + //----------------------------------------------------------------------------------------- + /// @brief Gets the rotation of the camera in degrees. + /// @return The current rotation angle in degrees. + //----------------------------------------------------------------------------------------- + float getRotation() const; + + //----------------------------------------------------------------------------------------- + /// @brief Gets the view matrix. + /// @return The view matrix. + //----------------------------------------------------------------------------------------- + const glm::mat4& getViewMatrix() const; + + //----------------------------------------------------------------------------------------- + /// @brief Gets the projection matrix. + /// @return The projection matrix. + //----------------------------------------------------------------------------------------- + const glm::mat4& getProjectionMatrix() const; + + //----------------------------------------------------------------------------------------- + /// @brief Gets the combined view-projection matrix. + /// @return The view-projection matrix. + //----------------------------------------------------------------------------------------- + glm::mat4 getViewProjectionMatrix() const; + + //----------------------------------------------------------------------------------------- + /// @brief Converts screen coordinates to world coordinates. + /// @param[ in ] screenPos The screen position (in pixels). + /// @param[ in ] viewportWidth The width of the viewport in pixels. + /// @param[ in ] viewportHeight The height of the viewport in pixels. + /// @return The corresponding world position. + //----------------------------------------------------------------------------------------- + glm::vec2 screenToWorld(const glm::vec2& screenPos, uint32_t viewportWidth, uint32_t viewportHeight) const; + + //----------------------------------------------------------------------------------------- + /// @brief Converts world coordinates to screen coordinates. + /// @param[ in ] worldPos The world position. + /// @param[ in ] viewportWidth The width of the viewport in pixels. + /// @param[ in ] viewportHeight The height of the viewport in pixels. + /// @return The corresponding screen position (in pixels). + //----------------------------------------------------------------------------------------- + glm::vec2 worldToScreen(const glm::vec2& worldPos, uint32_t viewportWidth, uint32_t viewportHeight) const; + + //----------------------------------------------------------------------------------------- + /// @brief Moves the camera by the specified offset. + /// @param[ in ] offset The offset to move the camera by. + //----------------------------------------------------------------------------------------- + void move(const glm::vec2& offset); + + //----------------------------------------------------------------------------------------- + /// @brief Resizes the camera viewport. + /// @param[ in ] width The new width of the viewport. + /// @param[ in ] height The new height of the viewport. + //----------------------------------------------------------------------------------------- + void resize(uint32_t width, uint32_t height); + + //----------------------------------------------------------------------------------------- + /// @brief Sets the viewport boundaries. + /// @param[ in ] x The x-coordinate of the viewport. + /// @param[ in ] y The y-coordinate of the viewport. + /// @param[ in ] width The width of the viewport. + /// @param[ in ] height The height of the viewport. + //----------------------------------------------------------------------------------------- + void setViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height); + + //----------------------------------------------------------------------------------------- + /// @brief Gets the viewport rectangle. + /// @return The viewport rectangle. + //----------------------------------------------------------------------------------------- + const core::Rect& getViewport() const; + + private: + glm::vec2 mPosition{ 0.0f, 0.0f }; + float mZoom{ 1.0f }; + float mRotation{ 0.0f }; + glm::mat4 mViewMatrix{ 1.0f }; + glm::mat4 mProjectionMatrix{ 1.0f }; + core::Rect mViewport{ 0, 0, 0, 0 }; + + bool mDirty{ true }; + + void updateMatrices(); + }; + + //--------------------------------------------------------------------------------------------- + /// @brief Base class for all 2D layers. + /// + /// Layers are used to organize 2D rendering into separate groups with their own + /// rendering order, camera, and visibility settings. + //--------------------------------------------------------------------------------------------- + class SEGFAULT_EXPORT Layer2D { + public: + /// @brief Constructs a new Layer2D. + Layer2D(); + + /// @brief Destroys the Layer2D. + virtual ~Layer2D() = default; + + /// @brief Initializes the layer. + /// @param[ in ] rhi The rendering hardware interface. + /// @return True if initialization was successful, false otherwise. + virtual bool init(renderer::RHI& rhi); + + /// @brief Shuts down the layer and releases resources. + virtual void shutdown(); + + /// @brief Updates the layer. + /// @param[ in ] deltaTime The time elapsed since the last frame. + virtual void update(float deltaTime); + + /// @brief Renders the layer. + /// @param[ in ] rhi The rendering hardware interface. + virtual void render(renderer::RHI& rhi); + + //----------------------------------------------------------------------------------------- + /// @brief Sets the name of the layer. + /// @param[ in ] name The name of the layer. + //----------------------------------------------------------------------------------------- + void setName(const std::string& name); + + //----------------------------------------------------------------------------------------- + /// @brief Gets the name of the layer. + /// @return The name of the layer. + //----------------------------------------------------------------------------------------- + const std::string& getName() const; + + //----------------------------------------------------------------------------------------- + /// @brief Sets the visibility of the layer. + /// @param[ in ] visible True to make the layer visible, false to hide it. + //----------------------------------------------------------------------------------------- + void setVisible(bool visible); + + //----------------------------------------------------------------------------------------- + /// @brief Gets the visibility of the layer. + /// @return True if the layer is visible, false otherwise. + //----------------------------------------------------------------------------------------- + bool isVisible() const; + + //----------------------------------------------------------------------------------------- + /// @brief Sets the rendering order of the layer (lower values render first). + /// @param[ in ] order The rendering order. + //----------------------------------------------------------------------------------------- + void setRenderOrder(int32_t order); + + //----------------------------------------------------------------------------------------- + /// @brief Gets the rendering order of the layer. + /// @return The rendering order. + //----------------------------------------------------------------------------------------- + int32_t getRenderOrder() const; + + //----------------------------------------------------------------------------------------- + /// @brief Sets the camera for this layer. + /// @param[ in ] camera The camera to use for rendering this layer. + //----------------------------------------------------------------------------------------- + void setCamera(Camera2D* camera); + + //----------------------------------------------------------------------------------------- + /// @brief Gets the camera for this layer. + /// @return Pointer to the camera, or nullptr if using the default camera. + //----------------------------------------------------------------------------------------- + Camera2D* getCamera() const; + + //----------------------------------------------------------------------------------------- + /// @brief Sets whether this layer uses its own camera or the scene's default camera. + /// @param[ in ] useOwnCamera True to use the layer's own camera, false to use the scene's camera. + //----------------------------------------------------------------------------------------- + void setUseOwnCamera(bool useOwnCamera); + + //----------------------------------------------------------------------------------------- + /// @brief Gets whether this layer uses its own camera. + /// @return True if using own camera, false otherwise. + //----------------------------------------------------------------------------------------- + bool getUseOwnCamera() const; + + protected: + std::string mName; + bool mVisible{ true }; + int32_t mRenderOrder{ 0 }; + Camera2D* mCamera{ nullptr }; + bool mUseOwnCamera{ false }; + }; + + //--------------------------------------------------------------------------------------------- + /// @brief Entity handle for 2D scene objects. + //--------------------------------------------------------------------------------------------- + struct SEGFAULT_EXPORT Entity2D { + core::guid id{ 0 }; + + /// @brief Checks if the entity is valid. + /// @return True if the entity is valid, false otherwise. + bool isValid() const; + + /// @brief Comparison operator. + /// @param[ in ] other The other entity to compare with. + /// @return True if the entities are equal, false otherwise. + bool operator==(const Entity2D& other) const; + + /// @brief Comparison operator. + /// @param[ in ] other The other entity to compare with. + /// @return True if the entities are not equal, false otherwise. + bool operator!=(const Entity2D& other) const; + }; + + //--------------------------------------------------------------------------------------------- + /// @brief Transform component for 2D entities. + /// + /// Stores position, rotation, scale, and other transform properties for 2D rendering. + //--------------------------------------------------------------------------------------------- + struct SEGFAULT_EXPORT Transform2DComponent final { + glm::vec2 mPosition{ 0.0f, 0.0f }; + float mRotation{ 0.0f }; // in degrees + glm::vec2 mScale{ 1.0f, 1.0f }; + glm::vec2 mOrigin{ 0.0f, 0.0f }; // pivot point (0-1 normalized) + glm::vec2 mSize{ 1.0f, 1.0f }; + glm::mat4 mWorldMatrix{ 1.0f }; + bool mDirty{ true }; + + /// @brief Updates the world matrix based on current transform properties. + void updateWorldMatrix(); + + /// @brief Sets the position. + /// @param[ in ] position The new position. + void setPosition(const glm::vec2& position); + + /// @brief Sets the rotation. + /// @param[ in ] rotation The new rotation in degrees. + void setRotation(float rotation); + + /// @brief Sets the scale. + /// @param[ in ] scale The new scale. + void setScale(const glm::vec2& scale); + + /// @brief Sets the size. + /// @param[ in ] size The new size. + void setSize(const glm::vec2& size); + + /// @brief Sets the origin/pivot point (normalized 0-1). + /// @param[ in ] origin The new origin. + void setOrigin(const glm::vec2& origin); + }; + + //--------------------------------------------------------------------------------------------- + /// @brief Sprite component for 2D rendering. + /// + /// Represents a textured quad that can be rendered with a texture. + //--------------------------------------------------------------------------------------------- + struct SEGFAULT_EXPORT SpriteComponent final { + core::guid mTextureHandle{ 0 }; + core::Rect mTextureRect{ 0, 0, 0, 0 }; + glm::vec4 mColor{ 1.0f, 1.0f, 1.0f, 1.0f }; + bool mFlipX{ false }; + bool mFlipY{ false }; + float mAlpha{ 1.0f }; + bool mVisible{ true }; + + /// @brief Default constructor. + SpriteComponent() = default; + + /// @brief Constructs a sprite component with a texture. + /// @param[ in ] textureHandle The handle to the texture. + SpriteComponent(core::guid textureHandle); + + /// @brief Sets the texture handle. + /// @param[ in ] textureHandle The handle to the texture. + void setTexture(core::guid textureHandle); + + /// @brief Sets the color. + /// @param[ in ] color The color to apply to the sprite. + void setColor(const glm::vec4& color); + + /// @brief Sets the alpha value. + /// @param[ in ] alpha The alpha value (0.0 - 1.0). + void setAlpha(float alpha); + + /// @brief Sets the texture rectangle (for sprite sheets). + /// @param[ in ] rect The texture rectangle. + void setTextureRect(const core::Rect& rect); + + /// @brief Sets the flip flags. + /// @param[ in ] flipX True to flip horizontally. + /// @param[ in ] flipY True to flip vertically. + void setFlip(bool flipX, bool flipY); + + /// @brief Sets visibility. + /// @param[ in ] visible True to make visible, false to hide. + void setVisible(bool visible); + }; + + //--------------------------------------------------------------------------------------------- + /// @brief Text component for 2D rendering. + /// + /// Represents text that can be rendered using a font. + //--------------------------------------------------------------------------------------------- + struct SEGFAULT_EXPORT TextComponent final { + std::string mText; + core::guid mFontHandle{ 0 }; + glm::vec4 mColor{ 1.0f, 1.0f, 1.0f, 1.0f }; + float mFontSize{ 16.0f }; + float mLineSpacing{ 1.0f }; + float mLetterSpacing{ 0.0f }; + bool mVisible{ true }; + bool mDirty{ true }; + + // Text alignment + enum class TextAlignment { + Left, + Center, + Right + }; + + enum class VerticalAlignment { + Top, + Middle, + Bottom + }; + + TextAlignment mHorizontalAlignment{ TextAlignment::Left }; + VerticalAlignment mVerticalAlignment{ VerticalAlignment::Top }; + + /// @brief Default constructor. + TextComponent() = default; + + /// @brief Constructs a text component with text and font. + /// @param[ in ] text The text to display. + /// @param[ in ] fontHandle The handle to the font. + TextComponent(const std::string& text, core::guid fontHandle); + + /// @brief Sets the text. + /// @param[ in ] text The text to display. + void setText(const std::string& text); + + /// @brief Sets the font handle. + /// @param[ in ] fontHandle The handle to the font. + void setFont(core::guid fontHandle); + + /// @brief Sets the color. + /// @param[ in ] color The color to apply to the text. + void setColor(const glm::vec4& color); + + /// @brief Sets the font size. + /// @param[ in ] fontSize The font size in points. + void setFontSize(float fontSize); + + /// @brief Sets the horizontal alignment. + /// @param[ in ] alignment The horizontal alignment. + void setHorizontalAlignment(TextAlignment alignment); + + /// @brief Sets the vertical alignment. + /// @param[ in ] alignment The vertical alignment. + void setVerticalAlignment(VerticalAlignment alignment); + + /// @brief Sets visibility. + /// @param[ in ] visible True to make visible, false to hide. + void setVisible(bool visible); + }; + + //--------------------------------------------------------------------------------------------- + /// @brief UI Sprite component for 2D user interface elements. + /// + /// Extended sprite component with UI-specific features like nine-slice scaling. + //--------------------------------------------------------------------------------------------- + struct SEGFAULT_EXPORT UISpriteComponent final { + core::guid mTextureHandle{ 0 }; + core::Rect mTextureRect{ 0, 0, 0, 0 }; + glm::vec4 mColor{ 1.0f, 1.0f, 1.0f, 1.0f }; + float mAlpha{ 1.0f }; + bool mVisible{ true }; + + // Nine-slice border settings + uint32_t mBorderLeft{ 0 }; + uint32_t mBorderRight{ 0 }; + uint32_t mBorderTop{ 0 }; + uint32_t mBorderBottom{ 0 }; + + // UI-specific settings + bool mInteractive{ false }; + bool mEnabled{ true }; + bool mSelected{ false }; + bool mPressed{ false }; + bool mHovered{ false }; + + // Callback functions for UI events + std::function mOnClick; + std::function mOnHoverEnter; + std::function mOnHoverExit; + + /// @brief Default constructor. + UISpriteComponent() = default; + + /// @brief Sets the texture handle. + /// @param[ in ] textureHandle The handle to the texture. + void setTexture(core::guid textureHandle); + + /// @brief Sets the nine-slice borders. + /// @param[ in ] left The left border width. + /// @param[ in ] right The right border width. + /// @param[ in ] top The top border height. + /// @param[ in ] bottom The bottom border height. + void setNineSliceBorders(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom); + + /// @brief Sets visibility. + /// @param[ in ] visible True to make visible, false to hide. + void setVisible(bool visible); + + /// @brief Sets interactiveness. + /// @param[ in ] interactive True to enable interaction, false to disable. + void setInteractive(bool interactive); + + /// @brief Sets enabled state. + /// @param[ in ] enabled True to enable, false to disable. + void setEnabled(bool enabled); + + /// @brief Sets the click callback. + /// @param[ in ] callback The function to call when clicked. + void setOnClick(std::function callback); + + /// @brief Sets the hover enter callback. + /// @param[ in ] callback The function to call when hovered over. + void setOnHoverEnter(std::function callback); + + /// @brief Sets the hover exit callback. + /// @param[ in ] callback The function to call when hover exits. + void setOnHoverExit(std::function callback); + + /// @brief Simulates a click event. + void onClick(); + + /// @brief Simulates a hover enter event. + void onHoverEnter(); + + /// @brief Simulates a hover exit event. + void onHoverExit(); + }; + + //--------------------------------------------------------------------------------------------- + /// @brief Render component for 2D entities. + /// + /// Contains render-specific data like layer assignment and render order. + //--------------------------------------------------------------------------------------------- + struct SEGFAULT_EXPORT Render2DComponent final { + Layer2D* mLayer{ nullptr }; + int32_t mRenderOrder{ 0 }; + bool mVisible{ true }; + bool mCastShadows{ false }; + bool mReceiveShadows{ false }; + + /// @brief Default constructor. + Render2DComponent() = default; + + /// @brief Sets the layer. + /// @param[ in ] layer The layer this entity belongs to. + void setLayer(Layer2D* layer); + + /// @brief Sets the render order within the layer. + /// @param[ in ] order The render order (lower values render first). + void setRenderOrder(int32_t order); + + /// @brief Sets visibility. + /// @param[ in ] visible True to make visible, false to hide. + void setVisible(bool visible); + }; + + //--------------------------------------------------------------------------------------------- + /// @brief Animation component for 2D entities. + /// + /// Supports frame-based animation for sprites. + //--------------------------------------------------------------------------------------------- + struct SEGFAULT_EXPORT Animation2DComponent final { + struct Frame final { + core::Rect mTextureRect; + float mDuration{ 0.1f }; // in seconds + }; + + std::vector mFrames; + uint32_t mCurrentFrame{ 0 }; + float mFrameTimer{ 0.0f }; + float mAnimationSpeed{ 1.0f }; + bool mPlaying{ false }; + bool mLoop{ true }; + bool mDirty{ true }; + + /// @brief Default constructor. + Animation2DComponent() = default; + + /// @brief Adds a frame to the animation. + /// @param[ in ] textureRect The texture rectangle for this frame. + /// @param[ in ] duration The duration of this frame in seconds. + void addFrame(const core::Rect& textureRect, float duration = 0.1f); + + /// @brief Sets the animation frames. + /// @param[ in ] frames The array of frames. + void setFrames(const std::vector& frames); + + /// @brief Plays the animation. + /// @param[ in ] loop True to loop the animation, false to play once. + void play(bool loop = true); + + /// @brief Stops the animation. + void stop(); + + /// @brief Pauses the animation. + void pause(); + + /// @brief Resumes the animation. + void resume(); + + /// @brief Updates the animation. + /// @param[ in ] deltaTime The time elapsed since the last frame. + void update(float deltaTime); + + /// @brief Gets the current frame's texture rectangle. + /// @return The texture rectangle of the current frame. + core::Rect getCurrentFrameRect() const; + + /// @brief Gets the current frame index. + /// @return The current frame index. + uint32_t getCurrentFrameIndex() const; + + /// @brief Sets the current frame index. + /// @param[ in ] frameIndex The frame index to set. + void setCurrentFrameIndex(uint32_t frameIndex); + + /// @brief Sets the animation speed. + /// @param[ in ] speed The animation speed multiplier. + void setAnimationSpeed(float speed); + + /// @brief Checks if the animation is playing. + /// @return True if playing, false otherwise. + bool isPlaying() const; + + /// @brief Checks if the animation is looped. + /// @return True if looped, false otherwise. + bool isLooping() const; + + /// @brief Gets the number of frames. + /// @return The number of frames. + uint32_t getFrameCount() const; + }; + + //--------------------------------------------------------------------------------------------- + /// @brief Base class for all 2D scenes. + /// + /// Provides the foundation for creating custom 2D scenes with entities, layers, and rendering. + //--------------------------------------------------------------------------------------------- + class SEGFAULT_EXPORT Scene2D { + public: + /// @brief Constructs a new Scene2D. + Scene2D(); + + /// @brief Destroys the Scene2D. + virtual ~Scene2D(); + + /// @brief Initializes the scene. + /// @param[ in ] rhi The rendering hardware interface. + /// @return True if initialization was successful, false otherwise. + virtual bool init(renderer::RHI& rhi); + + /// @brief Shuts down the scene and releases all resources. + virtual void shutdown(); + + /// @brief Updates the scene. + /// @param[ in ] deltaTime The time elapsed since the last frame. + virtual void update(float deltaTime); + + /// @brief Renders the scene. + /// @param[ in ] rhi The rendering hardware interface. + virtual void render(renderer::RHI& rhi); + + /// @brief Called when the scene is activated. + virtual void onActivate(); + + /// @brief Called when the scene is deactivated. + virtual void onDeactivate(); + + /// @brief Called when the scene is loaded. + virtual void onLoad(); + + /// @brief Called when the scene is unloaded. + virtual void onUnload(); + + //----------------------------------------------------------------------------------------- + /// @brief Creates a new 2D entity. + /// @return A new entity handle. + //----------------------------------------------------------------------------------------- + Entity2D createEntity(); + + //----------------------------------------------------------------------------------------- + /// @brief Destroys an entity and all its components. + /// @param[ in ] entity The entity to destroy. + //----------------------------------------------------------------------------------------- + void destroyEntity(Entity2D entity); + + //----------------------------------------------------------------------------------------- + /// @brief Adds a transform component to an entity. + /// @param[ in ] entity The entity to add the component to. + /// @return Reference to the added component. + //----------------------------------------------------------------------------------------- + Transform2DComponent& addTransformComponent(Entity2D entity); + + //----------------------------------------------------------------------------------------- + /// @brief Adds a sprite component to an entity. + /// @param[ in ] entity The entity to add the component to. + /// @return Reference to the added component. + //----------------------------------------------------------------------------------------- + SpriteComponent& addSpriteComponent(Entity2D entity); + + //----------------------------------------------------------------------------------------- + /// @brief Adds a text component to an entity. + /// @param[ in ] entity The entity to add the component to. + /// @return Reference to the added component. + //----------------------------------------------------------------------------------------- + TextComponent& addTextComponent(Entity2D entity); + + //----------------------------------------------------------------------------------------- + /// @brief Adds a UI sprite component to an entity. + /// @param[ in ] entity The entity to add the component to. + /// @return Reference to the added component. + //----------------------------------------------------------------------------------------- + UISpriteComponent& addUISpriteComponent(Entity2D entity); + + //----------------------------------------------------------------------------------------- + /// @brief Adds a render component to an entity. + /// @param[ in ] entity The entity to add the component to. + /// @return Reference to the added component. + //----------------------------------------------------------------------------------------- + Render2DComponent& addRenderComponent(Entity2D entity); + + //----------------------------------------------------------------------------------------- + /// @brief Adds an animation component to an entity. + /// @param[ in ] entity The entity to add the component to. + /// @return Reference to the added component. + //----------------------------------------------------------------------------------------- + Animation2DComponent& addAnimationComponent(Entity2D entity); + + //----------------------------------------------------------------------------------------- + /// @brief Gets a transform component from an entity. + /// @param[ in ] entity The entity to get the component from. + /// @return Pointer to the component, or nullptr if not found. + //----------------------------------------------------------------------------------------- + Transform2DComponent* getTransformComponent(Entity2D entity); + + //----------------------------------------------------------------------------------------- + /// @brief Gets a sprite component from an entity. + /// @param[ in ] entity The entity to get the component from. + /// @return Pointer to the component, or nullptr if not found. + //----------------------------------------------------------------------------------------- + SpriteComponent* getSpriteComponent(Entity2D entity); + + //----------------------------------------------------------------------------------------- + /// @brief Gets a text component from an entity. + /// @param[ in ] entity The entity to get the component from. + /// @return Pointer to the component, or nullptr if not found. + //----------------------------------------------------------------------------------------- + TextComponent* getTextComponent(Entity2D entity); + + //----------------------------------------------------------------------------------------- + /// @brief Gets a UI sprite component from an entity. + /// @param[ in ] entity The entity to get the component from. + /// @return Pointer to the component, or nullptr if not found. + //----------------------------------------------------------------------------------------- + UISpriteComponent* getUISpriteComponent(Entity2D entity); + + //----------------------------------------------------------------------------------------- + /// @brief Gets a render component from an entity. + /// @param[ in ] entity The entity to get the component from. + /// @return Pointer to the component, or nullptr if not found. + //----------------------------------------------------------------------------------------- + Render2DComponent* getRenderComponent(Entity2D entity); + + //----------------------------------------------------------------------------------------- + /// @brief Gets an animation component from an entity. + /// @param[ in ] entity The entity to get the component from. + /// @return Pointer to the component, or nullptr if not found. + //----------------------------------------------------------------------------------------- + Animation2DComponent* getAnimationComponent(Entity2D entity); + + //----------------------------------------------------------------------------------------- + /// @brief Creates a new layer. + /// @param[ in ] name The name of the layer. + /// @return Pointer to the created layer, or nullptr if creation failed. + //----------------------------------------------------------------------------------------- + Layer2D* createLayer(const std::string& name); + + //----------------------------------------------------------------------------------------- + /// @brief Destroys a layer by name. + /// @param[ in ] name The name of the layer to destroy. + /// @return True if the layer was found and destroyed, false otherwise. + //----------------------------------------------------------------------------------------- + bool destroyLayer(const std::string& name); + + //----------------------------------------------------------------------------------------- + /// @brief Gets a layer by name. + /// @param[ in ] name The name of the layer to retrieve. + /// @return Pointer to the layer, or nullptr if not found. + //----------------------------------------------------------------------------------------- + Layer2D* getLayer(const std::string& name) const; + + //----------------------------------------------------------------------------------------- + /// @brief Gets the default layer. + /// @return Pointer to the default layer. + //----------------------------------------------------------------------------------------- + Layer2D* getDefaultLayer() const; + + //----------------------------------------------------------------------------------------- + /// @brief Gets the main camera. + /// @return Reference to the main camera. + //----------------------------------------------------------------------------------------- + Camera2D& getMainCamera(); + + //----------------------------------------------------------------------------------------- + /// @brief Gets the main camera (const version). + /// @return Const reference to the main camera. + //----------------------------------------------------------------------------------------- + const Camera2D& getMainCamera() const; + + //----------------------------------------------------------------------------------------- + /// @brief Sets the viewport size. + /// @param[ in ] width The width of the viewport. + /// @param[ in ] height The height of the viewport. + //----------------------------------------------------------------------------------------- + void setViewportSize(uint32_t width, uint32_t height); + + //----------------------------------------------------------------------------------------- + /// @brief Gets the viewport width. + /// @return The viewport width. + //----------------------------------------------------------------------------------------- + uint32_t getViewportWidth() const; + + //----------------------------------------------------------------------------------------- + /// @brief Gets the viewport height. + /// @return The viewport height. + //----------------------------------------------------------------------------------------- + uint32_t getViewportHeight() const; + + //----------------------------------------------------------------------------------------- + /// @brief Checks if an entity is valid. + /// @param[ in ] entity The entity to check. + /// @return True if the entity is valid, false otherwise. + //----------------------------------------------------------------------------------------- + bool isValid(Entity2D entity) const; + + //----------------------------------------------------------------------------------------- + /// @brief Gets the scene name. + /// @return The scene name. + //----------------------------------------------------------------------------------------- + const std::string& getName() const; + + //----------------------------------------------------------------------------------------- + /// @brief Sets the scene name. + /// @param[ in ] name The scene name. + //----------------------------------------------------------------------------------------- + void setName(const std::string& name); + + //----------------------------------------------------------------------------------------- + /// @brief Gets the scene manager. + /// @return Pointer to the scene manager, or nullptr if not assigned. + //----------------------------------------------------------------------------------------- + Scene2DManager* getManager() const; + + protected: + // Component storage using dense arrays with entity mapping + template + class ComponentPool2D { + public: + T& add(Entity2D entity) { + const uint32_t idx = mDense.size(); + mEntityToIndex[entity.id] = idx; + mIndexToEntity.push_back(entity); + mDense.push_back(T{}); + return mDense[idx]; + } + + T* get(Entity2D entity) { + auto it = mEntityToIndex.find(entity.id); + return it == mEntityToIndex.end() ? nullptr : &mDense[it->second]; + } + + void remove(Entity2D entity) { + auto it = mEntityToIndex.find(entity.id); + if (it != mEntityToIndex.end()) { + uint32_t idx = it->second; + // Swap with last element for O(1) removal + if (idx < mDense.size() - 1) { + std::swap(mDense[idx], mDense.back()); + mEntityToIndex[mIndexToEntity.back().id] = idx; + mIndexToEntity[idx] = mIndexToEntity.back(); + } + mDense.pop_back(); + mIndexToEntity.pop_back(); + mEntityToIndex.erase(it); + } + } + + bool has(Entity2D entity) const { + return mEntityToIndex.find(entity.id) != mEntityToIndex.end(); + } + + std::vector& data() { return mDense; } + const std::vector& data() const { return mDense; } + + private: + std::vector mDense; + std::vector mIndexToEntity; + std::unordered_map mEntityToIndex; + }; + + ComponentPool2D mTransformPool; + ComponentPool2D mSpritePool; + ComponentPool2D mTextPool; + ComponentPool2D mUISpritePool; + ComponentPool2D mRenderPool; + ComponentPool2D mAnimationPool; + + std::unordered_map> mLayers; + std::unique_ptr mDefaultLayer; + Camera2D mMainCamera; + std::string mName; + Scene2DManager* mManager{ nullptr }; + uint32_t mViewportWidth{ 0 }; + uint32_t mViewportHeight{ 0 }; + core::guid mNextEntityId{ 1 }; + + // Friend declaration for manager access + friend class Scene2DManager; + }; + + //--------------------------------------------------------------------------------------------- + /// @brief Simple 2D scene for basic rendering. + /// + /// A convenience class for creating simple 2D scenes without complex setup. + //--------------------------------------------------------------------------------------------- + class SEGFAULT_EXPORT SimpleScene2D : public Scene2D { + public: + /// @brief Constructs a new SimpleScene2D. + SimpleScene2D(); + + /// @brief Destroys the SimpleScene2D. + ~SimpleScene2D() = default; + + /// @brief Initializes the simple scene. + /// @param[ in ] rhi The rendering hardware interface. + /// @return True if initialization was successful, false otherwise. + bool init(renderer::RHI& rhi) override; + + /// @brief Creates a simple sprite entity. + /// @param[ in ] textureHandle The handle to the texture. + /// @param[ in ] position The position of the sprite. + /// @param[ in ] size The size of the sprite. + /// @return The created entity. + Entity2D createSprite(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size); + + /// @brief Creates a simple text entity. + /// @param[ in ] text The text to display. + /// @param[ in ] fontHandle The handle to the font. + /// @param[ in ] position The position of the text. + /// @param[ in ] fontSize The font size. + /// @return The created entity. + Entity2D createText(const std::string& text, core::guid fontHandle, const glm::vec2& position, float fontSize = 16.0f); + + /// @brief Creates a simple UI button entity. + /// @param[ in ] textureHandle The handle to the button texture. + /// @param[ in ] position The position of the button. + /// @param[ in ] size The size of the button. + /// @param[ in ] onClick The callback function when the button is clicked. + /// @return The created entity. + Entity2D createButton(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size, std::function onClick); + }; + + //--------------------------------------------------------------------------------------------- + /// @brief UI Scene for user interface rendering. + /// + /// Specialized scene for UI elements with built-in input handling. + //--------------------------------------------------------------------------------------------- + class SEGFAULT_EXPORT UIScene2D : public Scene2D { + public: + /// @brief Constructs a new UIScene2D. + UIScene2D(); + + /// @brief Destroys the UIScene2D. + ~UIScene2D() = default; + + /// @brief Initializes the UI scene. + /// @param[ in ] rhi The rendering hardware interface. + /// @return True if initialization was successful, false otherwise. + bool init(renderer::RHI& rhi) override; + + /// @brief Updates the UI scene with input. + /// @param[ in ] deltaTime The time elapsed since the last frame. + void update(float deltaTime) override; + + // Input handling + virtual void onMouseMove(const glm::vec2& position); + virtual void onMouseDown(const glm::vec2& position, uint8_t button); + virtual void onMouseUp(const glm::vec2& position, uint8_t button); + + /// @brief Creates a UI panel. + /// @param[ in ] textureHandle The handle to the panel texture. + /// @param[ in ] position The position of the panel. + /// @param[ in ] size The size of the panel. + /// @return The created entity. + Entity2D createPanel(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size); + + /// @brief Creates a UI button. + /// @param[ in ] textureHandle The handle to the button texture. + /// @param[ in ] position The position of the button. + /// @param[ in ] size The size of the button. + /// @param[ in ] onClick The callback function when the button is clicked. + /// @return The created entity. + Entity2D createButton(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size, std::function onClick); + + /// @brief Creates a UI text element. + /// @param[ in ] text The text to display. + /// @param[ in ] fontHandle The handle to the font. + /// @param[ in ] position The position of the text. + /// @param[ in ] fontSize The font size. + /// @param[ in ] color The text color. + /// @return The created entity. + Entity2D createText(const std::string& text, core::guid fontHandle, const glm::vec2& position, float fontSize = 16.0f, const glm::vec4& color = glm::vec4(1.0f)); + + /// @brief Creates a UI checkbox. + /// @param[ in ] textureHandle The handle to the checkbox texture. + /// @param[ in ] position The position of the checkbox. + /// @param[ in ] size The size of the checkbox. + /// @param[ in ] onChanged The callback function when the checkbox state changes. + /// @return The created entity. + Entity2D createCheckbox(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size, std::function onChanged); + + /// @brief Creates a UI slider. + /// @param[ in ] backgroundTexture The handle to the background texture. + /// @param[ in ] handleTexture The handle to the handle texture. + /// @param[ in ] position The position of the slider. + /// @param[ in ] size The size of the slider. + /// @param[ in ] minValue The minimum value of the slider. + /// @param[ in ] maxValue The maximum value of the slider. + /// @param[ in ] onChanged The callback function when the slider value changes. + /// @return The created entity. + Entity2D createSlider(core::guid backgroundTexture, core::guid handleTexture, const glm::vec2& position, const glm::vec2& size, float minValue = 0.0f, float maxValue = 1.0f, std::function onChanged = nullptr); + }; + +} // namespace segfault::scene diff --git a/src/runtime/scene/Scene2DManager.cpp b/src/runtime/scene/Scene2DManager.cpp new file mode 100644 index 0000000..ba4ee50 --- /dev/null +++ b/src/runtime/scene/Scene2DManager.cpp @@ -0,0 +1,294 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2015-2026 Segfault by Kim Kulling + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-----------------------------------------------------------------------------------------------*/ + +#include "scene/Scene2DManager.h" +#include "scene/Scene2D.h" +#include "renderer/RHI.h" + +#include + +namespace segfault::scene { + + //--------------------------------------------------------------------------------------------- + // Scene2DManager implementation + //--------------------------------------------------------------------------------------------- + + Scene2DManager::Scene2DManager() : mActiveScene(nullptr), mRHI(nullptr) { + // empty + } + + Scene2DManager::~Scene2DManager() { + shutdown(); + } + + bool Scene2DManager::init(renderer::RHI& rhi) { + if (mRHI != nullptr) { + return true; // Already initialized + } + + mRHI = &rhi; + mActiveScene = nullptr; + mScenes.clear(); + mRenderOrder.clear(); + + return true; + } + + bool Scene2DManager::shutdown() { + unloadAllScenes(); + mRHI = nullptr; + mActiveScene = nullptr; + mSceneFactories.clear(); + return true; + } + + Scene2D* Scene2DManager::createScene(const std::string& name, const std::string& sceneClassName) { + if (mScenes.find(name) != mScenes.end()) { + core::logMessage(core::LogType::Warn, ("Scene with name '" + name + "' already exists").c_str()); + return nullptr; + } + + auto factoryIt = mSceneFactories.find(sceneClassName); + if (factoryIt == mSceneFactories.end()) { + core::logMessage(core::LogType::Error, ("Scene factory for class '" + sceneClassName + "' not registered").c_str()); + return nullptr; + } + + Scene2D* newScene = factoryIt->second(); + if (newScene == nullptr) { + core::logMessage(core::LogType::Error, ("Failed to create scene of class '" + sceneClassName + "'").c_str()); + return nullptr; + } + + newScene->mName = name; + newScene->mManager = this; + + if (!newScene->init(*mRHI)) { + core::logMessage(core::LogType::Error, ("Failed to initialize scene '" + name + "'").c_str()); + delete newScene; + return nullptr; + } + + auto result = mScenes.emplace(name, std::unique_ptr(newScene)); + if (!result.second) { + core::logMessage(core::LogType::Error, ("Failed to emplace scene '" + name + "' into manager").c_str()); + delete newScene; + return nullptr; + } + + // If this is the first scene, activate it + if (mActiveScene == nullptr) { + mActiveScene = newScene; + } + + // Add to render order if not already present + if (std::find(mRenderOrder.begin(), mRenderOrder.end(), name) == mRenderOrder.end()) { + mRenderOrder.push_back(name); + } + + core::logMessage(core::LogType::Info, ("Created scene '" + name + "' of class '" + sceneClassName + "'").c_str()); + + return newScene; + } + + Scene2D* Scene2DManager::loadScene(const std::string& name, std::function factory) { + if (mScenes.find(name) != mScenes.end()) { + core::logMessage(core::LogType::Warn, ("Scene with name '" + name + "' already exists").c_str()); + return nullptr; + } + + if (factory == nullptr) { + core::logMessage(core::LogType::Error, "Scene factory function is null"); + return nullptr; + } + + Scene2D* newScene = factory(); + if (newScene == nullptr) { + core::logMessage(core::LogType::Error, ("Factory failed to create scene '" + name + "'").c_str()); + return nullptr; + } + + newScene->mName = name; + newScene->mManager = this; + + if (!newScene->init(*mRHI)) { + core::logMessage(core::LogType::Error, ("Failed to initialize scene '" + name + "'").c_str()); + delete newScene; + return nullptr; + } + + auto result = mScenes.emplace(name, std::unique_ptr(newScene)); + if (!result.second) { + core::logMessage(core::LogType::Error, ("Failed to emplace scene '" + name + "' into manager").c_str()); + delete newScene; + return nullptr; + } + + // If this is the first scene, activate it + if (mActiveScene == nullptr) { + mActiveScene = newScene; + } + + // Add to render order if not already present + if (std::find(mRenderOrder.begin(), mRenderOrder.end(), name) == mRenderOrder.end()) { + mRenderOrder.push_back(name); + } + + core::logMessage(core::LogType::Info, ("Loaded scene '" + name + "'").c_str()); + + return newScene; + } + + bool Scene2DManager::unloadScene(const std::string& name) { + auto it = mScenes.find(name); + if (it == mScenes.end()) { + core::logMessage(core::LogType::Warn, ("Scene '" + name + "' not found for unloading").c_str()); + return false; + } + + // Check if this scene is active + if (mActiveScene == it->second.get()) { + mActiveScene = nullptr; + } + + // Remove from render order + auto renderIt = std::find(mRenderOrder.begin(), mRenderOrder.end(), name); + if (renderIt != mRenderOrder.end()) { + mRenderOrder.erase(renderIt); + } + + // Shutdown and remove the scene + it->second->shutdown(); + mScenes.erase(it); + + core::logMessage(core::LogType::Info, ("Unloaded scene '" + name + "'").c_str()); + + return true; + } + + void Scene2DManager::unloadAllScenes() { + // Deactivate current scene first + mActiveScene = nullptr; + + // Shutdown and clear all scenes + for (auto& pair : mScenes) { + pair.second->shutdown(); + } + mScenes.clear(); + mRenderOrder.clear(); + + core::logMessage(core::LogType::Info, "Unloaded all scenes"); + } + + bool Scene2DManager::activateScene(const std::string& name) { + auto it = mScenes.find(name); + if (it == mScenes.end()) { + core::logMessage(core::LogType::Error, ("Scene '" + name + "' not found for activation").c_str()); + return false; + } + + if (mActiveScene != nullptr) { + mActiveScene->onDeactivate(); + } + + mActiveScene = it->second.get(); + mActiveScene->onActivate(); + + core::logMessage(core::LogType::Info, ("Activated scene '" + name + "'").c_str()); + + return true; + } + + void Scene2DManager::deactivateCurrentScene() { + if (mActiveScene != nullptr) { + mActiveScene->onDeactivate(); + mActiveScene = nullptr; + core::logMessage(core::LogType::Info, "Deactivated current scene"); + } + } + + Scene2D* Scene2DManager::getActiveScene() const { + return mActiveScene; + } + + Scene2D* Scene2DManager::getScene(const std::string& name) const { + auto it = mScenes.find(name); + if (it == mScenes.end()) { + return nullptr; + } + return it->second.get(); + } + + bool Scene2DManager::hasScene(const std::string& name) const { + return mScenes.find(name) != mScenes.end(); + } + + void Scene2DManager::update(float deltaTime) { + // Update all loaded scenes + for (auto& pair : mScenes) { + pair.second->update(deltaTime); + } + } + + void Scene2DManager::render(renderer::RHI& rhi) { + if (mActiveScene != nullptr) { + mActiveScene->render(rhi); + } + } + + void Scene2DManager::renderScene(renderer::RHI& rhi, const std::string& name) { + auto it = mScenes.find(name); + if (it != mScenes.end()) { + it->second->render(rhi); + } + } + + void Scene2DManager::setRenderOrder(const std::vector& sceneNames) { + mRenderOrder = sceneNames; + + // Verify all scenes in render order exist + for (const auto& name : mRenderOrder) { + if (mScenes.find(name) == mScenes.end()) { + core::logMessage(core::LogType::Warn, ("Scene '" + name + "' in render order does not exist").c_str()); + } + } + } + + std::vector Scene2DManager::getSceneNames() const { + std::vector names; + for (const auto& pair : mScenes) { + names.push_back(pair.first); + } + return names; + } + + uint32_t Scene2DManager::getSceneCount() const { + return static_cast(mScenes.size()); + } + + void Scene2DManager::registerSceneFactory(const std::string& className, std::function factory) { + mSceneFactories[className] = factory; + core::logMessage(core::LogType::Info, ("Registered scene factory for class '" + className + "'").c_str()); + } + +} // namespace segfault::scene diff --git a/src/runtime/scene/Scene2DManager.h b/src/runtime/scene/Scene2DManager.h new file mode 100644 index 0000000..34539d7 --- /dev/null +++ b/src/runtime/scene/Scene2DManager.h @@ -0,0 +1,187 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2015-2026 Segfault by Kim Kulling + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-----------------------------------------------------------------------------------------------*/ +#pragma once + +#include "core/segfault.h" +#include "scene/Scene2D.h" + +#include +#include +#include +#include + +namespace segfault::scene { + + // Forward declarations + class Scene2D; + + //--------------------------------------------------------------------------------------------- + /// @class Scene2DManager + /// @brief Manages multiple 2D scenes, providing loading, unloading, activation, and rendering services. + /// + /// The Scene2DManager is the central point for handling all 2D rendering scenes in the engine. + /// It maintains a collection of scenes, manages scene transitions, and coordinates rendering. + //--------------------------------------------------------------------------------------------- + class SEGFAULT_EXPORT Scene2DManager final { + public: + /// @brief Constructs a new Scene2DManager. + Scene2DManager(); + + /// @brief Destroys the Scene2DManager and all loaded scenes. + ~Scene2DManager(); + + /// @brief Deleted copy constructor. + Scene2DManager(const Scene2DManager&) = delete; + + /// @brief Deleted copy assignment operator. + Scene2DManager& operator=(const Scene2DManager&) = delete; + + //----------------------------------------------------------------------------------------- + /// @brief Initializes the scene manager. + /// @param[ in ] rhi The rendering hardware interface to use. + /// @return True if initialization was successful, false otherwise. + //----------------------------------------------------------------------------------------- + bool init(renderer::RHI& rhi); + + //----------------------------------------------------------------------------------------- + /// @brief Shuts down the scene manager and releases all resources. + /// @return True if shutdown was successful, false otherwise. + //----------------------------------------------------------------------------------------- + bool shutdown(); + + //----------------------------------------------------------------------------------------- + /// @brief Creates and loads a new 2D scene. + /// @param[ in ] name The unique name of the scene. + /// @param[ in ] sceneClassName The class name of the scene to instantiate. + /// @return Pointer to the created scene, or nullptr if creation failed. + //----------------------------------------------------------------------------------------- + Scene2D* createScene(const std::string& name, const std::string& sceneClassName); + + //----------------------------------------------------------------------------------------- + /// @brief Loads a 2D scene from a custom factory function. + /// @param[ in ] name The unique name of the scene. + /// @param[ in ] factory Function that creates and returns a Scene2D pointer. + /// @return Pointer to the loaded scene, or nullptr if loading failed. + //----------------------------------------------------------------------------------------- + Scene2D* loadScene(const std::string& name, std::function factory); + + //----------------------------------------------------------------------------------------- + /// @brief Unloads a scene by name. + /// @param[ in ] name The name of the scene to unload. + /// @return True if the scene was found and unloaded, false otherwise. + //----------------------------------------------------------------------------------------- + bool unloadScene(const std::string& name); + + //----------------------------------------------------------------------------------------- + /// @brief Unloads all loaded scenes. + //----------------------------------------------------------------------------------------- + void unloadAllScenes(); + + //----------------------------------------------------------------------------------------- + /// @brief Activates a scene for rendering and updates. + /// @param[ in ] name The name of the scene to activate. + /// @return True if the scene was found and activated, false otherwise. + //----------------------------------------------------------------------------------------- + bool activateScene(const std::string& name); + + //----------------------------------------------------------------------------------------- + /// @brief Deactivates the currently active scene. + //----------------------------------------------------------------------------------------- + void deactivateCurrentScene(); + + //----------------------------------------------------------------------------------------- + /// @brief Gets the currently active scene. + /// @return Pointer to the active scene, or nullptr if no scene is active. + //----------------------------------------------------------------------------------------- + Scene2D* getActiveScene() const; + + //----------------------------------------------------------------------------------------- + /// @brief Gets the currently active scene by name. + /// @param[ in ] name The name of the scene to retrieve. + /// @return Pointer to the scene, or nullptr if not found. + //----------------------------------------------------------------------------------------- + Scene2D* getScene(const std::string& name) const; + + //----------------------------------------------------------------------------------------- + /// @brief Checks if a scene with the given name exists. + /// @param[ in ] name The name of the scene to check. + /// @return True if the scene exists, false otherwise. + //----------------------------------------------------------------------------------------- + bool hasScene(const std::string& name) const; + + //----------------------------------------------------------------------------------------- + /// @brief Updates all loaded scenes. + /// @param[ in ] deltaTime The time elapsed since the last frame in seconds. + //----------------------------------------------------------------------------------------- + void update(float deltaTime); + + //----------------------------------------------------------------------------------------- + /// @brief Renders the active scene. + /// @param[ in ] rhi The rendering hardware interface to use. + //----------------------------------------------------------------------------------------- + void render(renderer::RHI& rhi); + + //----------------------------------------------------------------------------------------- + /// @brief Renders a specific scene by name. + /// @param[ in ] rhi The rendering hardware interface to use. + /// @param[ in ] name The name of the scene to render. + //----------------------------------------------------------------------------------------- + void renderScene(renderer::RHI& rhi, const std::string& name); + + //----------------------------------------------------------------------------------------- + /// @brief Sets the rendering order for scenes. + /// @param[ in ] sceneNames Array of scene names in the desired rendering order. + //----------------------------------------------------------------------------------------- + void setRenderOrder(const std::vector& sceneNames); + + //----------------------------------------------------------------------------------------- + /// @brief Gets all loaded scene names. + /// @return Array of all loaded scene names. + //----------------------------------------------------------------------------------------- + std::vector getSceneNames() const; + + //----------------------------------------------------------------------------------------- + /// @brief Gets the number of loaded scenes. + /// @return The number of loaded scenes. + //----------------------------------------------------------------------------------------- + uint32_t getSceneCount() const; + + //----------------------------------------------------------------------------------------- + /// @brief Registers a scene factory function for a specific scene class name. + /// @param[ in ] className The class name to register the factory for. + /// @param[ in ] factory The factory function that creates scenes of this type. + //----------------------------------------------------------------------------------------- + void registerSceneFactory(const std::string& className, std::function factory); + + private: + using SceneMap = std::unordered_map>; + using SceneFactoryMap = std::unordered_map>; + + SceneMap mScenes; + SceneFactoryMap mSceneFactories; + Scene2D* mActiveScene{ nullptr }; + std::vector mRenderOrder; + renderer::RHI* mRHI{ nullptr }; + }; + +} // namespace segfault::scene From 75075c68de022bd283aedea8df06066399afcd5a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 16 Oct 2026 21:06:14 +0200 Subject: [PATCH 02/18] Ensure the asset build was performed --- .gitignore | 2 - scripts/CMakeLists.txt | 10 ++ src/editor/CMakeLists.txt | 2 + .../behaviortree_sample/CMakeLists.txt | 1 + src/examples/hello_world/CMakeLists.txt | 1 + src/examples/scene_sample/CMakeLists.txt | 1 + src/examples/scene_sample_2d/CMakeLists.txt | 1 + src/runtime/scene/Scene2D.cpp | 164 +----------------- src/runtime/scene/Scene2D.h | 93 ---------- src/tools/assetbaker/CMakeLists.txt | 1 + 10 files changed, 21 insertions(+), 255 deletions(-) diff --git a/.gitignore b/.gitignore index 4a4570c..1768168 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ CMakeFiles/ Makefile cmake_install.cmake install_manifest.txt -*.dir/ # vcpkg vcpkg/ @@ -50,7 +49,6 @@ vcpkg-installed/ *.log *.swp *.swo -*~ # OS-specific files .DS_Store diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index b5525fe..ef27c10 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -28,6 +28,16 @@ # Find Python interpreter find_package(Python3 COMPONENTS Interpreter REQUIRED) +if(CMAKE_BUILD_TYPE) + set(BUILD_CONFIG "${CMAKE_BUILD_TYPE}") +elseif(CMAKE_CONFIGURATION_TYPES) + # For multi-config generators, use the current config from the --config flag + # This requires CMake 3.15+ for proper generator expressions + set(BUILD_CONFIG "$") +else() + set(BUILD_CONFIG "Release") +endif() + # Get the build configuration (Debug/Release) if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(BUILD_CONFIG "Debug") diff --git a/src/editor/CMakeLists.txt b/src/editor/CMakeLists.txt index 016348a..20d668a 100644 --- a/src/editor/CMakeLists.txt +++ b/src/editor/CMakeLists.txt @@ -8,3 +8,5 @@ target_link_libraries(segfault_editor SDL2::SDL2 glm::glm ) + +add_dependencies(segfault_editor compile_all_assets) diff --git a/src/examples/behaviortree_sample/CMakeLists.txt b/src/examples/behaviortree_sample/CMakeLists.txt index 630db8e..e8364f9 100644 --- a/src/examples/behaviortree_sample/CMakeLists.txt +++ b/src/examples/behaviortree_sample/CMakeLists.txt @@ -1,4 +1,5 @@ add_executable(behaviortree_sample behaviortree_sample.cpp) target_link_libraries(behaviortree_sample example_common) +add_dependencies(behaviortree_sample compile_all_assets) set_target_properties(behaviortree_sample PROPERTIES FOLDER examples ) diff --git a/src/examples/hello_world/CMakeLists.txt b/src/examples/hello_world/CMakeLists.txt index 6ec6896..a4aa294 100644 --- a/src/examples/hello_world/CMakeLists.txt +++ b/src/examples/hello_world/CMakeLists.txt @@ -1,4 +1,5 @@ add_executable(hello_world hello_world.cpp) target_link_libraries(hello_world example_common) +add_dependencies(hello_world compile_all_assets) set_target_properties(hello_world PROPERTIES FOLDER examples ) diff --git a/src/examples/scene_sample/CMakeLists.txt b/src/examples/scene_sample/CMakeLists.txt index 50a4b16..238d408 100644 --- a/src/examples/scene_sample/CMakeLists.txt +++ b/src/examples/scene_sample/CMakeLists.txt @@ -1,4 +1,5 @@ add_executable(scene_sample scene_sample.cpp) target_link_libraries(scene_sample example_common) +add_dependencies(scene_sample compile_all_assets) set_target_properties(scene_sample PROPERTIES FOLDER examples ) diff --git a/src/examples/scene_sample_2d/CMakeLists.txt b/src/examples/scene_sample_2d/CMakeLists.txt index 5a31cae..0fc904d 100644 --- a/src/examples/scene_sample_2d/CMakeLists.txt +++ b/src/examples/scene_sample_2d/CMakeLists.txt @@ -1,4 +1,5 @@ add_executable(scene_sample_2d scene_sample_2d.cpp) target_link_libraries(scene_sample_2d example_common) +add_dependencies(scene_sample_2d compile_all_assets) set_target_properties(scene_sample_2d PROPERTIES FOLDER examples ) diff --git a/src/runtime/scene/Scene2D.cpp b/src/runtime/scene/Scene2D.cpp index 9fe13b9..5f43a87 100644 --- a/src/runtime/scene/Scene2D.cpp +++ b/src/runtime/scene/Scene2D.cpp @@ -261,9 +261,11 @@ namespace segfault::scene { //--------------------------------------------------------------------------------------------- void Transform2DComponent::updateWorldMatrix() { - if (!mDirty) return; + if (!mDirty) { + return; + } - glm::mat4 transform = glm::mat4(1.0f); + auto transform = glm::mat4(1.0f); // Translate to position transform = glm::translate(transform, glm::vec3(mPosition.x, mPosition.y, 0.0f)); @@ -398,67 +400,6 @@ namespace segfault::scene { mVisible = visible; } - //--------------------------------------------------------------------------------------------- - // UISpriteComponent implementation - //--------------------------------------------------------------------------------------------- - - void UISpriteComponent::setTexture(core::guid textureHandle) { - mTextureHandle = textureHandle; - } - - void UISpriteComponent::setNineSliceBorders(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) { - mBorderLeft = left; - mBorderRight = right; - mBorderTop = top; - mBorderBottom = bottom; - } - - void UISpriteComponent::setVisible(bool visible) { - mVisible = visible; - } - - void UISpriteComponent::setInteractive(bool interactive) { - mInteractive = interactive; - } - - void UISpriteComponent::setEnabled(bool enabled) { - mEnabled = enabled; - } - - void UISpriteComponent::setOnClick(std::function callback) { - mOnClick = callback; - } - - void UISpriteComponent::setOnHoverEnter(std::function callback) { - mOnHoverEnter = callback; - } - - void UISpriteComponent::setOnHoverExit(std::function callback) { - mOnHoverExit = callback; - } - - void UISpriteComponent::onClick() { - if (mEnabled && mInteractive && mOnClick) { - mPressed = true; - mOnClick(); - mPressed = false; - } - } - - void UISpriteComponent::onHoverEnter() { - if (mEnabled && mInteractive && mOnHoverEnter) { - mHovered = true; - mOnHoverEnter(); - } - } - - void UISpriteComponent::onHoverExit() { - if (mEnabled && mInteractive && mOnHoverExit) { - mHovered = false; - mOnHoverExit(); - } - } - //--------------------------------------------------------------------------------------------- // Render2DComponent implementation //--------------------------------------------------------------------------------------------- @@ -690,7 +631,6 @@ namespace segfault::scene { mTransformPool.remove(entity); mSpritePool.remove(entity); mTextPool.remove(entity); - mUISpritePool.remove(entity); mRenderPool.remove(entity); mAnimationPool.remove(entity); } @@ -707,10 +647,6 @@ namespace segfault::scene { return mTextPool.add(entity); } - UISpriteComponent& Scene2D::addUISpriteComponent(Entity2D entity) { - return mUISpritePool.add(entity); - } - Render2DComponent& Scene2D::addRenderComponent(Entity2D entity) { Render2DComponent& component = mRenderPool.add(entity); component.setLayer(mDefaultLayer.get()); @@ -733,10 +669,6 @@ namespace segfault::scene { return mTextPool.get(entity); } - UISpriteComponent* Scene2D::getUISpriteComponent(Entity2D entity) { - return mUISpritePool.get(entity); - } - Render2DComponent* Scene2D::getRenderComponent(Entity2D entity) { return mRenderPool.get(entity); } @@ -865,24 +797,6 @@ namespace segfault::scene { return entity; } - Entity2D SimpleScene2D::createButton(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size, std::function onClick) { - Entity2D entity = createEntity(); - - Transform2DComponent& transform = addTransformComponent(entity); - transform.setPosition(position); - transform.setSize(size); - - UISpriteComponent& uiSprite = addUISpriteComponent(entity); - uiSprite.setTexture(textureHandle); - uiSprite.setInteractive(true); - uiSprite.setOnClick(onClick); - - Render2DComponent& render = addRenderComponent(entity); - render.setVisible(true); - - return entity; - } - //--------------------------------------------------------------------------------------------- // UIScene2D implementation //--------------------------------------------------------------------------------------------- @@ -916,41 +830,6 @@ namespace segfault::scene { // Handle mouse up for UI elements } - Entity2D UIScene2D::createPanel(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size) { - Entity2D entity = createEntity(); - - Transform2DComponent& transform = addTransformComponent(entity); - transform.setPosition(position); - transform.setSize(size); - - SpriteComponent& sprite = addSpriteComponent(entity); - sprite.setTexture(textureHandle); - - Render2DComponent& render = addRenderComponent(entity); - render.setVisible(true); - - return entity; - } - - Entity2D UIScene2D::createButton(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size, std::function onClick) { - Entity2D entity = createEntity(); - - Transform2DComponent& transform = addTransformComponent(entity); - transform.setPosition(position); - transform.setSize(size); - - UISpriteComponent& uiSprite = addUISpriteComponent(entity); - uiSprite.setTexture(textureHandle); - uiSprite.setInteractive(true); - uiSprite.setEnabled(true); - uiSprite.setOnClick(onClick); - - Render2DComponent& render = addRenderComponent(entity); - render.setVisible(true); - - return entity; - } - Entity2D UIScene2D::createText(const std::string& text, core::guid fontHandle, const glm::vec2& position, float fontSize, const glm::vec4& color) { Entity2D entity = createEntity(); @@ -969,39 +848,4 @@ namespace segfault::scene { return entity; } - Entity2D UIScene2D::createCheckbox(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size, std::function onChanged) { - Entity2D entity = createEntity(); - - Transform2DComponent& transform = addTransformComponent(entity); - transform.setPosition(position); - transform.setSize(size); - - UISpriteComponent& uiSprite = addUISpriteComponent(entity); - uiSprite.setTexture(textureHandle); - uiSprite.setInteractive(true); - uiSprite.setEnabled(true); - // For checkbox, we'll use the selected state to represent checked/unchecked - - Render2DComponent& render = addRenderComponent(entity); - render.setVisible(true); - - return entity; - } - - Entity2D UIScene2D::createSlider(core::guid backgroundTexture, core::guid handleTexture, const glm::vec2& position, const glm::vec2& size, float minValue, float maxValue, std::function onChanged) { - Entity2D entity = createEntity(); - - Transform2DComponent& transform = addTransformComponent(entity); - transform.setPosition(position); - transform.setSize(size); - - SpriteComponent& background = addSpriteComponent(entity); - background.setTexture(backgroundTexture); - - Render2DComponent& render = addRenderComponent(entity); - render.setVisible(true); - - return entity; - } - } // namespace segfault::scene diff --git a/src/runtime/scene/Scene2D.h b/src/runtime/scene/Scene2D.h index 7cd1ab4..ee88c21 100644 --- a/src/runtime/scene/Scene2D.h +++ b/src/runtime/scene/Scene2D.h @@ -466,84 +466,6 @@ namespace segfault::scene { void setVisible(bool visible); }; - //--------------------------------------------------------------------------------------------- - /// @brief UI Sprite component for 2D user interface elements. - /// - /// Extended sprite component with UI-specific features like nine-slice scaling. - //--------------------------------------------------------------------------------------------- - struct SEGFAULT_EXPORT UISpriteComponent final { - core::guid mTextureHandle{ 0 }; - core::Rect mTextureRect{ 0, 0, 0, 0 }; - glm::vec4 mColor{ 1.0f, 1.0f, 1.0f, 1.0f }; - float mAlpha{ 1.0f }; - bool mVisible{ true }; - - // Nine-slice border settings - uint32_t mBorderLeft{ 0 }; - uint32_t mBorderRight{ 0 }; - uint32_t mBorderTop{ 0 }; - uint32_t mBorderBottom{ 0 }; - - // UI-specific settings - bool mInteractive{ false }; - bool mEnabled{ true }; - bool mSelected{ false }; - bool mPressed{ false }; - bool mHovered{ false }; - - // Callback functions for UI events - std::function mOnClick; - std::function mOnHoverEnter; - std::function mOnHoverExit; - - /// @brief Default constructor. - UISpriteComponent() = default; - - /// @brief Sets the texture handle. - /// @param[ in ] textureHandle The handle to the texture. - void setTexture(core::guid textureHandle); - - /// @brief Sets the nine-slice borders. - /// @param[ in ] left The left border width. - /// @param[ in ] right The right border width. - /// @param[ in ] top The top border height. - /// @param[ in ] bottom The bottom border height. - void setNineSliceBorders(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom); - - /// @brief Sets visibility. - /// @param[ in ] visible True to make visible, false to hide. - void setVisible(bool visible); - - /// @brief Sets interactiveness. - /// @param[ in ] interactive True to enable interaction, false to disable. - void setInteractive(bool interactive); - - /// @brief Sets enabled state. - /// @param[ in ] enabled True to enable, false to disable. - void setEnabled(bool enabled); - - /// @brief Sets the click callback. - /// @param[ in ] callback The function to call when clicked. - void setOnClick(std::function callback); - - /// @brief Sets the hover enter callback. - /// @param[ in ] callback The function to call when hovered over. - void setOnHoverEnter(std::function callback); - - /// @brief Sets the hover exit callback. - /// @param[ in ] callback The function to call when hover exits. - void setOnHoverExit(std::function callback); - - /// @brief Simulates a click event. - void onClick(); - - /// @brief Simulates a hover enter event. - void onHoverEnter(); - - /// @brief Simulates a hover exit event. - void onHoverExit(); - }; - //--------------------------------------------------------------------------------------------- /// @brief Render component for 2D entities. /// @@ -723,13 +645,6 @@ namespace segfault::scene { //----------------------------------------------------------------------------------------- TextComponent& addTextComponent(Entity2D entity); - //----------------------------------------------------------------------------------------- - /// @brief Adds a UI sprite component to an entity. - /// @param[ in ] entity The entity to add the component to. - /// @return Reference to the added component. - //----------------------------------------------------------------------------------------- - UISpriteComponent& addUISpriteComponent(Entity2D entity); - //----------------------------------------------------------------------------------------- /// @brief Adds a render component to an entity. /// @param[ in ] entity The entity to add the component to. @@ -765,13 +680,6 @@ namespace segfault::scene { //----------------------------------------------------------------------------------------- TextComponent* getTextComponent(Entity2D entity); - //----------------------------------------------------------------------------------------- - /// @brief Gets a UI sprite component from an entity. - /// @param[ in ] entity The entity to get the component from. - /// @return Pointer to the component, or nullptr if not found. - //----------------------------------------------------------------------------------------- - UISpriteComponent* getUISpriteComponent(Entity2D entity); - //----------------------------------------------------------------------------------------- /// @brief Gets a render component from an entity. /// @param[ in ] entity The entity to get the component from. @@ -919,7 +827,6 @@ namespace segfault::scene { ComponentPool2D mTransformPool; ComponentPool2D mSpritePool; ComponentPool2D mTextPool; - ComponentPool2D mUISpritePool; ComponentPool2D mRenderPool; ComponentPool2D mAnimationPool; diff --git a/src/tools/assetbaker/CMakeLists.txt b/src/tools/assetbaker/CMakeLists.txt index 28190ea..4639698 100644 --- a/src/tools/assetbaker/CMakeLists.txt +++ b/src/tools/assetbaker/CMakeLists.txt @@ -1,4 +1,5 @@ add_executable(assetbaker main.cpp) target_link_libraries(assetbaker segfault_runtime nlohmann_json::nlohmann_json) +add_dependencies(assetbaker compile_all_assets) set_target_properties(assetbaker PROPERTIES FOLDER tools\\assetbaker ) From 1ce8e1795ecee3337db877a23fc318ba47b6d364 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 16 Oct 2026 21:38:48 +0200 Subject: [PATCH 03/18] Fix review comment: store x- and y-offset on resize --- src/runtime/scene/Scene2D.cpp | 16 ++++++++-------- src/runtime/scene/Scene2D.h | 8 +++++--- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/runtime/scene/Scene2D.cpp b/src/runtime/scene/Scene2D.cpp index 5f43a87..6a1e10e 100644 --- a/src/runtime/scene/Scene2D.cpp +++ b/src/runtime/scene/Scene2D.cpp @@ -41,12 +41,12 @@ namespace segfault::scene { mDirty = true; } - void Camera2D::setOrthographicFromViewport(uint32_t width, uint32_t height, float near, float far) { + void Camera2D::setOrthographicFromViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height, float near, float far) { float halfWidth = static_cast(width) * 0.5f; float halfHeight = static_cast(height) * 0.5f; setOrthographic(-halfWidth, halfWidth, -halfHeight, halfHeight, near, far); - mViewport.x = 0; - mViewport.y = 0; + mViewport.x = x; + mViewport.y = y; mViewport.width = width; mViewport.height = height; mDirty = true; @@ -138,8 +138,8 @@ namespace segfault::scene { mDirty = true; } - void Camera2D::resize(uint32_t width, uint32_t height) { - setOrthographicFromViewport(width, height); + void Camera2D::resize(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { + setOrthographicFromViewport(x, y, width, height); } void Camera2D::setViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { @@ -147,7 +147,7 @@ namespace segfault::scene { mViewport.y = y; mViewport.width = width; mViewport.height = height; - resize(width, height); + resize(x, y, width, height); } const core::Rect& Camera2D::getViewport() const { @@ -523,7 +523,7 @@ namespace segfault::scene { bool Scene2D::init(renderer::RHI& rhi) { // Initialize main camera - mMainCamera.resize(mViewportWidth, mViewportHeight); + mMainCamera.resize(0, 0, mViewportWidth, mViewportHeight); // Initialize default layer if (mDefaultLayer) { @@ -722,7 +722,7 @@ namespace segfault::scene { void Scene2D::setViewportSize(uint32_t width, uint32_t height) { mViewportWidth = width; mViewportHeight = height; - mMainCamera.resize(width, height); + mMainCamera.resize(0, 0, width, height); } uint32_t Scene2D::getViewportWidth() const { diff --git a/src/runtime/scene/Scene2D.h b/src/runtime/scene/Scene2D.h index ee88c21..266fe0b 100644 --- a/src/runtime/scene/Scene2D.h +++ b/src/runtime/scene/Scene2D.h @@ -63,7 +63,7 @@ namespace segfault::scene { /// @param[ in ] near The near clipping plane. /// @param[ in ] far The far clipping plane. //----------------------------------------------------------------------------------------- - void setOrthographic(float left, float right, float bottom, float top, float near = -1.0f, float far = 1.0f); + void setOrthographic( float left, float right, float bottom, float top, float near = -1.0f, float far = 1.0f); //----------------------------------------------------------------------------------------- /// @brief Sets up an orthographic projection based on viewport size. @@ -72,7 +72,7 @@ namespace segfault::scene { /// @param[ in ] near The near clipping plane. /// @param[ in ] far The far clipping plane. //----------------------------------------------------------------------------------------- - void setOrthographicFromViewport(uint32_t width, uint32_t height, float near = -1.0f, float far = 1.0f); + void setOrthographicFromViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height, float near = -1.0f, float far = 1.0f); //----------------------------------------------------------------------------------------- /// @brief Sets the position of the camera. @@ -161,10 +161,12 @@ namespace segfault::scene { //----------------------------------------------------------------------------------------- /// @brief Resizes the camera viewport. + /// @param[ in ] x The x-coordinate of the viewport. + /// @param[ in ] y The y-coordinate of the viewport. /// @param[ in ] width The new width of the viewport. /// @param[ in ] height The new height of the viewport. //----------------------------------------------------------------------------------------- - void resize(uint32_t width, uint32_t height); + void resize(uint32_t x, uint32_t y, uint32_t width, uint32_t height); //----------------------------------------------------------------------------------------- /// @brief Sets the viewport boundaries. From 79e489c58f9ec8365c9e0354e86e0fecf5e90ae9 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 16 Oct 2026 21:43:25 +0200 Subject: [PATCH 04/18] Fix review findings: handling of out-of-bound animation and doubled added entity --- src/runtime/scene/Scene2D.cpp | 9 +++++++-- src/runtime/scene/Scene2D.h | 6 +++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/runtime/scene/Scene2D.cpp b/src/runtime/scene/Scene2D.cpp index 6a1e10e..116edc5 100644 --- a/src/runtime/scene/Scene2D.cpp +++ b/src/runtime/scene/Scene2D.cpp @@ -455,8 +455,13 @@ namespace segfault::scene { mFrameTimer += deltaTime * mAnimationSpeed; const Frame& currentFrame = mFrames[mCurrentFrame]; - while (mFrameTimer >= currentFrame.mDuration) { - mFrameTimer -= currentFrame.mDuration; + + if (mCurrentFrame >= mFrames.size()) { + mCurrentFrame = 0; + } + + while (mFrames[mCurrentFrame].mDuration > 0.0f && mFrameTimer >= mFrames[mCurrentFrame].mDuration) { + mFrameTimer -= mFrames[mCurrentFrame].mDuration; mCurrentFrame++; if (mCurrentFrame >= mFrames.size()) { diff --git a/src/runtime/scene/Scene2D.h b/src/runtime/scene/Scene2D.h index 266fe0b..8c08054 100644 --- a/src/runtime/scene/Scene2D.h +++ b/src/runtime/scene/Scene2D.h @@ -785,7 +785,11 @@ namespace segfault::scene { class ComponentPool2D { public: T& add(Entity2D entity) { - const uint32_t idx = mDense.size(); + auto it = mEntityToIndex.find(entity.id); + if (it != mEntityToIndex.end()) { + return mDense[it->second]; + } + const uint32_t idx = static_cast(mDense.size()); mEntityToIndex[entity.id] = idx; mIndexToEntity.push_back(entity); mDense.push_back(T{}); From 70661b66fe331af2ca6f741fe17879905a4f7459 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 16 Oct 2026 21:46:41 +0200 Subject: [PATCH 05/18] Review finding: fix missing onActivate/onDesactivate callback --- src/runtime/scene/Scene2DManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/runtime/scene/Scene2DManager.cpp b/src/runtime/scene/Scene2DManager.cpp index ba4ee50..c9cc27a 100644 --- a/src/runtime/scene/Scene2DManager.cpp +++ b/src/runtime/scene/Scene2DManager.cpp @@ -99,6 +99,7 @@ namespace segfault::scene { // If this is the first scene, activate it if (mActiveScene == nullptr) { mActiveScene = newScene; + mActiveScene->onActivate(); } // Add to render order if not already present @@ -147,6 +148,7 @@ namespace segfault::scene { // If this is the first scene, activate it if (mActiveScene == nullptr) { mActiveScene = newScene; + mActiveScene->onActivate(); } // Add to render order if not already present From 5e21df69cf2f97b82a9e2231456c349aeb8ef9c1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 16 Oct 2026 21:57:19 +0200 Subject: [PATCH 06/18] Update dependencies in CMake workflow Added glslc to the list of installed dependencies. --- .github/workflows/cmake-multi-platform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 3d3e0d8..103a665 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -57,7 +57,7 @@ jobs: run: | sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" sudo apt-get update -y -qq - sudo apt-get install libsdl2-dev libglm-dev libvulkan-volk-dev nlohmann-json3-dev libvulkan-dev libstb-dev + sudo apt-get install libsdl2-dev libglm-dev libvulkan-volk-dev nlohmann-json3-dev libvulkan-dev libstb-dev glslc - name: Set reusable strings From 1c2d65609da2ff9b547328c62b87b03ee05b3706 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 17 Oct 2026 09:30:29 +0200 Subject: [PATCH 07/18] Minor fixes. --- src/runtime/scene/Scene2D.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/scene/Scene2D.h b/src/runtime/scene/Scene2D.h index 8c08054..23ed976 100644 --- a/src/runtime/scene/Scene2D.h +++ b/src/runtime/scene/Scene2D.h @@ -307,12 +307,12 @@ namespace segfault::scene { /// @brief Comparison operator. /// @param[ in ] other The other entity to compare with. /// @return True if the entities are equal, false otherwise. - bool operator==(const Entity2D& other) const; + bool operator == (const Entity2D& other) const; /// @brief Comparison operator. /// @param[ in ] other The other entity to compare with. /// @return True if the entities are not equal, false otherwise. - bool operator!=(const Entity2D& other) const; + bool operator != (const Entity2D& other) const; }; //--------------------------------------------------------------------------------------------- From 0d16e188c142cf954f720dfe868be4326509095e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 17 Oct 2026 09:31:10 +0200 Subject: [PATCH 08/18] Add missing callback on desactivation --- src/runtime/scene/Scene2DManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/runtime/scene/Scene2DManager.cpp b/src/runtime/scene/Scene2DManager.cpp index c9cc27a..253a4a9 100644 --- a/src/runtime/scene/Scene2DManager.cpp +++ b/src/runtime/scene/Scene2DManager.cpp @@ -170,6 +170,7 @@ namespace segfault::scene { // Check if this scene is active if (mActiveScene == it->second.get()) { + mActiveScene->onDeactivate(); mActiveScene = nullptr; } From 0f323bf7559a87e4b685fd417db6338bf9132ab9 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 17 Oct 2026 15:36:38 +0200 Subject: [PATCH 09/18] Renamed files --- src/runtime/scene/{Scene2D.cpp => scene2D.cpp} | 0 src/runtime/scene/{Scene2D.h => scene2D.h} | 0 src/runtime/scene/{Scene2DManager.cpp => scene2dmanager.cpp} | 0 src/runtime/scene/{Scene2DManager.h => scene2dmanager.h} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename src/runtime/scene/{Scene2D.cpp => scene2D.cpp} (100%) rename src/runtime/scene/{Scene2D.h => scene2D.h} (100%) rename src/runtime/scene/{Scene2DManager.cpp => scene2dmanager.cpp} (100%) rename src/runtime/scene/{Scene2DManager.h => scene2dmanager.h} (100%) diff --git a/src/runtime/scene/Scene2D.cpp b/src/runtime/scene/scene2D.cpp similarity index 100% rename from src/runtime/scene/Scene2D.cpp rename to src/runtime/scene/scene2D.cpp diff --git a/src/runtime/scene/Scene2D.h b/src/runtime/scene/scene2D.h similarity index 100% rename from src/runtime/scene/Scene2D.h rename to src/runtime/scene/scene2D.h diff --git a/src/runtime/scene/Scene2DManager.cpp b/src/runtime/scene/scene2dmanager.cpp similarity index 100% rename from src/runtime/scene/Scene2DManager.cpp rename to src/runtime/scene/scene2dmanager.cpp diff --git a/src/runtime/scene/Scene2DManager.h b/src/runtime/scene/scene2dmanager.h similarity index 100% rename from src/runtime/scene/Scene2DManager.h rename to src/runtime/scene/scene2dmanager.h From aa3788850c39191fd0f5655f6eadabbf1012e64a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 17 Oct 2026 18:18:23 +0200 Subject: [PATCH 10/18] Install doxygen in CI workflow Added doxygen to the list of installed dependencies. --- .github/workflows/cmake-multi-platform.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 103a665..09f70f5 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -57,7 +57,8 @@ jobs: run: | sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" sudo apt-get update -y -qq - sudo apt-get install libsdl2-dev libglm-dev libvulkan-volk-dev nlohmann-json3-dev libvulkan-dev libstb-dev glslc + sudo apt-get install libsdl2-dev libglm-dev libvulkan-volk-dev nlohmann-json3-dev libvulkan-dev libstb-dev glslc | + doxygen - name: Set reusable strings From a2f56ead2652d7a0c92052cbff0d36aeb1ebd19c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 17 Oct 2026 18:21:09 +0200 Subject: [PATCH 11/18] Combine package installation commands in workflow --- .github/workflows/cmake-multi-platform.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 09f70f5..eab2543 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -57,8 +57,7 @@ jobs: run: | sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" sudo apt-get update -y -qq - sudo apt-get install libsdl2-dev libglm-dev libvulkan-volk-dev nlohmann-json3-dev libvulkan-dev libstb-dev glslc | - doxygen + sudo apt-get install libsdl2-dev libglm-dev libvulkan-volk-dev nlohmann-json3-dev libvulkan-dev libstb-dev glslc doxygen - name: Set reusable strings From 38202e699f1ce3c7b9cbb7b0dd28d14fbe6432a9 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 17 Oct 2026 18:24:06 +0200 Subject: [PATCH 12/18] Fix names --- src/runtime/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/runtime/CMakeLists.txt b/src/runtime/CMakeLists.txt index c269f49..7662bc7 100644 --- a/src/runtime/CMakeLists.txt +++ b/src/runtime/CMakeLists.txt @@ -14,10 +14,10 @@ SET(segfault_core_src set (segfault_scene_src scene/camera.h scene/scene.h - scene/Scene2D.h - scene/Scene2D.cpp - scene/Scene2DManager.h - scene/Scene2DManager.cpp + scene/scene2d.h + scene/scene2d.cpp + scene/scene2dmanager.h + scene/scene2dmanager.cpp ) SET(segfault_renderer_src From 0793f629fb249e0122c4bf2f4259a3a35da70330 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 17 Oct 2026 18:26:54 +0200 Subject: [PATCH 13/18] fix typo --- src/contrib/vcpkg | 2 +- src/runtime/scene/{scene2D.cpp => scene2d.cpp} | 0 src/runtime/scene/{scene2D.h => scene2d.h} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename src/runtime/scene/{scene2D.cpp => scene2d.cpp} (100%) rename src/runtime/scene/{scene2D.h => scene2d.h} (100%) diff --git a/src/contrib/vcpkg b/src/contrib/vcpkg index e7d7451..f75c836 160000 --- a/src/contrib/vcpkg +++ b/src/contrib/vcpkg @@ -1 +1 @@ -Subproject commit e7d7451462697d77ef319ddf2da8ff7320a82662 +Subproject commit f75c836a67777a86a2c1116a28b179827f028b66 diff --git a/src/runtime/scene/scene2D.cpp b/src/runtime/scene/scene2d.cpp similarity index 100% rename from src/runtime/scene/scene2D.cpp rename to src/runtime/scene/scene2d.cpp diff --git a/src/runtime/scene/scene2D.h b/src/runtime/scene/scene2d.h similarity index 100% rename from src/runtime/scene/scene2D.h rename to src/runtime/scene/scene2d.h From 38f77804728c20e37e5ecaae360f8c20f121890a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 17 Oct 2026 18:36:36 +0200 Subject: [PATCH 14/18] Add scene grouping for vs --- src/runtime/CMakeLists.txt | 1 + src/runtime/scene/scene2d.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/runtime/CMakeLists.txt b/src/runtime/CMakeLists.txt index 7662bc7..2ac88bd 100644 --- a/src/runtime/CMakeLists.txt +++ b/src/runtime/CMakeLists.txt @@ -55,6 +55,7 @@ source_group(ai FILES ${segfault_ai_src} ) source_group(core FILES ${segfault_core_src} ) source_group(application FILES ${segfault_application_src} ) source_group(renderer FILES ${segfault_renderer_src} ) +source_group(scene FILES ${segfault_scene_src} ) ADD_LIBRARY(segfault_runtime SHARED ${segfault_ai_src} diff --git a/src/runtime/scene/scene2d.cpp b/src/runtime/scene/scene2d.cpp index 116edc5..61f0898 100644 --- a/src/runtime/scene/scene2d.cpp +++ b/src/runtime/scene/scene2d.cpp @@ -21,7 +21,7 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -----------------------------------------------------------------------------------------------*/ -#include "scene/Scene2D.h" +#include "scene/scene2d.h" #include "renderer/RHI.h" #include From 4f4fbf55ca32641c49f47617a246ea6655175835 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 17 Oct 2026 21:05:48 +0200 Subject: [PATCH 15/18] Fix uppercase --- src/runtime/scene/scene2dmanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/scene/scene2dmanager.cpp b/src/runtime/scene/scene2dmanager.cpp index 253a4a9..48700ab 100644 --- a/src/runtime/scene/scene2dmanager.cpp +++ b/src/runtime/scene/scene2dmanager.cpp @@ -21,8 +21,8 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -----------------------------------------------------------------------------------------------*/ -#include "scene/Scene2DManager.h" -#include "scene/Scene2D.h" +#include "scene/scene2dmanager.h" +#include "scene/scene2d.h" #include "renderer/RHI.h" #include From 5902141bcb08514be29af695589e4264f91ec363 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 17 Oct 2026 21:37:02 +0200 Subject: [PATCH 16/18] Fix uppercase --- src/runtime/scene/scene2dmanager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/scene/scene2dmanager.h b/src/runtime/scene/scene2dmanager.h index 34539d7..2b656f9 100644 --- a/src/runtime/scene/scene2dmanager.h +++ b/src/runtime/scene/scene2dmanager.h @@ -23,7 +23,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #pragma once #include "core/segfault.h" -#include "scene/Scene2D.h" +#include "scene/scene2d.h" #include #include From e68c315decb7384285c37fe1e009dc12409de5ea Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 17 Oct 2026 22:05:36 +0200 Subject: [PATCH 17/18] Remove unused stuff --- src/runtime/scene/scene2d.cpp | 51 ------------------------- src/runtime/scene/scene2d.h | 71 ----------------------------------- 2 files changed, 122 deletions(-) diff --git a/src/runtime/scene/scene2d.cpp b/src/runtime/scene/scene2d.cpp index 61f0898..a532e8e 100644 --- a/src/runtime/scene/scene2d.cpp +++ b/src/runtime/scene/scene2d.cpp @@ -802,55 +802,4 @@ namespace segfault::scene { return entity; } - //--------------------------------------------------------------------------------------------- - // UIScene2D implementation - //--------------------------------------------------------------------------------------------- - - UIScene2D::UIScene2D() { - // empty - } - - bool UIScene2D::init(renderer::RHI& rhi) { - if (!Scene2D::init(rhi)) { - return false; - } - return true; - } - - void UIScene2D::update(float deltaTime) { - Scene2D::update(deltaTime); - // Additional UI-specific updates - } - - void UIScene2D::onMouseMove(const glm::vec2& position) { - // Handle mouse move for UI elements - // This would typically check for hover states - } - - void UIScene2D::onMouseDown(const glm::vec2& position, uint8_t button) { - // Handle mouse down for UI elements - } - - void UIScene2D::onMouseUp(const glm::vec2& position, uint8_t button) { - // Handle mouse up for UI elements - } - - Entity2D UIScene2D::createText(const std::string& text, core::guid fontHandle, const glm::vec2& position, float fontSize, const glm::vec4& color) { - Entity2D entity = createEntity(); - - Transform2DComponent& transform = addTransformComponent(entity); - transform.setPosition(position); - - TextComponent& textComponent = addTextComponent(entity); - textComponent.setText(text); - textComponent.setFont(fontHandle); - textComponent.setFontSize(fontSize); - textComponent.setColor(color); - - Render2DComponent& render = addRenderComponent(entity); - render.setVisible(true); - - return entity; - } - } // namespace segfault::scene diff --git a/src/runtime/scene/scene2d.h b/src/runtime/scene/scene2d.h index 23ed976..90458da 100644 --- a/src/runtime/scene/scene2d.h +++ b/src/runtime/scene/scene2d.h @@ -891,75 +891,4 @@ namespace segfault::scene { Entity2D createButton(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size, std::function onClick); }; - //--------------------------------------------------------------------------------------------- - /// @brief UI Scene for user interface rendering. - /// - /// Specialized scene for UI elements with built-in input handling. - //--------------------------------------------------------------------------------------------- - class SEGFAULT_EXPORT UIScene2D : public Scene2D { - public: - /// @brief Constructs a new UIScene2D. - UIScene2D(); - - /// @brief Destroys the UIScene2D. - ~UIScene2D() = default; - - /// @brief Initializes the UI scene. - /// @param[ in ] rhi The rendering hardware interface. - /// @return True if initialization was successful, false otherwise. - bool init(renderer::RHI& rhi) override; - - /// @brief Updates the UI scene with input. - /// @param[ in ] deltaTime The time elapsed since the last frame. - void update(float deltaTime) override; - - // Input handling - virtual void onMouseMove(const glm::vec2& position); - virtual void onMouseDown(const glm::vec2& position, uint8_t button); - virtual void onMouseUp(const glm::vec2& position, uint8_t button); - - /// @brief Creates a UI panel. - /// @param[ in ] textureHandle The handle to the panel texture. - /// @param[ in ] position The position of the panel. - /// @param[ in ] size The size of the panel. - /// @return The created entity. - Entity2D createPanel(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size); - - /// @brief Creates a UI button. - /// @param[ in ] textureHandle The handle to the button texture. - /// @param[ in ] position The position of the button. - /// @param[ in ] size The size of the button. - /// @param[ in ] onClick The callback function when the button is clicked. - /// @return The created entity. - Entity2D createButton(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size, std::function onClick); - - /// @brief Creates a UI text element. - /// @param[ in ] text The text to display. - /// @param[ in ] fontHandle The handle to the font. - /// @param[ in ] position The position of the text. - /// @param[ in ] fontSize The font size. - /// @param[ in ] color The text color. - /// @return The created entity. - Entity2D createText(const std::string& text, core::guid fontHandle, const glm::vec2& position, float fontSize = 16.0f, const glm::vec4& color = glm::vec4(1.0f)); - - /// @brief Creates a UI checkbox. - /// @param[ in ] textureHandle The handle to the checkbox texture. - /// @param[ in ] position The position of the checkbox. - /// @param[ in ] size The size of the checkbox. - /// @param[ in ] onChanged The callback function when the checkbox state changes. - /// @return The created entity. - Entity2D createCheckbox(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size, std::function onChanged); - - /// @brief Creates a UI slider. - /// @param[ in ] backgroundTexture The handle to the background texture. - /// @param[ in ] handleTexture The handle to the handle texture. - /// @param[ in ] position The position of the slider. - /// @param[ in ] size The size of the slider. - /// @param[ in ] minValue The minimum value of the slider. - /// @param[ in ] maxValue The maximum value of the slider. - /// @param[ in ] onChanged The callback function when the slider value changes. - /// @return The created entity. - Entity2D createSlider(core::guid backgroundTexture, core::guid handleTexture, const glm::vec2& position, const glm::vec2& size, float minValue = 0.0f, float maxValue = 1.0f, std::function onChanged = nullptr); - }; - } // namespace segfault::scene From 5626ed542189af5d94a86b0955a311212a771782 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 17 Oct 2026 22:08:35 +0200 Subject: [PATCH 18/18] Remove unused stuff --- src/runtime/scene/scene2d.cpp | 17 ----------------- src/runtime/scene/scene2d.h | 18 +----------------- 2 files changed, 1 insertion(+), 34 deletions(-) diff --git a/src/runtime/scene/scene2d.cpp b/src/runtime/scene/scene2d.cpp index a532e8e..7838dbe 100644 --- a/src/runtime/scene/scene2d.cpp +++ b/src/runtime/scene/scene2d.cpp @@ -785,21 +785,4 @@ namespace segfault::scene { return entity; } - Entity2D SimpleScene2D::createText(const std::string& text, core::guid fontHandle, const glm::vec2& position, float fontSize) { - Entity2D entity = createEntity(); - - Transform2DComponent& transform = addTransformComponent(entity); - transform.setPosition(position); - - TextComponent& textComponent = addTextComponent(entity); - textComponent.setText(text); - textComponent.setFont(fontHandle); - textComponent.setFontSize(fontSize); - - Render2DComponent& render = addRenderComponent(entity); - render.setVisible(true); - - return entity; - } - } // namespace segfault::scene diff --git a/src/runtime/scene/scene2d.h b/src/runtime/scene/scene2d.h index 90458da..2b01676 100644 --- a/src/runtime/scene/scene2d.h +++ b/src/runtime/scene/scene2d.h @@ -372,7 +372,7 @@ namespace segfault::scene { /// @brief Constructs a sprite component with a texture. /// @param[ in ] textureHandle The handle to the texture. - SpriteComponent(core::guid textureHandle); + explicit SpriteComponent(core::guid textureHandle); /// @brief Sets the texture handle. /// @param[ in ] textureHandle The handle to the texture. @@ -873,22 +873,6 @@ namespace segfault::scene { /// @param[ in ] size The size of the sprite. /// @return The created entity. Entity2D createSprite(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size); - - /// @brief Creates a simple text entity. - /// @param[ in ] text The text to display. - /// @param[ in ] fontHandle The handle to the font. - /// @param[ in ] position The position of the text. - /// @param[ in ] fontSize The font size. - /// @return The created entity. - Entity2D createText(const std::string& text, core::guid fontHandle, const glm::vec2& position, float fontSize = 16.0f); - - /// @brief Creates a simple UI button entity. - /// @param[ in ] textureHandle The handle to the button texture. - /// @param[ in ] position The position of the button. - /// @param[ in ] size The size of the button. - /// @param[ in ] onClick The callback function when the button is clicked. - /// @return The created entity. - Entity2D createButton(core::guid textureHandle, const glm::vec2& position, const glm::vec2& size, std::function onClick); }; } // namespace segfault::scene