Add SDL2+OpenGL ES 2 renderer backend for Emscripten/WebGL
Implements Phase 1 of renderer abstraction plan: - SDL2Types.h: SFML-compatible type stubs in sf:: namespace - SDL2Renderer.h/cpp: OpenGL ES 2 rendering implementation - EmscriptenStubs.cpp: Stubs for missing POSIX functions (wait3, wait4, wcsftime) Build system changes: - Add MCRF_SDL2 compile-time backend selection - Add Emscripten SDL2 link options (-sUSE_SDL=2, -sFULL_ES2=1) - Fix LONG_BIT mismatch for Emscripten in pyport.h Code changes for SDL2/headless compatibility: - Guard ImGui includes with !MCRF_HEADLESS && !MCRF_SDL2 - Defer GL shader initialization until after context creation Current status: Python runs in browser, rendering WIP (canvas sizing issues) Build commands: emcmake cmake -DMCRF_SDL2=ON -B build-emscripten emmake make -C build-emscripten Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
8c3128e29c
commit
c5cc022aa2
13 changed files with 2665 additions and 34 deletions
|
|
@ -11,10 +11,22 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
# Headless build option (no SFML, no graphics - for server/testing/Emscripten prep)
|
# Headless build option (no SFML, no graphics - for server/testing/Emscripten prep)
|
||||||
option(MCRF_HEADLESS "Build without graphics dependencies (SFML, ImGui)" OFF)
|
option(MCRF_HEADLESS "Build without graphics dependencies (SFML, ImGui)" OFF)
|
||||||
|
|
||||||
# Emscripten builds are always headless (no SFML yet - using stubs)
|
# SDL2 backend option (SDL2 + OpenGL ES 2 - for Emscripten/WebGL, Android, cross-platform)
|
||||||
|
option(MCRF_SDL2 "Build with SDL2+OpenGL ES 2 backend instead of SFML" OFF)
|
||||||
|
|
||||||
|
# Emscripten builds: use SDL2 if specified, otherwise fall back to headless
|
||||||
if(EMSCRIPTEN)
|
if(EMSCRIPTEN)
|
||||||
|
if(MCRF_SDL2)
|
||||||
|
message(STATUS "Emscripten detected - using SDL2 backend")
|
||||||
|
set(MCRF_HEADLESS OFF)
|
||||||
|
else()
|
||||||
set(MCRF_HEADLESS ON)
|
set(MCRF_HEADLESS ON)
|
||||||
message(STATUS "Emscripten detected - forcing HEADLESS mode")
|
message(STATUS "Emscripten detected - forcing HEADLESS mode (use -DMCRF_SDL2=ON for graphics)")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(MCRF_SDL2)
|
||||||
|
message(STATUS "Building with SDL2 backend - SDL2+OpenGL ES 2")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MCRF_HEADLESS)
|
if(MCRF_HEADLESS)
|
||||||
|
|
@ -36,9 +48,13 @@ if(EMSCRIPTEN)
|
||||||
# Emscripten build: use Python headers compiled for wasm32-emscripten
|
# Emscripten build: use Python headers compiled for wasm32-emscripten
|
||||||
# The pyconfig.h from cross-build has correct LONG_BIT and other settings
|
# The pyconfig.h from cross-build has correct LONG_BIT and other settings
|
||||||
set(PYTHON_WASM_BUILD "${CMAKE_SOURCE_DIR}/deps/cpython/cross-build/wasm32-emscripten/build/python")
|
set(PYTHON_WASM_BUILD "${CMAKE_SOURCE_DIR}/deps/cpython/cross-build/wasm32-emscripten/build/python")
|
||||||
|
# Force-include wasm pyconfig.h BEFORE anything else to set correct platform defines
|
||||||
add_compile_options(-include ${PYTHON_WASM_BUILD}/pyconfig.h)
|
add_compile_options(-include ${PYTHON_WASM_BUILD}/pyconfig.h)
|
||||||
|
# Override LONG_BIT - Emscripten's limits.h incorrectly defines it as 64 for wasm32
|
||||||
|
add_compile_definitions(LONG_BIT=32)
|
||||||
|
# Include wasm build directory FIRST so its pyconfig.h is found by #include "pyconfig.h"
|
||||||
|
include_directories(BEFORE ${PYTHON_WASM_BUILD})
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/deps/cpython/Include)
|
include_directories(${CMAKE_SOURCE_DIR}/deps/cpython/Include)
|
||||||
include_directories(${PYTHON_WASM_BUILD}) # For generated headers
|
|
||||||
message(STATUS "Using Emscripten Python from: ${PYTHON_WASM_BUILD}")
|
message(STATUS "Using Emscripten Python from: ${PYTHON_WASM_BUILD}")
|
||||||
elseif(MCRF_CROSS_WINDOWS)
|
elseif(MCRF_CROSS_WINDOWS)
|
||||||
# Windows cross-compilation: use cpython headers with PC/pyconfig.h
|
# Windows cross-compilation: use cpython headers with PC/pyconfig.h
|
||||||
|
|
@ -57,8 +73,9 @@ else()
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/deps/Python)
|
include_directories(${CMAKE_SOURCE_DIR}/deps/Python)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ImGui and ImGui-SFML include directories (not needed in headless mode)
|
# ImGui and ImGui-SFML include directories (not needed in headless or SDL2 mode)
|
||||||
if(NOT MCRF_HEADLESS)
|
# SDL2 builds will use ImGui with SDL2 backend later; for now, no ImGui
|
||||||
|
if(NOT MCRF_HEADLESS AND NOT MCRF_SDL2)
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/modules/imgui)
|
include_directories(${CMAKE_SOURCE_DIR}/modules/imgui)
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/modules/imgui-sfml)
|
include_directories(${CMAKE_SOURCE_DIR}/modules/imgui-sfml)
|
||||||
|
|
||||||
|
|
@ -75,13 +92,14 @@ endif()
|
||||||
# Collect all the source files
|
# Collect all the source files
|
||||||
file(GLOB_RECURSE SOURCES "src/*.cpp")
|
file(GLOB_RECURSE SOURCES "src/*.cpp")
|
||||||
|
|
||||||
# Add ImGui sources to the build (only if not headless)
|
# Add ImGui sources to the build (only if using SFML)
|
||||||
if(NOT MCRF_HEADLESS)
|
if(NOT MCRF_HEADLESS AND NOT MCRF_SDL2)
|
||||||
list(APPEND SOURCES ${IMGUI_SOURCES})
|
list(APPEND SOURCES ${IMGUI_SOURCES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Find OpenGL (required by ImGui-SFML) - not needed in headless mode
|
# Find OpenGL (required by ImGui-SFML) - not needed in headless mode
|
||||||
if(NOT MCRF_HEADLESS)
|
# SDL2 builds handle OpenGL ES 2 differently (via SDL2 or Emscripten)
|
||||||
|
if(NOT MCRF_HEADLESS AND NOT MCRF_SDL2)
|
||||||
if(MCRF_CROSS_WINDOWS)
|
if(MCRF_CROSS_WINDOWS)
|
||||||
# For cross-compilation, OpenGL is provided by MinGW
|
# For cross-compilation, OpenGL is provided by MinGW
|
||||||
set(OPENGL_LIBRARIES opengl32)
|
set(OPENGL_LIBRARIES opengl32)
|
||||||
|
|
@ -109,8 +127,27 @@ if(EMSCRIPTEN)
|
||||||
${LIBTCOD_WASM_BUILD}/_deps/lodepng-c-build/liblodepng-c.a
|
${LIBTCOD_WASM_BUILD}/_deps/lodepng-c-build/liblodepng-c.a
|
||||||
${LIBTCOD_WASM_BUILD}/_deps/utf8proc-build/libutf8proc.a)
|
${LIBTCOD_WASM_BUILD}/_deps/utf8proc-build/libutf8proc.a)
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/deps/platform/linux) # Use Linux platform stubs for now
|
include_directories(${CMAKE_SOURCE_DIR}/deps/platform/linux) # Use Linux platform stubs for now
|
||||||
|
# For SDL2 builds, add stb headers for image/font loading
|
||||||
|
if(MCRF_SDL2)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/deps/stb)
|
||||||
|
endif()
|
||||||
message(STATUS "Linking Emscripten Python: ${PYTHON_WASM_BUILD}/libpython3.14.a")
|
message(STATUS "Linking Emscripten Python: ${PYTHON_WASM_BUILD}/libpython3.14.a")
|
||||||
message(STATUS "Linking Emscripten libtcod: ${LIBTCOD_WASM_BUILD}/libtcod.a")
|
message(STATUS "Linking Emscripten libtcod: ${LIBTCOD_WASM_BUILD}/libtcod.a")
|
||||||
|
elseif(MCRF_SDL2)
|
||||||
|
# SDL2 build (non-Emscripten): link against SDL2 and system libraries
|
||||||
|
# Note: For desktop SDL2 builds in the future
|
||||||
|
find_package(SDL2 REQUIRED)
|
||||||
|
find_package(OpenGL REQUIRED)
|
||||||
|
set(LINK_LIBS
|
||||||
|
SDL2::SDL2
|
||||||
|
OpenGL::GL
|
||||||
|
tcod
|
||||||
|
python3.14
|
||||||
|
m dl util pthread)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/deps/platform/linux)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/deps/stb) # stb_image.h, stb_truetype.h
|
||||||
|
link_directories(${CMAKE_SOURCE_DIR}/__lib)
|
||||||
|
message(STATUS "Building with SDL2 backend (desktop)")
|
||||||
elseif(MCRF_HEADLESS)
|
elseif(MCRF_HEADLESS)
|
||||||
# Headless build: no SFML, no OpenGL
|
# Headless build: no SFML, no OpenGL
|
||||||
if(WIN32 OR MCRF_CROSS_WINDOWS)
|
if(WIN32 OR MCRF_CROSS_WINDOWS)
|
||||||
|
|
@ -195,6 +232,8 @@ endif()
|
||||||
add_executable(mcrogueface ${SOURCES})
|
add_executable(mcrogueface ${SOURCES})
|
||||||
|
|
||||||
# Define NO_SDL for libtcod-headless headers (excludes SDL-dependent code)
|
# Define NO_SDL for libtcod-headless headers (excludes SDL-dependent code)
|
||||||
|
# We ALWAYS need this because libtcod headers expect SDL3, not SDL2
|
||||||
|
# Our SDL2 backend is separate from libtcod's SDL3 renderer
|
||||||
target_compile_definitions(mcrogueface PRIVATE NO_SDL)
|
target_compile_definitions(mcrogueface PRIVATE NO_SDL)
|
||||||
|
|
||||||
# Define MCRF_HEADLESS for headless builds (excludes SFML/ImGui code)
|
# Define MCRF_HEADLESS for headless builds (excludes SFML/ImGui code)
|
||||||
|
|
@ -202,9 +241,15 @@ if(MCRF_HEADLESS)
|
||||||
target_compile_definitions(mcrogueface PRIVATE MCRF_HEADLESS)
|
target_compile_definitions(mcrogueface PRIVATE MCRF_HEADLESS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Define MCRF_SDL2 for SDL2 builds (uses SDL2+OpenGL ES 2 instead of SFML)
|
||||||
|
if(MCRF_SDL2)
|
||||||
|
target_compile_definitions(mcrogueface PRIVATE MCRF_SDL2)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Emscripten-specific link options (use ports for zlib, bzip2, sqlite3)
|
# Emscripten-specific link options (use ports for zlib, bzip2, sqlite3)
|
||||||
if(EMSCRIPTEN)
|
if(EMSCRIPTEN)
|
||||||
target_link_options(mcrogueface PRIVATE
|
# Base Emscripten options
|
||||||
|
set(EMSCRIPTEN_LINK_OPTIONS
|
||||||
-sUSE_ZLIB=1
|
-sUSE_ZLIB=1
|
||||||
-sUSE_BZIP2=1
|
-sUSE_BZIP2=1
|
||||||
-sUSE_SQLITE3=1
|
-sUSE_SQLITE3=1
|
||||||
|
|
@ -215,6 +260,9 @@ if(EMSCRIPTEN)
|
||||||
-sSTACK_OVERFLOW_CHECK=2
|
-sSTACK_OVERFLOW_CHECK=2
|
||||||
-fexceptions
|
-fexceptions
|
||||||
-sNO_DISABLE_EXCEPTION_CATCHING
|
-sNO_DISABLE_EXCEPTION_CATCHING
|
||||||
|
# Disable features that require dynamic linking support
|
||||||
|
-sERROR_ON_UNDEFINED_SYMBOLS=0
|
||||||
|
-sALLOW_UNIMPLEMENTED_SYSCALLS=1
|
||||||
# Preload Python stdlib into virtual filesystem at /lib/python3.14
|
# Preload Python stdlib into virtual filesystem at /lib/python3.14
|
||||||
--preload-file=${CMAKE_SOURCE_DIR}/wasm_stdlib/lib@/lib
|
--preload-file=${CMAKE_SOURCE_DIR}/wasm_stdlib/lib@/lib
|
||||||
# Preload game scripts into /scripts
|
# Preload game scripts into /scripts
|
||||||
|
|
@ -222,6 +270,24 @@ if(EMSCRIPTEN)
|
||||||
# Preload assets
|
# Preload assets
|
||||||
--preload-file=${CMAKE_SOURCE_DIR}/assets@/assets
|
--preload-file=${CMAKE_SOURCE_DIR}/assets@/assets
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add SDL2 options if using SDL2 backend
|
||||||
|
if(MCRF_SDL2)
|
||||||
|
list(APPEND EMSCRIPTEN_LINK_OPTIONS
|
||||||
|
-sUSE_SDL=2
|
||||||
|
-sFULL_ES2=1
|
||||||
|
-sMIN_WEBGL_VERSION=2
|
||||||
|
-sMAX_WEBGL_VERSION=2
|
||||||
|
)
|
||||||
|
# SDL2 flags are also needed at compile time for headers
|
||||||
|
target_compile_options(mcrogueface PRIVATE
|
||||||
|
-sUSE_SDL=2
|
||||||
|
)
|
||||||
|
message(STATUS "Emscripten SDL2 options enabled: -sUSE_SDL=2 -sFULL_ES2=1")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_options(mcrogueface PRIVATE ${EMSCRIPTEN_LINK_OPTIONS})
|
||||||
|
|
||||||
# Set Python home for the embedded interpreter
|
# Set Python home for the embedded interpreter
|
||||||
target_compile_definitions(mcrogueface PRIVATE
|
target_compile_definitions(mcrogueface PRIVATE
|
||||||
MCRF_WASM_PYTHON_HOME="/lib/python3.14"
|
MCRF_WASM_PYTHON_HOME="/lib/python3.14"
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,15 @@
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
#ifdef MCRF_HEADLESS
|
#ifdef MCRF_HEADLESS
|
||||||
// Use headless type stubs instead of SFML
|
// Use headless type stubs instead of SFML (no graphics, for CI/testing)
|
||||||
#include "platform/HeadlessTypes.h"
|
#include "platform/HeadlessTypes.h"
|
||||||
#define MCRF_GRAPHICS_BACKEND "headless"
|
#define MCRF_GRAPHICS_BACKEND "headless"
|
||||||
|
#elif defined(MCRF_SDL2)
|
||||||
|
// Use SDL2 + OpenGL ES 2 backend (for Emscripten/WebGL, Android, cross-platform)
|
||||||
|
#include "platform/SDL2Types.h"
|
||||||
|
#define MCRF_GRAPHICS_BACKEND "sdl2"
|
||||||
#else
|
#else
|
||||||
// Use SFML for graphics and audio
|
// Use SFML for graphics and audio (default desktop build)
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
#include <SFML/Audio.hpp>
|
#include <SFML/Audio.hpp>
|
||||||
#define MCRF_GRAPHICS_BACKEND "sfml"
|
#define MCRF_GRAPHICS_BACKEND "sfml"
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
#include "Animation.h"
|
#include "Animation.h"
|
||||||
#include "Timer.h"
|
#include "Timer.h"
|
||||||
#include "BenchmarkLogger.h"
|
#include "BenchmarkLogger.h"
|
||||||
#ifndef MCRF_HEADLESS
|
// ImGui is only available for SFML builds (not headless, not SDL2)
|
||||||
|
#if !defined(MCRF_HEADLESS) && !defined(MCRF_SDL2)
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "imgui-SFML.h"
|
#include "imgui-SFML.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -86,8 +87,8 @@ GameEngine::GameEngine(const McRogueFaceConfig& cfg)
|
||||||
window->setFramerateLimit(60);
|
window->setFramerateLimit(60);
|
||||||
render_target = window.get();
|
render_target = window.get();
|
||||||
|
|
||||||
#ifndef MCRF_HEADLESS
|
#if !defined(MCRF_HEADLESS) && !defined(MCRF_SDL2)
|
||||||
// Initialize ImGui for the window
|
// Initialize ImGui for the window (SFML builds only)
|
||||||
if (ImGui::SFML::Init(*window)) {
|
if (ImGui::SFML::Init(*window)) {
|
||||||
imguiInitialized = true;
|
imguiInitialized = true;
|
||||||
// Register settings handler before .ini is loaded (happens on first frame)
|
// Register settings handler before .ini is loaded (happens on first frame)
|
||||||
|
|
@ -199,7 +200,7 @@ void GameEngine::cleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown ImGui AFTER window is closed to avoid X11 BadCursor errors
|
// Shutdown ImGui AFTER window is closed to avoid X11 BadCursor errors
|
||||||
#ifndef MCRF_HEADLESS
|
#if !defined(MCRF_HEADLESS) && !defined(MCRF_SDL2)
|
||||||
if (imguiInitialized) {
|
if (imguiInitialized) {
|
||||||
ImGui::SFML::Shutdown();
|
ImGui::SFML::Shutdown();
|
||||||
imguiInitialized = false;
|
imguiInitialized = false;
|
||||||
|
|
@ -361,8 +362,8 @@ void GameEngine::doFrame()
|
||||||
if (!headless) {
|
if (!headless) {
|
||||||
sUserInput();
|
sUserInput();
|
||||||
|
|
||||||
#ifndef MCRF_HEADLESS
|
#if !defined(MCRF_HEADLESS) && !defined(MCRF_SDL2)
|
||||||
// Update ImGui
|
// Update ImGui (SFML builds only)
|
||||||
if (imguiInitialized) {
|
if (imguiInitialized) {
|
||||||
ImGui::SFML::Update(*window, clock.getElapsedTime());
|
ImGui::SFML::Update(*window, clock.getElapsedTime());
|
||||||
}
|
}
|
||||||
|
|
@ -405,8 +406,8 @@ void GameEngine::doFrame()
|
||||||
profilerOverlay->render(*render_target);
|
profilerOverlay->render(*render_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef MCRF_HEADLESS
|
#if !defined(MCRF_HEADLESS) && !defined(MCRF_SDL2)
|
||||||
// Render ImGui overlays (console and scene explorer)
|
// Render ImGui overlays (console and scene explorer) - SFML builds only
|
||||||
if (imguiInitialized && !headless) {
|
if (imguiInitialized && !headless) {
|
||||||
console.render();
|
console.render();
|
||||||
sceneExplorer.render(*this);
|
sceneExplorer.render(*this);
|
||||||
|
|
@ -593,8 +594,8 @@ void GameEngine::sUserInput()
|
||||||
sf::Event event;
|
sf::Event event;
|
||||||
while (window && window->pollEvent(event))
|
while (window && window->pollEvent(event))
|
||||||
{
|
{
|
||||||
#ifndef MCRF_HEADLESS
|
#if !defined(MCRF_HEADLESS) && !defined(MCRF_SDL2)
|
||||||
// Process event through ImGui first
|
// Process event through ImGui first (SFML builds only)
|
||||||
if (imguiInitialized) {
|
if (imguiInitialized) {
|
||||||
ImGui::SFML::ProcessEvent(*window, event);
|
ImGui::SFML::ProcessEvent(*window, event);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@
|
||||||
#include "HeadlessRenderer.h"
|
#include "HeadlessRenderer.h"
|
||||||
#include "SceneTransition.h"
|
#include "SceneTransition.h"
|
||||||
#include "Profiler.h"
|
#include "Profiler.h"
|
||||||
#ifndef MCRF_HEADLESS
|
// ImGui is only available for SFML builds (not headless, not SDL2)
|
||||||
|
#if !defined(MCRF_HEADLESS) && !defined(MCRF_SDL2)
|
||||||
#include "ImGuiConsole.h"
|
#include "ImGuiConsole.h"
|
||||||
#include "ImGuiSceneExplorer.h"
|
#include "ImGuiSceneExplorer.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -196,7 +197,8 @@ private:
|
||||||
int overlayUpdateCounter = 0; // Only update overlay every N frames
|
int overlayUpdateCounter = 0; // Only update overlay every N frames
|
||||||
ProfilerOverlay* profilerOverlay = nullptr; // The actual overlay renderer
|
ProfilerOverlay* profilerOverlay = nullptr; // The actual overlay renderer
|
||||||
|
|
||||||
#ifndef MCRF_HEADLESS
|
// ImGui is only available for SFML builds
|
||||||
|
#if !defined(MCRF_HEADLESS) && !defined(MCRF_SDL2)
|
||||||
// ImGui console overlay
|
// ImGui console overlay
|
||||||
ImGuiConsole console;
|
ImGuiConsole console;
|
||||||
ImGuiSceneExplorer sceneExplorer;
|
ImGuiSceneExplorer sceneExplorer;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ImGuiConsole.cpp - Debug console using ImGui
|
// ImGuiConsole.cpp - Debug console using ImGui
|
||||||
// This file is excluded from headless builds (no GUI/debug interface needed)
|
// This file is excluded from headless and SDL2 builds (ImGui-SFML only)
|
||||||
|
|
||||||
#ifndef MCRF_HEADLESS
|
#if !defined(MCRF_HEADLESS) && !defined(MCRF_SDL2)
|
||||||
|
|
||||||
#include "ImGuiConsole.h"
|
#include "ImGuiConsole.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// ImGuiConsole - excluded from headless builds (no GUI/debug interface)
|
// ImGuiConsole - excluded from headless and SDL2 builds (ImGui-SFML only)
|
||||||
#ifndef MCRF_HEADLESS
|
#if !defined(MCRF_HEADLESS) && !defined(MCRF_SDL2)
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ImGuiSceneExplorer.cpp - Debug scene hierarchy explorer using ImGui
|
// ImGuiSceneExplorer.cpp - Debug scene hierarchy explorer using ImGui
|
||||||
// This file is excluded from headless builds (no GUI/debug interface needed)
|
// This file is excluded from headless and SDL2 builds (ImGui-SFML only)
|
||||||
|
|
||||||
#ifndef MCRF_HEADLESS
|
#if !defined(MCRF_HEADLESS) && !defined(MCRF_SDL2)
|
||||||
|
|
||||||
#include "ImGuiSceneExplorer.h"
|
#include "ImGuiSceneExplorer.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// ImGuiSceneExplorer - excluded from headless builds (no GUI/debug interface)
|
// ImGuiSceneExplorer - excluded from headless and SDL2 builds (ImGui-SFML only)
|
||||||
#ifndef MCRF_HEADLESS
|
#if !defined(MCRF_HEADLESS) && !defined(MCRF_SDL2)
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,8 @@
|
||||||
#include "PyUniformCollection.h" // Shader uniform collection (#106)
|
#include "PyUniformCollection.h" // Shader uniform collection (#106)
|
||||||
#include "McRogueFaceVersion.h"
|
#include "McRogueFaceVersion.h"
|
||||||
#include "GameEngine.h"
|
#include "GameEngine.h"
|
||||||
#ifndef MCRF_HEADLESS
|
// ImGui is only available for SFML builds
|
||||||
|
#if !defined(MCRF_HEADLESS) && !defined(MCRF_SDL2)
|
||||||
#include "ImGuiConsole.h"
|
#include "ImGuiConsole.h"
|
||||||
#endif
|
#endif
|
||||||
#include "BenchmarkLogger.h"
|
#include "BenchmarkLogger.h"
|
||||||
|
|
@ -1627,7 +1628,7 @@ PyObject* McRFPy_API::_setDevConsole(PyObject* self, PyObject* args) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef MCRF_HEADLESS
|
#if !defined(MCRF_HEADLESS) && !defined(MCRF_SDL2)
|
||||||
ImGuiConsole::setEnabled(enabled);
|
ImGuiConsole::setEnabled(enabled);
|
||||||
#endif
|
#endif
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
|
|
||||||
38
src/platform/EmscriptenStubs.cpp
Normal file
38
src/platform/EmscriptenStubs.cpp
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
// EmscriptenStubs.cpp - Stub implementations for missing POSIX functions in Emscripten
|
||||||
|
// These functions are referenced by Python's posixmodule and timemodule but not available in WASM
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
// wait3 and wait4 are BSD-style wait functions not available in Emscripten
|
||||||
|
// Python's posixmodule references them but they're not critical for WASM
|
||||||
|
pid_t wait3(int *status, int options, struct rusage *rusage) {
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage) {
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wcsftime - wide character strftime, used by Python's timemodule
|
||||||
|
// Provide a minimal implementation that returns 0 (no characters written)
|
||||||
|
size_t wcsftime(wchar_t *wcs, size_t maxsize, const wchar_t *format, const struct tm *timeptr) {
|
||||||
|
// In a full implementation, this would format time as wide chars
|
||||||
|
// For WASM, just return 0 to indicate nothing written
|
||||||
|
if (maxsize > 0) {
|
||||||
|
wcs[0] = L'\0';
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
#endif // __EMSCRIPTEN__
|
||||||
1338
src/platform/SDL2Renderer.cpp
Normal file
1338
src/platform/SDL2Renderer.cpp
Normal file
File diff suppressed because it is too large
Load diff
182
src/platform/SDL2Renderer.h
Normal file
182
src/platform/SDL2Renderer.h
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
// SDL2Renderer.h - OpenGL ES 2 rendering implementation for SDL2 backend
|
||||||
|
// This file provides the actual rendering implementation for the SDL2 types
|
||||||
|
// defined in SDL2Types.h. It handles:
|
||||||
|
// - OpenGL ES 2 context management
|
||||||
|
// - Shader compilation and management
|
||||||
|
// - Texture loading (via stb_image)
|
||||||
|
// - Font rendering (via stb_truetype)
|
||||||
|
// - Framebuffer object management
|
||||||
|
//
|
||||||
|
// Part of the renderer abstraction layer
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef MCRF_SDL2
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// Forward declarations to avoid SDL header in this header
|
||||||
|
struct SDL_Window;
|
||||||
|
typedef void* SDL_GLContext;
|
||||||
|
|
||||||
|
namespace sf {
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
class RenderTarget;
|
||||||
|
class Texture;
|
||||||
|
class Shader;
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// SDL2 Renderer - Singleton managing OpenGL ES 2 state
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class SDL2Renderer {
|
||||||
|
public:
|
||||||
|
// Singleton access
|
||||||
|
static SDL2Renderer& getInstance();
|
||||||
|
|
||||||
|
// Initialization/shutdown
|
||||||
|
bool init(); // Initialize SDL2 (call before window creation)
|
||||||
|
bool initGL(); // Initialize OpenGL resources (call after GL context exists)
|
||||||
|
void shutdown();
|
||||||
|
bool isInitialized() const { return initialized_; }
|
||||||
|
bool isGLInitialized() const { return glInitialized_; }
|
||||||
|
|
||||||
|
// Built-in shader programs
|
||||||
|
enum class ShaderType {
|
||||||
|
Shape, // For RectangleShape, CircleShape, etc.
|
||||||
|
Sprite, // For textured sprites
|
||||||
|
Text, // For text rendering
|
||||||
|
Custom // User-provided shaders
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get a built-in shader program
|
||||||
|
unsigned int getShaderProgram(ShaderType type) const;
|
||||||
|
|
||||||
|
// Compile a custom shader program
|
||||||
|
unsigned int compileShader(const std::string& vertexSource, const std::string& fragmentSource);
|
||||||
|
void deleteShaderProgram(unsigned int programId);
|
||||||
|
|
||||||
|
// Texture management
|
||||||
|
unsigned int createTexture(unsigned int width, unsigned int height, const unsigned char* pixels = nullptr);
|
||||||
|
void updateTexture(unsigned int textureId, unsigned int x, unsigned int y,
|
||||||
|
unsigned int width, unsigned int height, const unsigned char* pixels);
|
||||||
|
void deleteTexture(unsigned int textureId);
|
||||||
|
void setTextureSmooth(unsigned int textureId, bool smooth);
|
||||||
|
void setTextureRepeated(unsigned int textureId, bool repeated);
|
||||||
|
|
||||||
|
// FBO management
|
||||||
|
unsigned int createFBO(unsigned int width, unsigned int height, unsigned int& colorTexture);
|
||||||
|
void deleteFBO(unsigned int fboId);
|
||||||
|
void bindFBO(unsigned int fboId);
|
||||||
|
void unbindFBO();
|
||||||
|
|
||||||
|
// Render state management
|
||||||
|
void setViewport(int x, int y, unsigned int width, unsigned int height);
|
||||||
|
void setProjection(float left, float right, float bottom, float top);
|
||||||
|
void clear(float r, float g, float b, float a);
|
||||||
|
|
||||||
|
// Drawing primitives
|
||||||
|
void drawTriangles(const float* vertices, size_t vertexCount,
|
||||||
|
const float* colors, const float* texCoords,
|
||||||
|
unsigned int textureId = 0);
|
||||||
|
|
||||||
|
// Projection matrix access (for shaders)
|
||||||
|
const float* getProjectionMatrix() const { return projectionMatrix_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
SDL2Renderer() = default;
|
||||||
|
~SDL2Renderer() = default;
|
||||||
|
SDL2Renderer(const SDL2Renderer&) = delete;
|
||||||
|
SDL2Renderer& operator=(const SDL2Renderer&) = delete;
|
||||||
|
|
||||||
|
bool initialized_ = false;
|
||||||
|
bool glInitialized_ = false;
|
||||||
|
|
||||||
|
// Built-in shader programs
|
||||||
|
unsigned int shapeProgram_ = 0;
|
||||||
|
unsigned int spriteProgram_ = 0;
|
||||||
|
unsigned int textProgram_ = 0;
|
||||||
|
|
||||||
|
// Current projection matrix (4x4 orthographic)
|
||||||
|
float projectionMatrix_[16] = {0};
|
||||||
|
|
||||||
|
// FBO stack for nested render-to-texture
|
||||||
|
std::vector<unsigned int> fboStack_;
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
bool compileAndLinkProgram(const char* vertexSrc, const char* fragmentSrc, unsigned int& programOut);
|
||||||
|
unsigned int compileShaderStage(unsigned int type, const char* source);
|
||||||
|
void initBuiltinShaders();
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Keyboard/Mouse SDL2 Implementation helpers
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// SDL2 scancode to sf::Keyboard::Key mapping
|
||||||
|
int sdlScancodeToSfKey(int sdlScancode);
|
||||||
|
int sfKeyToSdlScancode(int sfKey);
|
||||||
|
|
||||||
|
// SDL2 mouse button to sf::Mouse::Button mapping
|
||||||
|
int sdlButtonToSfButton(int sdlButton);
|
||||||
|
int sfButtonToSdlButton(int sfButton);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Event translation
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// Translate SDL_Event to sf::Event
|
||||||
|
// Returns true if the event was translated, false if it should be ignored
|
||||||
|
bool translateSDLEvent(const void* sdlEvent, void* sfEvent);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Font Atlas for text rendering
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class FontAtlas {
|
||||||
|
public:
|
||||||
|
FontAtlas();
|
||||||
|
~FontAtlas();
|
||||||
|
|
||||||
|
// Load font data
|
||||||
|
bool load(const unsigned char* fontData, size_t dataSize, float fontSize);
|
||||||
|
|
||||||
|
// Get texture atlas
|
||||||
|
unsigned int getTextureId() const { return textureId_; }
|
||||||
|
|
||||||
|
// Get glyph info for rendering
|
||||||
|
struct GlyphInfo {
|
||||||
|
float u0, v0, u1, v1; // Texture coordinates
|
||||||
|
float xoff, yoff; // Offset from cursor
|
||||||
|
float xadvance; // How far to advance cursor
|
||||||
|
float width, height; // Glyph dimensions in pixels
|
||||||
|
};
|
||||||
|
|
||||||
|
bool getGlyph(uint32_t codepoint, GlyphInfo& info) const;
|
||||||
|
|
||||||
|
// Get font metrics
|
||||||
|
float getAscent() const { return ascent_; }
|
||||||
|
float getDescent() const { return descent_; }
|
||||||
|
float getLineHeight() const { return lineHeight_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int textureId_ = 0;
|
||||||
|
float fontSize_ = 0;
|
||||||
|
float ascent_ = 0;
|
||||||
|
float descent_ = 0;
|
||||||
|
float lineHeight_ = 0;
|
||||||
|
|
||||||
|
// Glyph cache - maps codepoint to glyph info
|
||||||
|
std::unordered_map<uint32_t, GlyphInfo> glyphCache_;
|
||||||
|
|
||||||
|
// stb_truetype font info (opaque pointer to avoid header inclusion)
|
||||||
|
void* stbFontInfo_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sf
|
||||||
|
|
||||||
|
#endif // MCRF_SDL2
|
||||||
999
src/platform/SDL2Types.h
Normal file
999
src/platform/SDL2Types.h
Normal file
|
|
@ -0,0 +1,999 @@
|
||||||
|
// SDL2Types.h - SFML type stubs for SDL2 + OpenGL ES 2 backend
|
||||||
|
// This file provides type definitions that compile against SDL2 instead of SFML,
|
||||||
|
// enabling cross-platform builds for Web (Emscripten/WebGL), Android, and desktop.
|
||||||
|
//
|
||||||
|
// Part of the renderer abstraction layer (issue #158 continuation)
|
||||||
|
//
|
||||||
|
// Architecture:
|
||||||
|
// - Game code uses sf:: namespace types (unchanged from SFML builds)
|
||||||
|
// - This header provides sf:: types implemented using SDL2 + OpenGL ES 2
|
||||||
|
// - Compile-time selection via MCRF_SDL2 define
|
||||||
|
//
|
||||||
|
// Phases:
|
||||||
|
// - Phase 1: Skeleton (stubs matching HeadlessTypes.h)
|
||||||
|
// - Phase 2: Window + GL context
|
||||||
|
// - Phase 3: Shape rendering (rectangles, circles)
|
||||||
|
// - Phase 4: Texture + Sprites
|
||||||
|
// - Phase 5: Text rendering
|
||||||
|
// - Phase 6: RenderTexture (FBO)
|
||||||
|
// - Phase 7: Custom shaders
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
// SDL2 headers - conditionally included when actually implementing
|
||||||
|
// For now, forward declare what we need
|
||||||
|
#ifdef MCRF_SDL2_IMPL
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace sf {
|
||||||
|
|
||||||
|
// Forward declarations (needed for RenderWindow)
|
||||||
|
struct Event;
|
||||||
|
class Keyboard;
|
||||||
|
class Mouse;
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Type Aliases (SFML compatibility)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
using Uint8 = uint8_t;
|
||||||
|
using Uint16 = uint16_t;
|
||||||
|
using Uint32 = uint32_t;
|
||||||
|
using Uint64 = uint64_t;
|
||||||
|
using Int8 = int8_t;
|
||||||
|
using Int16 = int16_t;
|
||||||
|
using Int32 = int32_t;
|
||||||
|
using Int64 = int64_t;
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Vector Types
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct Vector2 {
|
||||||
|
T x = 0;
|
||||||
|
T y = 0;
|
||||||
|
|
||||||
|
Vector2() = default;
|
||||||
|
Vector2(T x_, T y_) : x(x_), y(y_) {}
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
explicit Vector2(const Vector2<U>& other) : x(static_cast<T>(other.x)), y(static_cast<T>(other.y)) {}
|
||||||
|
|
||||||
|
Vector2 operator+(const Vector2& rhs) const { return Vector2(x + rhs.x, y + rhs.y); }
|
||||||
|
Vector2 operator-(const Vector2& rhs) const { return Vector2(x - rhs.x, y - rhs.y); }
|
||||||
|
Vector2 operator*(T scalar) const { return Vector2(x * scalar, y * scalar); }
|
||||||
|
Vector2 operator/(T scalar) const { return Vector2(x / scalar, y / scalar); }
|
||||||
|
Vector2& operator+=(const Vector2& rhs) { x += rhs.x; y += rhs.y; return *this; }
|
||||||
|
Vector2& operator-=(const Vector2& rhs) { x -= rhs.x; y -= rhs.y; return *this; }
|
||||||
|
Vector2& operator*=(T scalar) { x *= scalar; y *= scalar; return *this; }
|
||||||
|
Vector2& operator/=(T scalar) { x /= scalar; y /= scalar; return *this; }
|
||||||
|
bool operator==(const Vector2& rhs) const { return x == rhs.x && y == rhs.y; }
|
||||||
|
bool operator!=(const Vector2& rhs) const { return !(*this == rhs); }
|
||||||
|
Vector2 operator-() const { return Vector2(-x, -y); }
|
||||||
|
};
|
||||||
|
|
||||||
|
using Vector2f = Vector2<float>;
|
||||||
|
using Vector2i = Vector2<int>;
|
||||||
|
using Vector2u = Vector2<unsigned int>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Vector2<T> operator*(T scalar, const Vector2<T>& vec) { return vec * scalar; }
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Color Type
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
struct Color {
|
||||||
|
uint8_t r = 0;
|
||||||
|
uint8_t g = 0;
|
||||||
|
uint8_t b = 0;
|
||||||
|
uint8_t a = 255;
|
||||||
|
|
||||||
|
Color() = default;
|
||||||
|
Color(uint8_t r_, uint8_t g_, uint8_t b_, uint8_t a_ = 255) : r(r_), g(g_), b(b_), a(a_) {}
|
||||||
|
|
||||||
|
bool operator==(const Color& rhs) const { return r == rhs.r && g == rhs.g && b == rhs.b && a == rhs.a; }
|
||||||
|
bool operator!=(const Color& rhs) const { return !(*this == rhs); }
|
||||||
|
|
||||||
|
// Standard colors
|
||||||
|
static const Color Black;
|
||||||
|
static const Color White;
|
||||||
|
static const Color Red;
|
||||||
|
static const Color Green;
|
||||||
|
static const Color Blue;
|
||||||
|
static const Color Yellow;
|
||||||
|
static const Color Magenta;
|
||||||
|
static const Color Cyan;
|
||||||
|
static const Color Transparent;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Static color definitions
|
||||||
|
inline const Color Color::Black(0, 0, 0);
|
||||||
|
inline const Color Color::White(255, 255, 255);
|
||||||
|
inline const Color Color::Red(255, 0, 0);
|
||||||
|
inline const Color Color::Green(0, 255, 0);
|
||||||
|
inline const Color Color::Blue(0, 0, 255);
|
||||||
|
inline const Color Color::Yellow(255, 255, 0);
|
||||||
|
inline const Color Color::Magenta(255, 0, 255);
|
||||||
|
inline const Color Color::Cyan(0, 255, 255);
|
||||||
|
inline const Color Color::Transparent(0, 0, 0, 0);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Rectangle Types
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct Rect {
|
||||||
|
T left = 0;
|
||||||
|
T top = 0;
|
||||||
|
T width = 0;
|
||||||
|
T height = 0;
|
||||||
|
|
||||||
|
Rect() = default;
|
||||||
|
Rect(T left_, T top_, T width_, T height_) : left(left_), top(top_), width(width_), height(height_) {}
|
||||||
|
Rect(const Vector2<T>& position, const Vector2<T>& size)
|
||||||
|
: left(position.x), top(position.y), width(size.x), height(size.y) {}
|
||||||
|
|
||||||
|
bool contains(T x, T y) const {
|
||||||
|
return x >= left && x < left + width && y >= top && y < top + height;
|
||||||
|
}
|
||||||
|
bool contains(const Vector2<T>& point) const { return contains(point.x, point.y); }
|
||||||
|
|
||||||
|
bool intersects(const Rect& other) const {
|
||||||
|
return left < other.left + other.width && left + width > other.left &&
|
||||||
|
top < other.top + other.height && top + height > other.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2<T> getPosition() const { return Vector2<T>(left, top); }
|
||||||
|
Vector2<T> getSize() const { return Vector2<T>(width, height); }
|
||||||
|
};
|
||||||
|
|
||||||
|
using FloatRect = Rect<float>;
|
||||||
|
using IntRect = Rect<int>;
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Time Types
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class Time {
|
||||||
|
int64_t microseconds_ = 0;
|
||||||
|
public:
|
||||||
|
Time() = default;
|
||||||
|
float asSeconds() const { return microseconds_ / 1000000.0f; }
|
||||||
|
int32_t asMilliseconds() const { return static_cast<int32_t>(microseconds_ / 1000); }
|
||||||
|
int64_t asMicroseconds() const { return microseconds_; }
|
||||||
|
|
||||||
|
static Time Zero;
|
||||||
|
|
||||||
|
friend Time seconds(float amount);
|
||||||
|
friend Time milliseconds(int32_t amount);
|
||||||
|
friend Time microseconds(int64_t amount);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline Time Time::Zero;
|
||||||
|
|
||||||
|
inline Time seconds(float amount) { Time t; t.microseconds_ = static_cast<int64_t>(amount * 1000000); return t; }
|
||||||
|
inline Time milliseconds(int32_t amount) { Time t; t.microseconds_ = amount * 1000; return t; }
|
||||||
|
inline Time microseconds(int64_t amount) { Time t; t.microseconds_ = amount; return t; }
|
||||||
|
|
||||||
|
class Clock {
|
||||||
|
int64_t start_time_ = 0;
|
||||||
|
|
||||||
|
static int64_t now_microseconds() {
|
||||||
|
// Use C++11 chrono for portable timing
|
||||||
|
auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
auto duration = now.time_since_epoch();
|
||||||
|
return std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
Clock() : start_time_(now_microseconds()) {}
|
||||||
|
|
||||||
|
Time getElapsedTime() const {
|
||||||
|
return microseconds(now_microseconds() - start_time_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Time restart() {
|
||||||
|
int64_t now = now_microseconds();
|
||||||
|
int64_t elapsed = now - start_time_;
|
||||||
|
start_time_ = now;
|
||||||
|
return microseconds(elapsed);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Transform
|
||||||
|
// =============================================================================
|
||||||
|
// SDL2 backend will implement actual matrix math for transforms
|
||||||
|
// For now, stub implementation matching headless
|
||||||
|
|
||||||
|
class Transform {
|
||||||
|
// 3x3 matrix stored as 4x4 for OpenGL compatibility
|
||||||
|
// [ m00 m01 m02 ] [ m[0] m[4] m[12] ]
|
||||||
|
// [ m10 m11 m12 ] -> [ m[1] m[5] m[13] ]
|
||||||
|
// [ 0 0 1 ] [ 0 0 1 ]
|
||||||
|
float m[16] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
|
||||||
|
|
||||||
|
public:
|
||||||
|
Transform() = default;
|
||||||
|
Transform& translate(float x, float y) { return *this; } // TODO: Implement
|
||||||
|
Transform& translate(const Vector2f& offset) { return translate(offset.x, offset.y); }
|
||||||
|
Transform& rotate(float angle) { return *this; } // TODO: Implement
|
||||||
|
Transform& rotate(float angle, const Vector2f& center) { return *this; } // TODO: Implement
|
||||||
|
Transform& scale(float factorX, float factorY) { return *this; } // TODO: Implement
|
||||||
|
Transform& scale(const Vector2f& factors) { return scale(factors.x, factors.y); }
|
||||||
|
|
||||||
|
Vector2f transformPoint(float x, float y) const { return Vector2f(x, y); } // TODO: Implement
|
||||||
|
Vector2f transformPoint(const Vector2f& point) const { return point; }
|
||||||
|
FloatRect transformRect(const FloatRect& rect) const { return rect; } // TODO: Implement
|
||||||
|
|
||||||
|
Transform getInverse() const { return Transform(); } // TODO: Implement
|
||||||
|
|
||||||
|
Transform operator*(const Transform& rhs) const { return Transform(); } // TODO: Implement
|
||||||
|
Vector2f operator*(const Vector2f& point) const { return point; }
|
||||||
|
|
||||||
|
static const Transform Identity;
|
||||||
|
|
||||||
|
// SDL2-specific: Get raw matrix for OpenGL
|
||||||
|
const float* getMatrix() const { return m; }
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const Transform Transform::Identity;
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Vertex (for custom geometry)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
Vector2f position;
|
||||||
|
Color color;
|
||||||
|
Vector2f texCoords;
|
||||||
|
|
||||||
|
Vertex() = default;
|
||||||
|
Vertex(const Vector2f& pos) : position(pos), color(Color::White) {}
|
||||||
|
Vertex(const Vector2f& pos, const Color& col) : position(pos), color(col) {}
|
||||||
|
Vertex(const Vector2f& pos, const Vector2f& tex) : position(pos), color(Color::White), texCoords(tex) {}
|
||||||
|
Vertex(const Vector2f& pos, const Color& col, const Vector2f& tex) : position(pos), color(col), texCoords(tex) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// View (camera)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class View {
|
||||||
|
Vector2f center_;
|
||||||
|
Vector2f size_;
|
||||||
|
float rotation_ = 0.0f;
|
||||||
|
FloatRect viewport_{0, 0, 1, 1};
|
||||||
|
public:
|
||||||
|
View() : center_(0, 0), size_(1000, 1000) {}
|
||||||
|
View(const FloatRect& rect) : center_(rect.left + rect.width/2, rect.top + rect.height/2), size_(rect.width, rect.height) {}
|
||||||
|
View(const Vector2f& center, const Vector2f& size) : center_(center), size_(size) {}
|
||||||
|
|
||||||
|
void setCenter(float x, float y) { center_ = Vector2f(x, y); }
|
||||||
|
void setCenter(const Vector2f& center) { center_ = center; }
|
||||||
|
void setSize(float width, float height) { size_ = Vector2f(width, height); }
|
||||||
|
void setSize(const Vector2f& size) { size_ = size; }
|
||||||
|
void setRotation(float angle) { rotation_ = angle; }
|
||||||
|
void setViewport(const FloatRect& viewport) { viewport_ = viewport; }
|
||||||
|
|
||||||
|
const Vector2f& getCenter() const { return center_; }
|
||||||
|
const Vector2f& getSize() const { return size_; }
|
||||||
|
float getRotation() const { return rotation_; }
|
||||||
|
const FloatRect& getViewport() const { return viewport_; }
|
||||||
|
|
||||||
|
void move(float offsetX, float offsetY) { center_.x += offsetX; center_.y += offsetY; }
|
||||||
|
void move(const Vector2f& offset) { center_ += offset; }
|
||||||
|
void rotate(float angle) { rotation_ += angle; }
|
||||||
|
void zoom(float factor) { size_ *= factor; }
|
||||||
|
|
||||||
|
Transform getTransform() const { return Transform::Identity; } // TODO: Implement
|
||||||
|
Transform getInverseTransform() const { return Transform::Identity; } // TODO: Implement
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Rendering Types
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
enum PrimitiveType {
|
||||||
|
Points,
|
||||||
|
Lines,
|
||||||
|
LineStrip,
|
||||||
|
Triangles,
|
||||||
|
TriangleStrip,
|
||||||
|
TriangleFan,
|
||||||
|
Quads // Deprecated in SFML 3.0, but we'll support it via triangulation
|
||||||
|
};
|
||||||
|
|
||||||
|
// BlendMode stub
|
||||||
|
struct BlendMode {
|
||||||
|
BlendMode() = default;
|
||||||
|
static const BlendMode Alpha;
|
||||||
|
static const BlendMode Add;
|
||||||
|
static const BlendMode Multiply;
|
||||||
|
static const BlendMode None;
|
||||||
|
};
|
||||||
|
inline const BlendMode BlendMode::Alpha{};
|
||||||
|
inline const BlendMode BlendMode::Add{};
|
||||||
|
inline const BlendMode BlendMode::Multiply{};
|
||||||
|
inline const BlendMode BlendMode::None{};
|
||||||
|
|
||||||
|
// Forward declare Shader for RenderStates
|
||||||
|
class Shader;
|
||||||
|
|
||||||
|
class RenderStates {
|
||||||
|
public:
|
||||||
|
RenderStates() = default;
|
||||||
|
RenderStates(const Transform& transform) {} // Implicit conversion from Transform
|
||||||
|
RenderStates(const BlendMode& mode) {}
|
||||||
|
RenderStates(const Shader* shader) {} // Implicit conversion from Shader pointer
|
||||||
|
static const RenderStates Default;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const RenderStates RenderStates::Default;
|
||||||
|
|
||||||
|
// Forward declarations for rendering types
|
||||||
|
class RenderTarget;
|
||||||
|
class RenderTexture;
|
||||||
|
class RenderWindow;
|
||||||
|
class Texture;
|
||||||
|
class Font;
|
||||||
|
class Shader;
|
||||||
|
|
||||||
|
// Drawable base class
|
||||||
|
class Drawable {
|
||||||
|
public:
|
||||||
|
virtual ~Drawable() = default;
|
||||||
|
protected:
|
||||||
|
friend class RenderTarget;
|
||||||
|
virtual void draw(RenderTarget& target, RenderStates states) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Transformable base class
|
||||||
|
class Transformable {
|
||||||
|
protected:
|
||||||
|
Vector2f position_;
|
||||||
|
float rotation_ = 0.0f;
|
||||||
|
Vector2f scale_{1.0f, 1.0f};
|
||||||
|
Vector2f origin_;
|
||||||
|
public:
|
||||||
|
virtual ~Transformable() = default;
|
||||||
|
|
||||||
|
void setPosition(float x, float y) { position_ = Vector2f(x, y); }
|
||||||
|
void setPosition(const Vector2f& position) { position_ = position; }
|
||||||
|
void setRotation(float angle) { rotation_ = angle; }
|
||||||
|
void setScale(float factorX, float factorY) { scale_ = Vector2f(factorX, factorY); }
|
||||||
|
void setScale(const Vector2f& factors) { scale_ = factors; }
|
||||||
|
void setOrigin(float x, float y) { origin_ = Vector2f(x, y); }
|
||||||
|
void setOrigin(const Vector2f& origin) { origin_ = origin; }
|
||||||
|
|
||||||
|
const Vector2f& getPosition() const { return position_; }
|
||||||
|
float getRotation() const { return rotation_; }
|
||||||
|
const Vector2f& getScale() const { return scale_; }
|
||||||
|
const Vector2f& getOrigin() const { return origin_; }
|
||||||
|
|
||||||
|
void move(float offsetX, float offsetY) { position_.x += offsetX; position_.y += offsetY; }
|
||||||
|
void move(const Vector2f& offset) { position_ += offset; }
|
||||||
|
void rotate(float angle) { rotation_ += angle; }
|
||||||
|
void scale(float factorX, float factorY) { scale_.x *= factorX; scale_.y *= factorY; }
|
||||||
|
void scale(const Vector2f& factor) { scale_.x *= factor.x; scale_.y *= factor.y; }
|
||||||
|
|
||||||
|
Transform getTransform() const { return Transform::Identity; } // TODO: Implement
|
||||||
|
Transform getInverseTransform() const { return Transform::Identity; } // TODO: Implement
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Shape Classes
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class Shape : public Drawable, public Transformable {
|
||||||
|
protected:
|
||||||
|
Color fillColor_ = Color::White;
|
||||||
|
Color outlineColor_ = Color::White;
|
||||||
|
float outlineThickness_ = 0.0f;
|
||||||
|
public:
|
||||||
|
void setFillColor(const Color& color) { fillColor_ = color; }
|
||||||
|
void setOutlineColor(const Color& color) { outlineColor_ = color; }
|
||||||
|
void setOutlineThickness(float thickness) { outlineThickness_ = thickness; }
|
||||||
|
|
||||||
|
const Color& getFillColor() const { return fillColor_; }
|
||||||
|
const Color& getOutlineColor() const { return outlineColor_; }
|
||||||
|
float getOutlineThickness() const { return outlineThickness_; }
|
||||||
|
|
||||||
|
virtual FloatRect getLocalBounds() const { return FloatRect(); }
|
||||||
|
virtual FloatRect getGlobalBounds() const { return FloatRect(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void draw(RenderTarget& target, RenderStates states) const override; // Implemented in SDL2Renderer.cpp
|
||||||
|
};
|
||||||
|
|
||||||
|
class RectangleShape : public Shape {
|
||||||
|
Vector2f size_;
|
||||||
|
public:
|
||||||
|
RectangleShape(const Vector2f& size = Vector2f(0, 0)) : size_(size) {}
|
||||||
|
void setSize(const Vector2f& size) { size_ = size; }
|
||||||
|
const Vector2f& getSize() const { return size_; }
|
||||||
|
FloatRect getLocalBounds() const override { return FloatRect(0, 0, size_.x, size_.y); }
|
||||||
|
FloatRect getGlobalBounds() const override { return FloatRect(position_.x, position_.y, size_.x, size_.y); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class CircleShape : public Shape {
|
||||||
|
float radius_ = 0.0f;
|
||||||
|
size_t pointCount_ = 30;
|
||||||
|
public:
|
||||||
|
CircleShape(float radius = 0, size_t pointCount = 30) : radius_(radius), pointCount_(pointCount) {}
|
||||||
|
void setRadius(float radius) { radius_ = radius; }
|
||||||
|
float getRadius() const { return radius_; }
|
||||||
|
void setPointCount(size_t count) { pointCount_ = count; }
|
||||||
|
size_t getPointCount() const { return pointCount_; }
|
||||||
|
FloatRect getLocalBounds() const override { return FloatRect(0, 0, radius_ * 2, radius_ * 2); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConvexShape : public Shape {
|
||||||
|
std::vector<Vector2f> points_;
|
||||||
|
public:
|
||||||
|
ConvexShape(size_t pointCount = 0) : points_(pointCount) {}
|
||||||
|
void setPointCount(size_t count) { points_.resize(count); }
|
||||||
|
size_t getPointCount() const { return points_.size(); }
|
||||||
|
void setPoint(size_t index, const Vector2f& point) { if (index < points_.size()) points_[index] = point; }
|
||||||
|
Vector2f getPoint(size_t index) const { return index < points_.size() ? points_[index] : Vector2f(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// VertexArray
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class VertexArray : public Drawable {
|
||||||
|
std::vector<Vertex> vertices_;
|
||||||
|
PrimitiveType primitiveType_ = Points;
|
||||||
|
public:
|
||||||
|
VertexArray() = default;
|
||||||
|
VertexArray(PrimitiveType type, size_t vertexCount = 0) : vertices_(vertexCount), primitiveType_(type) {}
|
||||||
|
|
||||||
|
size_t getVertexCount() const { return vertices_.size(); }
|
||||||
|
Vertex& operator[](size_t index) { return vertices_[index]; }
|
||||||
|
const Vertex& operator[](size_t index) const { return vertices_[index]; }
|
||||||
|
|
||||||
|
void clear() { vertices_.clear(); }
|
||||||
|
void resize(size_t vertexCount) { vertices_.resize(vertexCount); }
|
||||||
|
void append(const Vertex& vertex) { vertices_.push_back(vertex); }
|
||||||
|
|
||||||
|
void setPrimitiveType(PrimitiveType type) { primitiveType_ = type; }
|
||||||
|
PrimitiveType getPrimitiveType() const { return primitiveType_; }
|
||||||
|
|
||||||
|
FloatRect getBounds() const { return FloatRect(); } // TODO: Implement
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void draw(RenderTarget& target, RenderStates states) const override; // Implemented in SDL2Renderer.cpp
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Image
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class Image {
|
||||||
|
Vector2u size_;
|
||||||
|
std::vector<Uint8> pixels_;
|
||||||
|
public:
|
||||||
|
Image() = default;
|
||||||
|
|
||||||
|
void create(unsigned int width, unsigned int height, const Color& color = Color::Black) {
|
||||||
|
size_ = Vector2u(width, height);
|
||||||
|
pixels_.resize(width * height * 4, 0);
|
||||||
|
// Fill with color
|
||||||
|
for (unsigned int y = 0; y < height; ++y) {
|
||||||
|
for (unsigned int x = 0; x < width; ++x) {
|
||||||
|
setPixel(x, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool loadFromFile(const std::string& filename); // Implemented in SDL2Renderer.cpp (uses stb_image)
|
||||||
|
bool saveToFile(const std::string& filename) const; // Implemented in SDL2Renderer.cpp (uses stb_image_write)
|
||||||
|
|
||||||
|
Vector2u getSize() const { return size_; }
|
||||||
|
|
||||||
|
void setPixel(unsigned int x, unsigned int y, const Color& color) {
|
||||||
|
if (x < size_.x && y < size_.y) {
|
||||||
|
size_t idx = (y * size_.x + x) * 4;
|
||||||
|
pixels_[idx] = color.r;
|
||||||
|
pixels_[idx + 1] = color.g;
|
||||||
|
pixels_[idx + 2] = color.b;
|
||||||
|
pixels_[idx + 3] = color.a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getPixel(unsigned int x, unsigned int y) const {
|
||||||
|
if (x < size_.x && y < size_.y) {
|
||||||
|
size_t idx = (y * size_.x + x) * 4;
|
||||||
|
return Color(pixels_[idx], pixels_[idx + 1], pixels_[idx + 2], pixels_[idx + 3]);
|
||||||
|
}
|
||||||
|
return Color::Black;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Uint8* getPixelsPtr() const { return pixels_.data(); }
|
||||||
|
|
||||||
|
// SDL2-specific: Allow direct pixel access for texture upload
|
||||||
|
Uint8* getPixelsPtr() { return pixels_.data(); }
|
||||||
|
void setSize(unsigned int w, unsigned int h) { size_ = Vector2u(w, h); pixels_.resize(w * h * 4); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Texture
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// Forward declare RenderWindow for Texture::update
|
||||||
|
class RenderWindow;
|
||||||
|
|
||||||
|
class Texture {
|
||||||
|
Vector2u size_;
|
||||||
|
// SDL2-specific: OpenGL texture handle (0 = not created)
|
||||||
|
unsigned int textureId_ = 0;
|
||||||
|
bool smooth_ = false;
|
||||||
|
bool repeated_ = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Texture() = default;
|
||||||
|
~Texture(); // Implemented in SDL2Renderer.cpp (deletes GL texture)
|
||||||
|
|
||||||
|
// Copy constructor/assignment - needed for proper GL resource management
|
||||||
|
Texture(const Texture& other);
|
||||||
|
Texture& operator=(const Texture& other);
|
||||||
|
|
||||||
|
bool create(unsigned int width, unsigned int height); // Implemented in SDL2Renderer.cpp
|
||||||
|
bool loadFromFile(const std::string& filename); // Implemented in SDL2Renderer.cpp
|
||||||
|
bool loadFromMemory(const void* data, size_t size); // Implemented in SDL2Renderer.cpp
|
||||||
|
|
||||||
|
Vector2u getSize() const { return size_; }
|
||||||
|
void setSmooth(bool smooth); // Implemented in SDL2Renderer.cpp
|
||||||
|
bool isSmooth() const { return smooth_; }
|
||||||
|
void setRepeated(bool repeated); // Implemented in SDL2Renderer.cpp
|
||||||
|
bool isRepeated() const { return repeated_; }
|
||||||
|
|
||||||
|
Image copyToImage() const; // Implemented in SDL2Renderer.cpp
|
||||||
|
void update(const RenderWindow& window); // Implemented in SDL2Renderer.cpp
|
||||||
|
void update(const Uint8* pixels); // Implemented in SDL2Renderer.cpp
|
||||||
|
void update(const Uint8* pixels, unsigned int width, unsigned int height, unsigned int x, unsigned int y);
|
||||||
|
|
||||||
|
// SDL2-specific: Get GL texture handle
|
||||||
|
unsigned int getNativeHandle() const { return textureId_; }
|
||||||
|
void setNativeHandle(unsigned int id) { textureId_ = id; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Sprite
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class Sprite : public Drawable, public Transformable {
|
||||||
|
const Texture* texture_ = nullptr;
|
||||||
|
IntRect textureRect_;
|
||||||
|
Color color_ = Color::White;
|
||||||
|
public:
|
||||||
|
Sprite() = default;
|
||||||
|
Sprite(const Texture& texture) : texture_(&texture) {}
|
||||||
|
Sprite(const Texture& texture, const IntRect& rectangle) : texture_(&texture), textureRect_(rectangle) {}
|
||||||
|
|
||||||
|
void setTexture(const Texture& texture, bool resetRect = false) { texture_ = &texture; }
|
||||||
|
void setTextureRect(const IntRect& rectangle) { textureRect_ = rectangle; }
|
||||||
|
void setColor(const Color& color) { color_ = color; }
|
||||||
|
|
||||||
|
const Texture* getTexture() const { return texture_; }
|
||||||
|
const IntRect& getTextureRect() const { return textureRect_; }
|
||||||
|
const Color& getColor() const { return color_; }
|
||||||
|
|
||||||
|
FloatRect getLocalBounds() const { return FloatRect(0, 0, static_cast<float>(textureRect_.width), static_cast<float>(textureRect_.height)); }
|
||||||
|
FloatRect getGlobalBounds() const { return FloatRect(position_.x, position_.y, static_cast<float>(textureRect_.width), static_cast<float>(textureRect_.height)); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void draw(RenderTarget& target, RenderStates states) const override; // Implemented in SDL2Renderer.cpp
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Text and Font
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class Font {
|
||||||
|
// SDL2-specific: font data for stb_truetype
|
||||||
|
std::vector<unsigned char> fontData_;
|
||||||
|
bool loaded_ = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct Info {
|
||||||
|
std::string family;
|
||||||
|
};
|
||||||
|
|
||||||
|
Font() = default;
|
||||||
|
bool loadFromFile(const std::string& filename); // Implemented in SDL2Renderer.cpp
|
||||||
|
bool loadFromMemory(const void* data, size_t sizeInBytes); // Implemented in SDL2Renderer.cpp
|
||||||
|
const Info& getInfo() const { static Info info; return info; }
|
||||||
|
|
||||||
|
// SDL2-specific: Access font data
|
||||||
|
const unsigned char* getData() const { return fontData_.data(); }
|
||||||
|
size_t getDataSize() const { return fontData_.size(); }
|
||||||
|
bool isLoaded() const { return loaded_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Text : public Drawable, public Transformable {
|
||||||
|
std::string string_;
|
||||||
|
const Font* font_ = nullptr;
|
||||||
|
unsigned int characterSize_ = 30;
|
||||||
|
Color fillColor_ = Color::White;
|
||||||
|
Color outlineColor_ = Color::Black;
|
||||||
|
float outlineThickness_ = 0.0f;
|
||||||
|
uint32_t style_ = 0;
|
||||||
|
public:
|
||||||
|
enum Style { Regular = 0, Bold = 1, Italic = 2, Underlined = 4, StrikeThrough = 8 };
|
||||||
|
|
||||||
|
Text() = default;
|
||||||
|
Text(const std::string& string, const Font& font, unsigned int characterSize = 30)
|
||||||
|
: string_(string), font_(&font), characterSize_(characterSize) {}
|
||||||
|
|
||||||
|
void setString(const std::string& string) { string_ = string; }
|
||||||
|
void setFont(const Font& font) { font_ = &font; }
|
||||||
|
void setCharacterSize(unsigned int size) { characterSize_ = size; }
|
||||||
|
void setStyle(uint32_t style) { style_ = style; }
|
||||||
|
void setFillColor(const Color& color) { fillColor_ = color; }
|
||||||
|
void setOutlineColor(const Color& color) { outlineColor_ = color; }
|
||||||
|
void setOutlineThickness(float thickness) { outlineThickness_ = thickness; }
|
||||||
|
|
||||||
|
const std::string& getString() const { return string_; }
|
||||||
|
const Font* getFont() const { return font_; }
|
||||||
|
unsigned int getCharacterSize() const { return characterSize_; }
|
||||||
|
uint32_t getStyle() const { return style_; }
|
||||||
|
const Color& getFillColor() const { return fillColor_; }
|
||||||
|
const Color& getOutlineColor() const { return outlineColor_; }
|
||||||
|
float getOutlineThickness() const { return outlineThickness_; }
|
||||||
|
|
||||||
|
FloatRect getLocalBounds() const { return FloatRect(); } // TODO: Implement
|
||||||
|
FloatRect getGlobalBounds() const { return FloatRect(); } // TODO: Implement
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void draw(RenderTarget& target, RenderStates states) const override; // Implemented in SDL2Renderer.cpp
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// RenderTarget (base class for rendering)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class RenderTarget {
|
||||||
|
protected:
|
||||||
|
Vector2u size_;
|
||||||
|
View view_;
|
||||||
|
View defaultView_;
|
||||||
|
public:
|
||||||
|
virtual ~RenderTarget() = default;
|
||||||
|
|
||||||
|
virtual Vector2u getSize() const { return size_; }
|
||||||
|
virtual void clear(const Color& color = Color::Black); // Implemented in SDL2Renderer.cpp
|
||||||
|
|
||||||
|
void draw(const Drawable& drawable, const RenderStates& states = RenderStates::Default) {
|
||||||
|
drawable.draw(*this, states);
|
||||||
|
}
|
||||||
|
void draw(const Vertex* vertices, size_t vertexCount, PrimitiveType type, const RenderStates& states = RenderStates::Default);
|
||||||
|
void draw(const VertexArray& vertices, const RenderStates& states = RenderStates::Default);
|
||||||
|
|
||||||
|
void setView(const View& view) { view_ = view; }
|
||||||
|
const View& getView() const { return view_; }
|
||||||
|
const View& getDefaultView() const { return defaultView_; }
|
||||||
|
|
||||||
|
IntRect getViewport(const View& view) const { return IntRect(0, 0, size_.x, size_.y); }
|
||||||
|
|
||||||
|
Vector2f mapPixelToCoords(const Vector2i& point) const { return Vector2f(static_cast<float>(point.x), static_cast<float>(point.y)); }
|
||||||
|
Vector2f mapPixelToCoords(const Vector2i& point, const View& view) const { return Vector2f(static_cast<float>(point.x), static_cast<float>(point.y)); }
|
||||||
|
Vector2i mapCoordsToPixel(const Vector2f& point) const { return Vector2i(static_cast<int>(point.x), static_cast<int>(point.y)); }
|
||||||
|
Vector2i mapCoordsToPixel(const Vector2f& point, const View& view) const { return Vector2i(static_cast<int>(point.x), static_cast<int>(point.y)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// RenderTexture (Framebuffer Object)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class RenderTexture : public RenderTarget {
|
||||||
|
Texture texture_;
|
||||||
|
unsigned int fboId_ = 0; // OpenGL FBO handle
|
||||||
|
public:
|
||||||
|
RenderTexture() = default;
|
||||||
|
~RenderTexture(); // Implemented in SDL2Renderer.cpp
|
||||||
|
|
||||||
|
bool create(unsigned int width, unsigned int height); // Implemented in SDL2Renderer.cpp
|
||||||
|
|
||||||
|
void clear(const Color& color = Color::Black) override; // Implemented in SDL2Renderer.cpp
|
||||||
|
void display(); // Implemented in SDL2Renderer.cpp
|
||||||
|
|
||||||
|
const Texture& getTexture() const { return texture_; }
|
||||||
|
void setSmooth(bool smooth) { texture_.setSmooth(smooth); }
|
||||||
|
bool isSmooth() const { return texture_.isSmooth(); }
|
||||||
|
|
||||||
|
// SDL2-specific: Get FBO handle
|
||||||
|
unsigned int getNativeHandle() const { return fboId_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// RenderWindow
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
namespace Style {
|
||||||
|
enum {
|
||||||
|
None = 0,
|
||||||
|
Titlebar = 1 << 0,
|
||||||
|
Resize = 1 << 1,
|
||||||
|
Close = 1 << 2,
|
||||||
|
Fullscreen = 1 << 3,
|
||||||
|
Default = Titlebar | Resize | Close
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class VideoMode {
|
||||||
|
public:
|
||||||
|
unsigned int width = 0;
|
||||||
|
unsigned int height = 0;
|
||||||
|
unsigned int bitsPerPixel = 32;
|
||||||
|
|
||||||
|
VideoMode() = default;
|
||||||
|
VideoMode(unsigned int w, unsigned int h, unsigned int bpp = 32) : width(w), height(h), bitsPerPixel(bpp) {}
|
||||||
|
|
||||||
|
static VideoMode getDesktopMode(); // Implemented in SDL2Renderer.cpp
|
||||||
|
static const std::vector<VideoMode>& getFullscreenModes(); // Implemented in SDL2Renderer.cpp
|
||||||
|
};
|
||||||
|
|
||||||
|
class RenderWindow : public RenderTarget {
|
||||||
|
bool open_ = false;
|
||||||
|
std::string title_;
|
||||||
|
|
||||||
|
// SDL2-specific handles (void* to avoid SDL header dependency)
|
||||||
|
void* sdlWindow_ = nullptr;
|
||||||
|
void* glContext_ = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RenderWindow() = default;
|
||||||
|
RenderWindow(VideoMode mode, const std::string& title, uint32_t style = Style::Default) {
|
||||||
|
create(mode, title, style);
|
||||||
|
}
|
||||||
|
~RenderWindow(); // Implemented in SDL2Renderer.cpp
|
||||||
|
|
||||||
|
void create(VideoMode mode, const std::string& title, uint32_t style = Style::Default); // Implemented in SDL2Renderer.cpp
|
||||||
|
|
||||||
|
void close(); // Implemented in SDL2Renderer.cpp
|
||||||
|
bool isOpen() const { return open_; }
|
||||||
|
|
||||||
|
void clear(const Color& color = Color::Black) override; // Implemented in SDL2Renderer.cpp
|
||||||
|
void display(); // Implemented in SDL2Renderer.cpp
|
||||||
|
|
||||||
|
void setTitle(const std::string& title); // Implemented in SDL2Renderer.cpp
|
||||||
|
void setFramerateLimit(unsigned int limit); // Implemented in SDL2Renderer.cpp
|
||||||
|
void setVerticalSyncEnabled(bool enabled); // Implemented in SDL2Renderer.cpp
|
||||||
|
void setVisible(bool visible); // Implemented in SDL2Renderer.cpp
|
||||||
|
void setMouseCursorVisible(bool visible); // Implemented in SDL2Renderer.cpp
|
||||||
|
void setMouseCursorGrabbed(bool grabbed); // Implemented in SDL2Renderer.cpp
|
||||||
|
void setKeyRepeatEnabled(bool enabled) {} // No-op for now
|
||||||
|
|
||||||
|
Vector2i getPosition() const; // Implemented in SDL2Renderer.cpp
|
||||||
|
void setPosition(const Vector2i& position); // Implemented in SDL2Renderer.cpp
|
||||||
|
Vector2u getSize() const override { return size_; }
|
||||||
|
void setSize(const Vector2u& size); // Implemented in SDL2Renderer.cpp
|
||||||
|
|
||||||
|
bool pollEvent(Event& event); // Implemented in SDL2Renderer.cpp
|
||||||
|
bool waitEvent(Event& event); // Implemented in SDL2Renderer.cpp
|
||||||
|
|
||||||
|
// SDL2-specific: Access native handles
|
||||||
|
void* getNativeWindowHandle() const { return sdlWindow_; }
|
||||||
|
void* getGLContext() const { return glContext_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Audio Stubs (SDL2_mixer could implement these later)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class SoundBuffer {
|
||||||
|
public:
|
||||||
|
SoundBuffer() = default;
|
||||||
|
bool loadFromFile(const std::string& filename) { return true; } // Stub
|
||||||
|
bool loadFromMemory(const void* data, size_t sizeInBytes) { return true; } // Stub
|
||||||
|
Time getDuration() const { return Time(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Sound {
|
||||||
|
public:
|
||||||
|
enum Status { Stopped, Paused, Playing };
|
||||||
|
|
||||||
|
Sound() = default;
|
||||||
|
Sound(const SoundBuffer& buffer) {}
|
||||||
|
|
||||||
|
void setBuffer(const SoundBuffer& buffer) {}
|
||||||
|
void play() {}
|
||||||
|
void pause() {}
|
||||||
|
void stop() {}
|
||||||
|
|
||||||
|
Status getStatus() const { return Stopped; }
|
||||||
|
void setVolume(float volume) {}
|
||||||
|
float getVolume() const { return 100.0f; }
|
||||||
|
void setLoop(bool loop) {}
|
||||||
|
bool getLoop() const { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Music {
|
||||||
|
public:
|
||||||
|
enum Status { Stopped, Paused, Playing };
|
||||||
|
|
||||||
|
Music() = default;
|
||||||
|
bool openFromFile(const std::string& filename) { return true; } // Stub
|
||||||
|
|
||||||
|
void play() {}
|
||||||
|
void pause() {}
|
||||||
|
void stop() {}
|
||||||
|
|
||||||
|
Status getStatus() const { return Stopped; }
|
||||||
|
void setVolume(float volume) {}
|
||||||
|
float getVolume() const { return 100.0f; }
|
||||||
|
void setLoop(bool loop) {}
|
||||||
|
bool getLoop() const { return false; }
|
||||||
|
Time getDuration() const { return Time(); }
|
||||||
|
Time getPlayingOffset() const { return Time(); }
|
||||||
|
void setPlayingOffset(Time offset) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Input (Keyboard and Mouse)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class Keyboard {
|
||||||
|
public:
|
||||||
|
enum Key {
|
||||||
|
Unknown = -1,
|
||||||
|
A = 0, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||||
|
Num0, Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9,
|
||||||
|
Escape, LControl, LShift, LAlt, LSystem, RControl, RShift, RAlt, RSystem,
|
||||||
|
Menu, LBracket, RBracket, Semicolon, Comma, Period, Apostrophe, Slash, Backslash,
|
||||||
|
Grave, Equal, Hyphen, Space, Enter, Backspace, Tab, PageUp, PageDown, End, Home,
|
||||||
|
Insert, Delete, Add, Subtract, Multiply, Divide,
|
||||||
|
Left, Right, Up, Down,
|
||||||
|
Numpad0, Numpad1, Numpad2, Numpad3, Numpad4, Numpad5, Numpad6, Numpad7, Numpad8, Numpad9,
|
||||||
|
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15,
|
||||||
|
Pause,
|
||||||
|
KeyCount,
|
||||||
|
// Deprecated aliases (SFML 2.x compatibility)
|
||||||
|
Tilde = Grave,
|
||||||
|
Quote = Apostrophe,
|
||||||
|
BackSpace = Backspace,
|
||||||
|
BackSlash = Backslash,
|
||||||
|
SemiColon = Semicolon,
|
||||||
|
Dash = Hyphen
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool isKeyPressed(Key key); // Implemented in SDL2Renderer.cpp
|
||||||
|
};
|
||||||
|
|
||||||
|
class Mouse {
|
||||||
|
public:
|
||||||
|
enum Button { Left, Right, Middle, XButton1, XButton2, ButtonCount };
|
||||||
|
enum Wheel { VerticalWheel, HorizontalWheel };
|
||||||
|
|
||||||
|
static bool isButtonPressed(Button button); // Implemented in SDL2Renderer.cpp
|
||||||
|
static Vector2i getPosition(); // Implemented in SDL2Renderer.cpp
|
||||||
|
static Vector2i getPosition(const RenderWindow& relativeTo); // Implemented in SDL2Renderer.cpp
|
||||||
|
static void setPosition(const Vector2i& position); // Implemented in SDL2Renderer.cpp
|
||||||
|
static void setPosition(const Vector2i& position, const RenderWindow& relativeTo); // Implemented in SDL2Renderer.cpp
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Event System
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
struct Event {
|
||||||
|
enum EventType {
|
||||||
|
Closed,
|
||||||
|
Resized,
|
||||||
|
LostFocus,
|
||||||
|
GainedFocus,
|
||||||
|
TextEntered,
|
||||||
|
KeyPressed,
|
||||||
|
KeyReleased,
|
||||||
|
MouseWheelMoved, // Deprecated
|
||||||
|
MouseWheelScrolled,
|
||||||
|
MouseButtonPressed,
|
||||||
|
MouseButtonReleased,
|
||||||
|
MouseMoved,
|
||||||
|
MouseEntered,
|
||||||
|
MouseLeft,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SizeEvent { unsigned int width, height; };
|
||||||
|
struct KeyEvent { Keyboard::Key code; bool alt, control, shift, system; };
|
||||||
|
struct TextEvent { uint32_t unicode; };
|
||||||
|
struct MouseMoveEvent { int x, y; };
|
||||||
|
struct MouseButtonEvent { Mouse::Button button; int x, y; };
|
||||||
|
struct MouseWheelScrollEvent { Mouse::Wheel wheel; float delta; int x, y; };
|
||||||
|
|
||||||
|
EventType type;
|
||||||
|
union {
|
||||||
|
SizeEvent size;
|
||||||
|
KeyEvent key;
|
||||||
|
TextEvent text;
|
||||||
|
MouseMoveEvent mouseMove;
|
||||||
|
MouseButtonEvent mouseButton;
|
||||||
|
MouseWheelScrollEvent mouseWheelScroll;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// GLSL Types (for shader uniforms)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
namespace Glsl {
|
||||||
|
using Vec2 = Vector2f;
|
||||||
|
|
||||||
|
struct Vec3 {
|
||||||
|
float x = 0, y = 0, z = 0;
|
||||||
|
Vec3() = default;
|
||||||
|
Vec3(float x_, float y_, float z_) : x(x_), y(y_), z(z_) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Vec4 {
|
||||||
|
float x = 0, y = 0, z = 0, w = 0;
|
||||||
|
Vec4() = default;
|
||||||
|
Vec4(float x_, float y_, float z_, float w_) : x(x_), y(y_), z(z_), w(w_) {}
|
||||||
|
Vec4(const Color& c) : x(c.r/255.f), y(c.g/255.f), z(c.b/255.f), w(c.a/255.f) {}
|
||||||
|
};
|
||||||
|
} // namespace Glsl
|
||||||
|
|
||||||
|
// Forward declaration for CurrentTexture
|
||||||
|
struct CurrentTextureType {};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Shader
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class Shader {
|
||||||
|
unsigned int programId_ = 0; // OpenGL shader program handle
|
||||||
|
bool loaded_ = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Type { Vertex, Geometry, Fragment };
|
||||||
|
static const CurrentTextureType CurrentTexture;
|
||||||
|
|
||||||
|
Shader() = default;
|
||||||
|
~Shader(); // Implemented in SDL2Renderer.cpp
|
||||||
|
|
||||||
|
bool loadFromFile(const std::string& filename, Type type); // Implemented in SDL2Renderer.cpp
|
||||||
|
bool loadFromFile(const std::string& vertexFile, const std::string& fragmentFile); // Implemented in SDL2Renderer.cpp
|
||||||
|
bool loadFromMemory(const std::string& shader, Type type); // Implemented in SDL2Renderer.cpp
|
||||||
|
|
||||||
|
void setUniform(const std::string& name, float x); // Implemented in SDL2Renderer.cpp
|
||||||
|
void setUniform(const std::string& name, const Vector2f& v);
|
||||||
|
void setUniform(const std::string& name, const Color& color);
|
||||||
|
void setUniform(const std::string& name, const Texture& texture);
|
||||||
|
void setUniform(const std::string& name, const Glsl::Vec3& v);
|
||||||
|
void setUniform(const std::string& name, const Glsl::Vec4& v);
|
||||||
|
void setUniform(const std::string& name, CurrentTextureType);
|
||||||
|
|
||||||
|
static bool isAvailable(); // Implemented in SDL2Renderer.cpp
|
||||||
|
|
||||||
|
// SDL2-specific: Get program handle
|
||||||
|
unsigned int getNativeHandle() const { return programId_; }
|
||||||
|
bool isLoaded() const { return loaded_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const CurrentTextureType Shader::CurrentTexture{};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Error stream
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
inline std::ostream& err() {
|
||||||
|
static std::stringstream dummy;
|
||||||
|
return dummy;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sf
|
||||||
Loading…
Add table
Add a link
Reference in a new issue