feat: Add service interfaces for audio, configuration, graphics, GUI, input, physics, scene, script, shader, and window management

This commit is contained in:
2026-01-04 02:36:14 +00:00
parent c95bcca25f
commit a84aa34681
10 changed files with 966 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
#pragma once
#include <filesystem>
namespace sdl3cpp::services {
/**
* @brief Audio playback service interface.
*
* Handles background music and sound effect playback using SDL audio.
* Wraps the AudioPlayer class with a clean service interface.
*/
class IAudioService {
public:
virtual ~IAudioService() = default;
/**
* @brief Initialize the audio subsystem.
*
* @throws std::runtime_error if audio initialization fails
*/
virtual void Initialize() = 0;
/**
* @brief Shutdown the audio subsystem and stop all playback.
*/
virtual void Shutdown() = 0;
/**
* @brief Play a background music track.
*
* Only one background track can play at a time. Calling this
* method will stop any currently playing background music.
*
* @param path Path to the audio file (OGG, WAV, etc.)
* @param loop Whether to loop the track
* @throws std::runtime_error if file cannot be loaded
*/
virtual void PlayBackground(const std::filesystem::path& path, bool loop = true) = 0;
/**
* @brief Play a sound effect.
*
* Multiple sound effects can play simultaneously.
*
* @param path Path to the audio file
* @param loop Whether to loop the effect (usually false)
* @throws std::runtime_error if file cannot be loaded
*/
virtual void PlayEffect(const std::filesystem::path& path, bool loop = false) = 0;
/**
* @brief Stop background music playback.
*/
virtual void StopBackground() = 0;
/**
* @brief Stop all audio playback (background and effects).
*/
virtual void StopAll() = 0;
/**
* @brief Set master volume.
*
* @param volume Volume level from 0.0 (silent) to 1.0 (full)
*/
virtual void SetVolume(float volume) = 0;
/**
* @brief Get the current master volume.
*
* @return Volume level from 0.0 to 1.0
*/
virtual float GetVolume() const = 0;
/**
* @brief Check if background music is currently playing.
*
* @return true if playing, false otherwise
*/
virtual bool IsBackgroundPlaying() const = 0;
};
} // namespace sdl3cpp::services

View File

@@ -0,0 +1,56 @@
#pragma once
#include <cstdint>
#include <filesystem>
#include <string>
namespace sdl3cpp::services {
/**
* @brief Configuration service interface.
*
* Provides access to application configuration loaded from JSON or defaults.
* Similar to Spring's @ConfigurationProperties.
*/
class IConfigService {
public:
virtual ~IConfigService() = default;
/**
* @brief Get the configured window width.
* @return Window width in pixels
*/
virtual uint32_t GetWindowWidth() const = 0;
/**
* @brief Get the configured window height.
* @return Window height in pixels
*/
virtual uint32_t GetWindowHeight() const = 0;
/**
* @brief Get the path to the Lua script to execute.
* @return Path to the script file
*/
virtual std::filesystem::path GetScriptPath() const = 0;
/**
* @brief Check if Lua debug mode is enabled.
* @return true if debug mode is enabled, false otherwise
*/
virtual bool IsLuaDebugEnabled() const = 0;
/**
* @brief Get the window title.
* @return Window title string
*/
virtual std::string GetWindowTitle() const = 0;
/**
* @brief Get required Vulkan device extensions.
* @return List of extension names
*/
virtual std::vector<const char*> GetDeviceExtensions() const = 0;
};
} // namespace sdl3cpp::services

View File

@@ -0,0 +1,184 @@
#pragma once
#include "../../core/vertex.hpp"
#include <array>
#include <cstdint>
#include <string>
#include <unordered_map>
#include <vector>
#include <vulkan/vulkan.h>
// Forward declare SDL type
struct SDL_Window;
namespace sdl3cpp::services {
/**
* @brief Graphics service configuration.
*/
struct GraphicsConfig {
std::vector<const char*> deviceExtensions;
VkFormat preferredFormat = VK_FORMAT_B8G8R8A8_SRGB;
bool enableValidationLayers = false;
};
/**
* @brief Shader file paths for a shader program.
*/
struct ShaderPaths {
std::string vertex;
std::string fragment;
};
/**
* @brief Render command for a single draw call.
*/
struct RenderCommand {
uint32_t indexOffset;
uint32_t indexCount;
int32_t vertexOffset;
std::string shaderKey;
std::array<float, 16> modelMatrix;
};
/**
* @brief Graphics service interface (Vulkan rendering).
*
* Abstracts all Vulkan rendering operations including device management,
* swapchain, pipelines, buffers, and rendering.
*
* This is the largest service, consolidating ~1,500 lines of Vulkan code
* from the original Sdl3App class.
*/
class IGraphicsService {
public:
virtual ~IGraphicsService() = default;
/**
* @brief Initialize the Vulkan instance, device, and queues.
*
* @param window The SDL window to create a surface for
* @param config Graphics configuration
* @throws std::runtime_error if device initialization fails
*/
virtual void InitializeDevice(SDL_Window* window, const GraphicsConfig& config) = 0;
/**
* @brief Initialize the swapchain for presenting rendered images.
*
* Must be called after InitializeDevice().
*
* @throws std::runtime_error if swapchain creation fails
*/
virtual void InitializeSwapchain() = 0;
/**
* @brief Recreate the swapchain (e.g., after window resize).
*
* @throws std::runtime_error if swapchain recreation fails
*/
virtual void RecreateSwapchain() = 0;
/**
* @brief Shutdown and release all Vulkan resources.
*/
virtual void Shutdown() = 0;
/**
* @brief Load and compile shader programs.
*
* @param shaders Map of shader key to shader file paths
* @throws std::runtime_error if shader compilation fails
*/
virtual void LoadShaders(const std::unordered_map<std::string, ShaderPaths>& shaders) = 0;
/**
* @brief Upload vertex data to GPU buffer.
*
* @param vertices Vector of vertex data
* @throws std::runtime_error if buffer creation fails
*/
virtual void UploadVertexData(const std::vector<core::Vertex>& vertices) = 0;
/**
* @brief Upload index data to GPU buffer.
*
* @param indices Vector of index data
* @throws std::runtime_error if buffer creation fails
*/
virtual void UploadIndexData(const std::vector<uint16_t>& indices) = 0;
/**
* @brief Begin a new frame and acquire the next swapchain image.
*
* @return true if successful, false if swapchain needs recreation
*/
virtual bool BeginFrame() = 0;
/**
* @brief Render the scene with the given render commands.
*
* @param commands List of render commands to execute
* @param viewProj View-projection matrix
*/
virtual void RenderScene(const std::vector<RenderCommand>& commands,
const std::array<float, 16>& viewProj) = 0;
/**
* @brief End the frame and present the rendered image.
*
* @return true if successful, false if swapchain needs recreation
*/
virtual bool EndFrame() = 0;
/**
* @brief Wait for all GPU operations to complete.
*
* Called before cleanup or when synchronization is needed.
*/
virtual void WaitIdle() = 0;
/**
* @brief Get the Vulkan logical device.
*
* @return VkDevice handle (needed for GUI renderer, etc.)
*/
virtual VkDevice GetDevice() const = 0;
/**
* @brief Get the Vulkan physical device.
*
* @return VkPhysicalDevice handle
*/
virtual VkPhysicalDevice GetPhysicalDevice() const = 0;
/**
* @brief Get the current swapchain extent (framebuffer size).
*
* @return VkExtent2D with width and height
*/
virtual VkExtent2D GetSwapchainExtent() const = 0;
/**
* @brief Get the swapchain image format.
*
* @return VkFormat of swapchain images
*/
virtual VkFormat GetSwapchainFormat() const = 0;
/**
* @brief Get the current command buffer being recorded.
*
* @return VkCommandBuffer (needed for GUI rendering)
*/
virtual VkCommandBuffer GetCurrentCommandBuffer() const = 0;
/**
* @brief Get the graphics queue.
*
* @return VkQueue handle
*/
virtual VkQueue GetGraphicsQueue() const = 0;
};
} // namespace sdl3cpp::services

View File

@@ -0,0 +1,77 @@
#pragma once
#include <cstdint>
#include <filesystem>
#include <vector>
#include <vulkan/vulkan.h>
namespace sdl3cpp::services {
// Forward declare GUI types
struct GuiCommand;
/**
* @brief GUI rendering service interface.
*
* Handles 2D GUI overlay rendering using Vulkan.
* Wraps the GuiRenderer class with support for SVG, text, and shapes.
*/
class IGuiService {
public:
virtual ~IGuiService() = default;
/**
* @brief Initialize the GUI renderer.
*
* @param device Vulkan logical device
* @param physicalDevice Vulkan physical device
* @param format Swapchain image format
* @param resourcePath Path to GUI resources (fonts, SVG files, etc.)
* @throws std::runtime_error if initialization fails
*/
virtual void Initialize(VkDevice device,
VkPhysicalDevice physicalDevice,
VkFormat format,
const std::filesystem::path& resourcePath) = 0;
/**
* @brief Prepare GUI commands for rendering.
*
* Processes GUI commands from Lua and prepares rendering data.
*
* @param commands Vector of GUI commands to render
* @param width Viewport width in pixels
* @param height Viewport height in pixels
*/
virtual void PrepareFrame(const std::vector<GuiCommand>& commands,
uint32_t width,
uint32_t height) = 0;
/**
* @brief Render GUI overlay to swapchain image.
*
* Records rendering commands into the provided command buffer.
*
* @param commandBuffer Vulkan command buffer to record into
* @param image Target swapchain image
*/
virtual void RenderToSwapchain(VkCommandBuffer commandBuffer, VkImage image) = 0;
/**
* @brief Handle window resize.
*
* Recreates internal resources for the new window size.
*
* @param width New width in pixels
* @param height New height in pixels
* @param format Swapchain image format
*/
virtual void Resize(uint32_t width, uint32_t height, VkFormat format) = 0;
/**
* @brief Shutdown and release GPU resources.
*/
virtual void Shutdown() = 0;
};
} // namespace sdl3cpp::services

View File

@@ -0,0 +1,81 @@
#pragma once
#include <string>
#include <unordered_set>
#include <SDL3/SDL.h>
namespace sdl3cpp::services {
/**
* @brief Input state snapshot for a single frame.
*/
struct InputState {
float mouseX = 0.0f;
float mouseY = 0.0f;
float mouseWheelDeltaX = 0.0f;
float mouseWheelDeltaY = 0.0f;
std::unordered_set<SDL_Keycode> keysPressed;
std::unordered_set<uint8_t> mouseButtonsPressed;
std::string textInput;
};
/**
* @brief Input handling service interface.
*
* Subscribes to input events from the event bus and maintains
* the current input state for queries by other services.
*/
class IInputService {
public:
virtual ~IInputService() = default;
/**
* @brief Process an SDL event and update input state.
*
* Called by the window service when events are polled.
* Updates internal state and publishes events to the event bus.
*
* @param event The SDL event to process
*/
virtual void ProcessEvent(const SDL_Event& event) = 0;
/**
* @brief Reset per-frame input state.
*
* Called at the beginning of each frame to clear transient state
* like mouse wheel delta and text input.
*/
virtual void ResetFrameState() = 0;
/**
* @brief Get the current input state.
*
* @return Reference to the current input state
*/
virtual const InputState& GetState() const = 0;
/**
* @brief Check if a key is currently pressed.
*
* @param key The SDL keycode to check
* @return true if the key is pressed, false otherwise
*/
virtual bool IsKeyPressed(SDL_Keycode key) const = 0;
/**
* @brief Check if a mouse button is currently pressed.
*
* @param button The mouse button (SDL_BUTTON_LEFT, SDL_BUTTON_RIGHT, etc.)
* @return true if the button is pressed, false otherwise
*/
virtual bool IsMouseButtonPressed(uint8_t button) const = 0;
/**
* @brief Get the current mouse position.
*
* @return Pair of (x, y) coordinates in pixels
*/
virtual std::pair<float, float> GetMousePosition() const = 0;
};
} // namespace sdl3cpp::services

View File

@@ -0,0 +1,133 @@
#pragma once
#include <string>
#include <LinearMath/btVector3.h>
#include <LinearMath/btTransform.h>
namespace sdl3cpp::services {
/**
* @brief Physics simulation service interface.
*
* Provides rigid body physics simulation using Bullet Physics.
* Wraps the PhysicsBridge class with a clean service interface.
*/
class IPhysicsService {
public:
virtual ~IPhysicsService() = default;
/**
* @brief Initialize the physics world.
*
* @param gravity Gravity vector (default: {0, -9.8, 0})
*/
virtual void Initialize(const btVector3& gravity = btVector3(0, -9.8f, 0)) = 0;
/**
* @brief Shutdown and release physics resources.
*/
virtual void Shutdown() = 0;
/**
* @brief Add a box-shaped rigid body to the simulation.
*
* @param name Unique identifier for this body
* @param halfExtents Half-extents of the box (half width/height/depth)
* @param mass Mass in kg (0 for static objects)
* @param transform Initial position and rotation
* @return true if added successfully, false if name already exists
*/
virtual bool AddBoxRigidBody(const std::string& name,
const btVector3& halfExtents,
float mass,
const btTransform& transform) = 0;
/**
* @brief Add a sphere-shaped rigid body to the simulation.
*
* @param name Unique identifier for this body
* @param radius Sphere radius
* @param mass Mass in kg (0 for static objects)
* @param transform Initial position and rotation
* @return true if added successfully, false if name already exists
*/
virtual bool AddSphereRigidBody(const std::string& name,
float radius,
float mass,
const btTransform& transform) = 0;
/**
* @brief Remove a rigid body from the simulation.
*
* @param name Identifier of the body to remove
* @return true if removed, false if not found
*/
virtual bool RemoveRigidBody(const std::string& name) = 0;
/**
* @brief Step the physics simulation forward in time.
*
* @param deltaTime Time step in seconds (typically 1/60)
* @param maxSubSteps Maximum number of sub-steps for stability
*/
virtual void StepSimulation(float deltaTime, int maxSubSteps = 10) = 0;
/**
* @brief Get the current transform of a rigid body.
*
* @param name Identifier of the body
* @param outTransform Output parameter for the transform
* @return true if body exists, false otherwise
*/
virtual bool GetTransform(const std::string& name, btTransform& outTransform) const = 0;
/**
* @brief Set the transform of a rigid body.
*
* @param name Identifier of the body
* @param transform New position and rotation
* @return true if body exists, false otherwise
*/
virtual bool SetTransform(const std::string& name, const btTransform& transform) = 0;
/**
* @brief Apply a force to a rigid body.
*
* @param name Identifier of the body
* @param force Force vector in Newtons
* @return true if body exists, false otherwise
*/
virtual bool ApplyForce(const std::string& name, const btVector3& force) = 0;
/**
* @brief Apply an impulse to a rigid body.
*
* @param name Identifier of the body
* @param impulse Impulse vector
* @return true if body exists, false otherwise
*/
virtual bool ApplyImpulse(const std::string& name, const btVector3& impulse) = 0;
/**
* @brief Set the linear velocity of a rigid body.
*
* @param name Identifier of the body
* @param velocity Velocity vector in m/s
* @return true if body exists, false otherwise
*/
virtual bool SetLinearVelocity(const std::string& name, const btVector3& velocity) = 0;
/**
* @brief Get the number of rigid bodies in the simulation.
*
* @return Body count
*/
virtual size_t GetBodyCount() const = 0;
/**
* @brief Clear all rigid bodies from the simulation.
*/
virtual void Clear() = 0;
};
} // namespace sdl3cpp::services

View File

@@ -0,0 +1,58 @@
#pragma once
#include "i_graphics_service.hpp"
#include "i_script_service.hpp"
#include <vector>
namespace sdl3cpp::services {
/**
* @brief Scene management service interface.
*
* Maintains the scene graph and generates render commands for the graphics service.
* Separated from script service to decouple scene state from Lua execution.
*/
class ISceneService {
public:
virtual ~ISceneService() = default;
/**
* @brief Load a scene from scene objects.
*
* Replaces the current scene with new objects.
*
* @param objects Vector of scene objects to load
*/
virtual void LoadScene(const std::vector<SceneObject>& objects) = 0;
/**
* @brief Update scene state (animations, transformations, etc.).
*
* @param deltaTime Time since last update in seconds
*/
virtual void UpdateScene(float deltaTime) = 0;
/**
* @brief Generate render commands for the current scene.
*
* Evaluates model matrices and prepares draw calls.
*
* @param time Current time in seconds
* @return Vector of render commands for the graphics service
*/
virtual std::vector<RenderCommand> GetRenderCommands(float time) const = 0;
/**
* @brief Clear the scene (remove all objects).
*/
virtual void Clear() = 0;
/**
* @brief Get the number of objects in the scene.
*
* @return Object count
*/
virtual size_t GetObjectCount() const = 0;
};
} // namespace sdl3cpp::services

View File

@@ -0,0 +1,126 @@
#pragma once
#include "../../core/vertex.hpp"
#include "i_graphics_service.hpp"
#include <array>
#include <cstdint>
#include <filesystem>
#include <string>
#include <unordered_map>
#include <vector>
namespace sdl3cpp::services {
/**
* @brief Scene object data structure.
*/
struct SceneObject {
std::string id;
std::vector<core::Vertex> vertices;
std::vector<uint16_t> indices;
std::string shaderKey;
int modelMatrixFuncRef; // Lua function reference
};
/**
* @brief GUI input snapshot for Lua.
*/
struct GuiInputSnapshot {
float mouseX;
float mouseY;
float mouseWheelDelta;
bool mouseLeftPressed;
bool mouseRightPressed;
std::string textInput;
};
// Forward declare GUI types (defined in script/gui_types.hpp)
struct GuiCommand;
/**
* @brief Script service interface (Lua integration).
*
* Provides a clean API for Lua script execution and data exchange.
* Wraps the ScriptEngine and its internal managers (SceneManager,
* ShaderManager, GuiManager, AudioManager).
*/
class IScriptService {
public:
virtual ~IScriptService() = default;
/**
* @brief Load and execute a Lua script.
*
* @param path Path to the Lua script file
* @param debug Whether to enable Lua debug mode
* @throws std::runtime_error if script loading fails
*/
virtual void LoadScript(const std::filesystem::path& path, bool debug) = 0;
/**
* @brief Load scene objects from Lua.
*
* Calls the Lua function that generates the scene graph.
*
* @return Vector of scene objects with geometry and metadata
*/
virtual std::vector<SceneObject> LoadSceneObjects() = 0;
/**
* @brief Compute model matrix for an object using Lua function.
*
* @param funcRef Lua function reference (from SceneObject)
* @param time Current time in seconds
* @return 4x4 model transformation matrix (column-major)
*/
virtual std::array<float, 16> ComputeModelMatrix(int funcRef, float time) = 0;
/**
* @brief Get view-projection matrix from Lua.
*
* @param aspect Window aspect ratio (width/height)
* @return 4x4 view-projection matrix (column-major)
*/
virtual std::array<float, 16> GetViewProjectionMatrix(float aspect) = 0;
/**
* @brief Load shader paths from Lua configuration.
*
* @return Map of shader key to shader file paths
*/
virtual std::unordered_map<std::string, ShaderPaths> LoadShaderPaths() = 0;
/**
* @brief Update GUI input state in Lua.
*
* Sends current input state to Lua for GUI processing.
*
* @param input Input snapshot for this frame
*/
virtual void UpdateGuiInput(const GuiInputSnapshot& input) = 0;
/**
* @brief Get GUI rendering commands from Lua.
*
* Retrieves the list of GUI commands generated by Lua scripts.
*
* @return Vector of GUI commands to render
*/
virtual std::vector<GuiCommand> GetGuiCommands() = 0;
/**
* @brief Check if there are pending GUI commands.
*
* @return true if GUI commands are available, false otherwise
*/
virtual bool HasGuiCommands() const = 0;
/**
* @brief Update physics simulation (calls Lua if needed).
*
* @param deltaTime Time since last update in seconds
*/
virtual void UpdatePhysics(float deltaTime) = 0;
};
} // namespace sdl3cpp::services

View File

@@ -0,0 +1,72 @@
#pragma once
#include "i_graphics_service.hpp"
#include <string>
#include <unordered_map>
#include <vulkan/vulkan.h>
namespace sdl3cpp::services {
/**
* @brief Shader management service interface.
*
* Handles shader compilation, pipeline creation, and shader program management.
* Wraps the ShaderManager from the script module.
*/
class IShaderService {
public:
virtual ~IShaderService() = default;
/**
* @brief Register a shader program.
*
* @param key Unique identifier for this shader program
* @param paths Vertex and fragment shader file paths
* @throws std::runtime_error if shader files don't exist
*/
virtual void RegisterShader(const std::string& key, const ShaderPaths& paths) = 0;
/**
* @brief Compile all registered shaders and create pipelines.
*
* Must be called after all shaders are registered and before rendering.
*
* @throws std::runtime_error if compilation fails
*/
virtual void CompileAll() = 0;
/**
* @brief Get the Vulkan pipeline for a shader program.
*
* @param key Shader program identifier
* @return VkPipeline handle
* @throws std::out_of_range if shader key doesn't exist
*/
virtual VkPipeline GetPipeline(const std::string& key) const = 0;
/**
* @brief Check if a shader program exists.
*
* @param key Shader program identifier
* @return true if registered, false otherwise
*/
virtual bool HasShader(const std::string& key) const = 0;
/**
* @brief Get the number of registered shader programs.
*
* @return Shader count
*/
virtual size_t GetShaderCount() const = 0;
/**
* @brief Reload all shaders (for hot-reload support).
*
* Recompiles all shaders from their source files.
*
* @throws std::runtime_error if compilation fails
*/
virtual void ReloadAll() = 0;
};
} // namespace sdl3cpp::services

View File

@@ -0,0 +1,95 @@
#pragma once
#include <cstdint>
#include <string>
#include <utility>
// Forward declare SDL types to avoid including SDL headers
struct SDL_Window;
namespace sdl3cpp::services {
/**
* @brief Window configuration structure.
*/
struct WindowConfig {
uint32_t width;
uint32_t height;
std::string title;
bool resizable;
};
/**
* @brief Window management service interface.
*
* Handles SDL window creation, event polling, and window state management.
* Decouples SDL-specific details from the rest of the application.
*/
class IWindowService {
public:
virtual ~IWindowService() = default;
/**
* @brief Create and show the application window.
*
* @param config Window configuration (size, title, flags)
* @throws std::runtime_error if window creation fails
*/
virtual void CreateWindow(const WindowConfig& config) = 0;
/**
* @brief Destroy the window and release resources.
*/
virtual void DestroyWindow() = 0;
/**
* @brief Get the native SDL window handle.
*
* Required for Vulkan surface creation and other low-level operations.
*
* @return Pointer to SDL_Window, or nullptr if no window exists
*/
virtual SDL_Window* GetNativeHandle() const = 0;
/**
* @brief Get the current window size.
*
* @return Pair of (width, height) in pixels
*/
virtual std::pair<uint32_t, uint32_t> GetSize() const = 0;
/**
* @brief Check if the window should close.
*
* Returns true when the user clicks the close button or
* a quit event is received.
*
* @return true if the application should exit, false otherwise
*/
virtual bool ShouldClose() const = 0;
/**
* @brief Poll SDL events and publish them to the event bus.
*
* This should be called once per frame to process user input
* and window events. Events are converted to application events
* and published via the event bus.
*/
virtual void PollEvents() = 0;
/**
* @brief Set the window title.
*
* @param title New window title
*/
virtual void SetTitle(const std::string& title) = 0;
/**
* @brief Check if the window is minimized.
*
* @return true if minimized, false otherwise
*/
virtual bool IsMinimized() const = 0;
};
} // namespace sdl3cpp::services