first commit
This commit is contained in:
commit
5c7d1905a9
25 changed files with 4034 additions and 0 deletions
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Build directories
|
||||
build/
|
||||
cmake-build-*/
|
||||
out/
|
||||
.cache/
|
||||
# IDE files
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Compiled files
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# CMake generated
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
Makefile
|
||||
compile_commands.json
|
||||
|
||||
# Dependencies fetched by CMake
|
||||
_deps/
|
||||
external/*/
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
120
CMakeLists.txt
Normal file
120
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
cmake_minimum_required(VERSION 3.20)
|
||||
project(sdl3_flecs_template VERSION 1.0.0 LANGUAGES CXX)
|
||||
|
||||
# C++ Standard
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# Build type defaults to Debug
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
endif()
|
||||
|
||||
# Compiler warnings
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_compile_options(-g -O0)
|
||||
else()
|
||||
add_compile_options(-O2)
|
||||
endif()
|
||||
elseif(MSVC)
|
||||
add_compile_options(/W4)
|
||||
endif()
|
||||
|
||||
# =============================================================================
|
||||
# Dependencies
|
||||
# =============================================================================
|
||||
|
||||
# SDL3 - Using FetchContent for automatic download
|
||||
include(FetchContent)
|
||||
|
||||
# SDL3
|
||||
FetchContent_Declare(
|
||||
SDL3
|
||||
GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
|
||||
GIT_TAG main
|
||||
GIT_SHALLOW TRUE
|
||||
GIT_PROGRESS TRUE
|
||||
)
|
||||
|
||||
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_STATIC ON CACHE BOOL "" FORCE)
|
||||
set(SDL_TEST OFF CACHE BOOL "" FORCE)
|
||||
|
||||
# Platform-specific SDL backends
|
||||
if(EMSCRIPTEN)
|
||||
set(SDL_X11 OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_WAYLAND OFF CACHE BOOL "" FORCE)
|
||||
else()
|
||||
# Disable X11, use Wayland only on Linux
|
||||
set(SDL_X11 OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_WAYLAND ON CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
|
||||
FetchContent_MakeAvailable(SDL3)
|
||||
|
||||
# FLECS
|
||||
FetchContent_Declare(
|
||||
flecs
|
||||
GIT_REPOSITORY https://github.com/SanderMertens/flecs.git
|
||||
GIT_TAG master
|
||||
GIT_SHALLOW TRUE
|
||||
GIT_PROGRESS TRUE
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(flecs)
|
||||
|
||||
# =============================================================================
|
||||
# Main Executable
|
||||
# =============================================================================
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
src/main.cpp
|
||||
src/game.cpp
|
||||
src/systems.cpp
|
||||
src/pipeline.cpp
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
SDL3::SDL3-static
|
||||
flecs::flecs_static
|
||||
)
|
||||
|
||||
# Platform-specific settings
|
||||
if(EMSCRIPTEN)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX ".html")
|
||||
target_link_options(${PROJECT_NAME} PRIVATE
|
||||
-sUSE_SDL=0
|
||||
-sALLOW_MEMORY_GROWTH=1
|
||||
-sTOTAL_MEMORY=67108864
|
||||
-sMAX_WEBGL_VERSION=2
|
||||
--shell-file ${CMAKE_CURRENT_SOURCE_DIR}/web/shell.html
|
||||
)
|
||||
elseif(WIN32)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE winmm imm32 version setupapi)
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
# Linux dependencies are handled by SDL3
|
||||
endif()
|
||||
|
||||
# =============================================================================
|
||||
# Install
|
||||
# =============================================================================
|
||||
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
|
||||
# =============================================================================
|
||||
# CPack (optional packaging)
|
||||
# =============================================================================
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
|
||||
include(CPack)
|
||||
165
README.md
Normal file
165
README.md
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
# SDL3 + FLECS Template
|
||||
|
||||
A scaffold/template for building games using SDL3 and the FLECS Entity Component System.
|
||||
|
||||
## Features
|
||||
|
||||
- **SDL3** - Modern cross-platform multimedia library
|
||||
- **FLECS** - Fast and lightweight Entity Component System
|
||||
- **CMake** - Cross-platform build system with FetchContent for dependencies
|
||||
- **Clean Architecture** - Separated components, systems, and game logic
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
.
|
||||
├── CMakeLists.txt # Build configuration
|
||||
├── include/
|
||||
│ ├── components/ # ECS component definitions
|
||||
│ │ ├── transform.h # Position, Velocity, Rotation, Scale
|
||||
│ │ ├── render.h # Color, Sprite, RectShape, CircleShape
|
||||
│ │ ├── input.h # PlayerControlled, InputState
|
||||
│ │ └── common.h # Name, ToDestroy, Lifetime, Health
|
||||
│ ├── components.h # Master component include
|
||||
│ ├── systems.h # System declarations
|
||||
│ └── game.h # Game context and main loop
|
||||
├── src/
|
||||
│ ├── main.c # Entry point
|
||||
│ ├── game.c # Game initialization and loop
|
||||
│ └── systems.c # ECS system implementations
|
||||
└── external/ # (unused - deps fetched via CMake)
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- CMake 3.20+
|
||||
- C11 compatible compiler (GCC, Clang, MSVC)
|
||||
- Git (for fetching dependencies)
|
||||
|
||||
### Linux/macOS
|
||||
|
||||
```bash
|
||||
# Create build directory
|
||||
mkdir build && cd build
|
||||
|
||||
# Configure
|
||||
cmake ..
|
||||
|
||||
# Build
|
||||
cmake --build .
|
||||
|
||||
# Run
|
||||
./sdl3_flecs_template
|
||||
```
|
||||
|
||||
### Windows (Visual Studio)
|
||||
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
cmake .. -G "Visual Studio 17 2022"
|
||||
cmake --build . --config Release
|
||||
.\Release\sdl3_flecs_template.exe
|
||||
```
|
||||
|
||||
### Windows (MinGW)
|
||||
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
cmake .. -G "MinGW Makefiles"
|
||||
cmake --build .
|
||||
.\sdl3_flecs_template.exe
|
||||
```
|
||||
|
||||
## Usage Guide
|
||||
|
||||
### Adding Components
|
||||
|
||||
1. Create a new header in `include/components/` (e.g., `physics.h`)
|
||||
2. Define your component struct:
|
||||
```c
|
||||
typedef struct RigidBody {
|
||||
float mass;
|
||||
float friction;
|
||||
} RigidBody;
|
||||
```
|
||||
3. Include it in `include/components.h`
|
||||
4. Register it in `src/systems.c`:
|
||||
```c
|
||||
ECS_COMPONENT_DECLARE(RigidBody);
|
||||
// In register_components():
|
||||
ECS_COMPONENT_DEFINE(world, RigidBody);
|
||||
```
|
||||
|
||||
### Adding Systems
|
||||
|
||||
1. Declare the system in `include/systems.h`:
|
||||
```c
|
||||
void PhysicsSystem(ecs_iter_t* it);
|
||||
```
|
||||
2. Implement it in `src/systems.c`:
|
||||
```c
|
||||
void PhysicsSystem(ecs_iter_t* it) {
|
||||
Position* pos = ecs_field(it, Position, 0);
|
||||
RigidBody* rb = ecs_field(it, RigidBody, 1);
|
||||
|
||||
for (int i = 0; i < it->count; i++) {
|
||||
// Physics logic here
|
||||
}
|
||||
}
|
||||
```
|
||||
3. Register it in `register_systems()`:
|
||||
```c
|
||||
ecs_system(world, {
|
||||
.entity = ecs_entity(world, {
|
||||
.name = "PhysicsSystem",
|
||||
.add = ecs_ids(ecs_dependson(EcsOnUpdate))
|
||||
}),
|
||||
.query.terms = {
|
||||
{ .id = ecs_id(Position), .inout = EcsInOut },
|
||||
{ .id = ecs_id(RigidBody), .inout = EcsIn }
|
||||
},
|
||||
.callback = PhysicsSystem
|
||||
});
|
||||
```
|
||||
|
||||
### Creating Entities
|
||||
|
||||
```c
|
||||
ecs_entity_t player = ecs_new(world);
|
||||
|
||||
ecs_set(world, player, Position, { .x = 100, .y = 100 });
|
||||
ecs_set(world, player, Velocity, { .x = 0, .y = 0 });
|
||||
ecs_set(world, player, RectShape, { .width = 32, .height = 32 });
|
||||
ecs_set(world, player, Color, { .r = 0, .g = 255, .b = 0, .a = 255 });
|
||||
ecs_set(world, player, PlayerControlled, { .player_id = 0 });
|
||||
```
|
||||
|
||||
### System Phases
|
||||
|
||||
FLECS provides built-in phases for ordering systems:
|
||||
|
||||
- `EcsOnLoad` - Load external data
|
||||
- `EcsPostLoad` - Process loaded data
|
||||
- `EcsPreUpdate` - Prepare for update
|
||||
- `EcsOnUpdate` - Main update logic
|
||||
- `EcsOnValidate` - Validate state
|
||||
- `EcsPostUpdate` - Post-update cleanup
|
||||
- `EcsPreStore` - Prepare for rendering
|
||||
- `EcsOnStore` - Render/store output
|
||||
|
||||
## Controls
|
||||
|
||||
- **ESC** - Quit
|
||||
- **P** - Pause/unpause
|
||||
|
||||
## License
|
||||
|
||||
This template is released into the public domain. Use it however you like!
|
||||
|
||||
## Resources
|
||||
|
||||
- [SDL3 Documentation](https://wiki.libsdl.org/SDL3)
|
||||
- [FLECS Documentation](https://www.flecs.dev/flecs/md_docs_2Docs.html)
|
||||
- [FLECS Examples](https://github.com/SanderMertens/flecs/tree/master/examples)
|
||||
82
build-web/CPackConfig.cmake
Normal file
82
build-web/CPackConfig.cmake
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
# This file will be configured to contain variables for CPack. These variables
|
||||
# should be set in the CMake list file of the project before CPack module is
|
||||
# included. The list of available CPACK_xxx variables and their associated
|
||||
# documentation may be obtained using
|
||||
# cpack --help-variable-list
|
||||
#
|
||||
# Some variables are common to all generators (e.g. CPACK_PACKAGE_NAME)
|
||||
# and some are specific to a generator
|
||||
# (e.g. CPACK_NSIS_EXTRA_INSTALL_COMMANDS). The generator specific variables
|
||||
# usually begin with CPACK_<GENNAME>_xxxx.
|
||||
|
||||
|
||||
set(CPACK_BINARY_DEB "OFF")
|
||||
set(CPACK_BINARY_FREEBSD "OFF")
|
||||
set(CPACK_BINARY_IFW "OFF")
|
||||
set(CPACK_BINARY_NSIS "OFF")
|
||||
set(CPACK_BINARY_RPM "OFF")
|
||||
set(CPACK_BINARY_STGZ "ON")
|
||||
set(CPACK_BINARY_TBZ2 "OFF")
|
||||
set(CPACK_BINARY_TGZ "ON")
|
||||
set(CPACK_BINARY_TXZ "OFF")
|
||||
set(CPACK_BINARY_TZ "ON")
|
||||
set(CPACK_BUILD_SOURCE_DIRS "/home/saarsena/Dev/renderpip;/home/saarsena/Dev/renderpip/build-web")
|
||||
set(CPACK_CMAKE_GENERATOR "Unix Makefiles")
|
||||
set(CPACK_COMPONENT_UNSPECIFIED_HIDDEN "TRUE")
|
||||
set(CPACK_COMPONENT_UNSPECIFIED_REQUIRED "TRUE")
|
||||
set(CPACK_DEFAULT_PACKAGE_DESCRIPTION_FILE "/usr/share/cmake/Templates/CPack.GenericDescription.txt")
|
||||
set(CPACK_DEFAULT_PACKAGE_DESCRIPTION_SUMMARY "sdl3_flecs_template built using CMake")
|
||||
set(CPACK_GENERATOR "STGZ;TGZ;TZ")
|
||||
set(CPACK_INNOSETUP_ARCHITECTURE "x86")
|
||||
set(CPACK_INSTALL_CMAKE_PROJECTS "/home/saarsena/Dev/renderpip/build-web;sdl3_flecs_template;ALL;/")
|
||||
set(CPACK_INSTALL_PREFIX "/home/saarsena/emsdk/upstream/emscripten/cache/sysroot")
|
||||
set(CPACK_MODULE_PATH "/home/saarsena/emsdk/upstream/emscripten/cmake/Modules;/home/saarsena/emsdk/upstream/emscripten/cmake/Modules;/home/saarsena/emsdk/upstream/emscripten/cmake/Modules;/home/saarsena/emsdk/upstream/emscripten/cmake/Modules")
|
||||
set(CPACK_NSIS_DISPLAY_NAME "sdl3_flecs_template 1.0.0")
|
||||
set(CPACK_NSIS_INSTALLER_ICON_CODE "")
|
||||
set(CPACK_NSIS_INSTALLER_MUI_ICON_CODE "")
|
||||
set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES")
|
||||
set(CPACK_NSIS_PACKAGE_NAME "sdl3_flecs_template 1.0.0")
|
||||
set(CPACK_NSIS_UNINSTALL_NAME "Uninstall")
|
||||
set(CPACK_OBJCOPY_EXECUTABLE "/usr/bin/llvm-objcopy")
|
||||
set(CPACK_OBJDUMP_EXECUTABLE "/usr/bin/llvm-objdump")
|
||||
set(CPACK_OUTPUT_CONFIG_FILE "/home/saarsena/Dev/renderpip/build-web/CPackConfig.cmake")
|
||||
set(CPACK_PACKAGE_DEFAULT_LOCATION "/")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_FILE "/usr/share/cmake/Templates/CPack.GenericDescription.txt")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "sdl3_flecs_template built using CMake")
|
||||
set(CPACK_PACKAGE_FILE_NAME "sdl3_flecs_template-1.0.0-Emscripten")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "sdl3_flecs_template 1.0.0")
|
||||
set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "sdl3_flecs_template 1.0.0")
|
||||
set(CPACK_PACKAGE_NAME "sdl3_flecs_template")
|
||||
set(CPACK_PACKAGE_RELOCATABLE "true")
|
||||
set(CPACK_PACKAGE_VENDOR "Humanity")
|
||||
set(CPACK_PACKAGE_VERSION "1.0.0")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "1")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "0")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "0")
|
||||
set(CPACK_PROJECT_NAME "sdl3_flecs_template")
|
||||
set(CPACK_PROJECT_VERSION "1.0.0")
|
||||
set(CPACK_READELF_EXECUTABLE "/usr/bin/llvm-readelf")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "/usr/share/cmake/Templates/CPack.GenericLicense.txt")
|
||||
set(CPACK_RESOURCE_FILE_README "/usr/share/cmake/Templates/CPack.GenericDescription.txt")
|
||||
set(CPACK_RESOURCE_FILE_WELCOME "/usr/share/cmake/Templates/CPack.GenericWelcome.txt")
|
||||
set(CPACK_SET_DESTDIR "OFF")
|
||||
set(CPACK_SOURCE_GENERATOR "TBZ2;TGZ;TXZ;TZ")
|
||||
set(CPACK_SOURCE_OUTPUT_CONFIG_FILE "/home/saarsena/Dev/renderpip/build-web/CPackSourceConfig.cmake")
|
||||
set(CPACK_SOURCE_RPM "OFF")
|
||||
set(CPACK_SOURCE_TBZ2 "ON")
|
||||
set(CPACK_SOURCE_TGZ "ON")
|
||||
set(CPACK_SOURCE_TXZ "ON")
|
||||
set(CPACK_SOURCE_TZ "ON")
|
||||
set(CPACK_SOURCE_ZIP "OFF")
|
||||
set(CPACK_SYSTEM_NAME "Emscripten")
|
||||
set(CPACK_THREADS "1")
|
||||
set(CPACK_TOPLEVEL_TAG "Emscripten")
|
||||
set(CPACK_WIX_SIZEOF_VOID_P "4")
|
||||
|
||||
if(NOT CPACK_PROPERTIES_FILE)
|
||||
set(CPACK_PROPERTIES_FILE "/home/saarsena/Dev/renderpip/build-web/CPackProperties.cmake")
|
||||
endif()
|
||||
|
||||
if(EXISTS ${CPACK_PROPERTIES_FILE})
|
||||
include(${CPACK_PROPERTIES_FILE})
|
||||
endif()
|
||||
90
build-web/CPackSourceConfig.cmake
Normal file
90
build-web/CPackSourceConfig.cmake
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# This file will be configured to contain variables for CPack. These variables
|
||||
# should be set in the CMake list file of the project before CPack module is
|
||||
# included. The list of available CPACK_xxx variables and their associated
|
||||
# documentation may be obtained using
|
||||
# cpack --help-variable-list
|
||||
#
|
||||
# Some variables are common to all generators (e.g. CPACK_PACKAGE_NAME)
|
||||
# and some are specific to a generator
|
||||
# (e.g. CPACK_NSIS_EXTRA_INSTALL_COMMANDS). The generator specific variables
|
||||
# usually begin with CPACK_<GENNAME>_xxxx.
|
||||
|
||||
|
||||
set(CPACK_BINARY_DEB "OFF")
|
||||
set(CPACK_BINARY_FREEBSD "OFF")
|
||||
set(CPACK_BINARY_IFW "OFF")
|
||||
set(CPACK_BINARY_NSIS "OFF")
|
||||
set(CPACK_BINARY_RPM "OFF")
|
||||
set(CPACK_BINARY_STGZ "ON")
|
||||
set(CPACK_BINARY_TBZ2 "OFF")
|
||||
set(CPACK_BINARY_TGZ "ON")
|
||||
set(CPACK_BINARY_TXZ "OFF")
|
||||
set(CPACK_BINARY_TZ "ON")
|
||||
set(CPACK_BUILD_SOURCE_DIRS "/home/saarsena/Dev/renderpip;/home/saarsena/Dev/renderpip/build-web")
|
||||
set(CPACK_CMAKE_GENERATOR "Unix Makefiles")
|
||||
set(CPACK_COMPONENT_UNSPECIFIED_HIDDEN "TRUE")
|
||||
set(CPACK_COMPONENT_UNSPECIFIED_REQUIRED "TRUE")
|
||||
set(CPACK_DEFAULT_PACKAGE_DESCRIPTION_FILE "/usr/share/cmake/Templates/CPack.GenericDescription.txt")
|
||||
set(CPACK_DEFAULT_PACKAGE_DESCRIPTION_SUMMARY "sdl3_flecs_template built using CMake")
|
||||
set(CPACK_GENERATOR "TBZ2;TGZ;TXZ;TZ")
|
||||
set(CPACK_IGNORE_FILES "/CVS/;/\\.svn/;/\\.bzr/;/\\.hg/;/\\.git/;\\.swp\$;\\.#;/#")
|
||||
set(CPACK_INNOSETUP_ARCHITECTURE "x86")
|
||||
set(CPACK_INSTALLED_DIRECTORIES "/home/saarsena/Dev/renderpip;/")
|
||||
set(CPACK_INSTALL_CMAKE_PROJECTS "")
|
||||
set(CPACK_INSTALL_PREFIX "/home/saarsena/emsdk/upstream/emscripten/cache/sysroot")
|
||||
set(CPACK_MODULE_PATH "/home/saarsena/emsdk/upstream/emscripten/cmake/Modules;/home/saarsena/emsdk/upstream/emscripten/cmake/Modules;/home/saarsena/emsdk/upstream/emscripten/cmake/Modules;/home/saarsena/emsdk/upstream/emscripten/cmake/Modules")
|
||||
set(CPACK_NSIS_DISPLAY_NAME "sdl3_flecs_template 1.0.0")
|
||||
set(CPACK_NSIS_INSTALLER_ICON_CODE "")
|
||||
set(CPACK_NSIS_INSTALLER_MUI_ICON_CODE "")
|
||||
set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES")
|
||||
set(CPACK_NSIS_PACKAGE_NAME "sdl3_flecs_template 1.0.0")
|
||||
set(CPACK_NSIS_UNINSTALL_NAME "Uninstall")
|
||||
set(CPACK_OBJCOPY_EXECUTABLE "/usr/bin/llvm-objcopy")
|
||||
set(CPACK_OBJDUMP_EXECUTABLE "/usr/bin/llvm-objdump")
|
||||
set(CPACK_OUTPUT_CONFIG_FILE "/home/saarsena/Dev/renderpip/build-web/CPackConfig.cmake")
|
||||
set(CPACK_PACKAGE_DEFAULT_LOCATION "/")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_FILE "/usr/share/cmake/Templates/CPack.GenericDescription.txt")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "sdl3_flecs_template built using CMake")
|
||||
set(CPACK_PACKAGE_FILE_NAME "sdl3_flecs_template-1.0.0-Source")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "sdl3_flecs_template 1.0.0")
|
||||
set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "sdl3_flecs_template 1.0.0")
|
||||
set(CPACK_PACKAGE_NAME "sdl3_flecs_template")
|
||||
set(CPACK_PACKAGE_RELOCATABLE "true")
|
||||
set(CPACK_PACKAGE_VENDOR "Humanity")
|
||||
set(CPACK_PACKAGE_VERSION "1.0.0")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "1")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "0")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "0")
|
||||
set(CPACK_PROJECT_NAME "sdl3_flecs_template")
|
||||
set(CPACK_PROJECT_VERSION "1.0.0")
|
||||
set(CPACK_READELF_EXECUTABLE "/usr/bin/llvm-readelf")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "/usr/share/cmake/Templates/CPack.GenericLicense.txt")
|
||||
set(CPACK_RESOURCE_FILE_README "/usr/share/cmake/Templates/CPack.GenericDescription.txt")
|
||||
set(CPACK_RESOURCE_FILE_WELCOME "/usr/share/cmake/Templates/CPack.GenericWelcome.txt")
|
||||
set(CPACK_RPM_PACKAGE_SOURCES "ON")
|
||||
set(CPACK_SET_DESTDIR "OFF")
|
||||
set(CPACK_SOURCE_GENERATOR "TBZ2;TGZ;TXZ;TZ")
|
||||
set(CPACK_SOURCE_IGNORE_FILES "/CVS/;/\\.svn/;/\\.bzr/;/\\.hg/;/\\.git/;\\.swp\$;\\.#;/#")
|
||||
set(CPACK_SOURCE_INSTALLED_DIRECTORIES "/home/saarsena/Dev/renderpip;/")
|
||||
set(CPACK_SOURCE_OUTPUT_CONFIG_FILE "/home/saarsena/Dev/renderpip/build-web/CPackSourceConfig.cmake")
|
||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME "sdl3_flecs_template-1.0.0-Source")
|
||||
set(CPACK_SOURCE_RPM "OFF")
|
||||
set(CPACK_SOURCE_TBZ2 "ON")
|
||||
set(CPACK_SOURCE_TGZ "ON")
|
||||
set(CPACK_SOURCE_TOPLEVEL_TAG "Emscripten-Source")
|
||||
set(CPACK_SOURCE_TXZ "ON")
|
||||
set(CPACK_SOURCE_TZ "ON")
|
||||
set(CPACK_SOURCE_ZIP "OFF")
|
||||
set(CPACK_STRIP_FILES "")
|
||||
set(CPACK_SYSTEM_NAME "Emscripten")
|
||||
set(CPACK_THREADS "1")
|
||||
set(CPACK_TOPLEVEL_TAG "Emscripten-Source")
|
||||
set(CPACK_WIX_SIZEOF_VOID_P "4")
|
||||
|
||||
if(NOT CPACK_PROPERTIES_FILE)
|
||||
set(CPACK_PROPERTIES_FILE "/home/saarsena/Dev/renderpip/build-web/CPackProperties.cmake")
|
||||
endif()
|
||||
|
||||
if(EXISTS ${CPACK_PROPERTIES_FILE})
|
||||
include(${CPACK_PROPERTIES_FILE})
|
||||
endif()
|
||||
1
build-web/sdl3_flecs_template.html
Normal file
1
build-web/sdl3_flecs_template.html
Normal file
|
|
@ -0,0 +1 @@
|
|||
<!doctypehtml><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1"name=viewport><title>Render Pipeline Visualizer</title><style>*{margin:0;padding:0;box-sizing:border-box}body{background:#1e1e28;display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;overflow:hidden;font-family:'Courier New',monospace;color:#aaa}canvas#canvas{width:100vw;height:100vh;display:block;object-fit:contain}#output{display:none}#status{position:fixed;bottom:4px;right:8px;font-size:.75em;color:#444}</style></head><body><canvas height=720 id=canvas oncontextmenu=event.preventDefault() tabindex=-1 width=1280></canvas><div id=status>Loading...</div><textarea id=output rows=8 style=display:none></textarea><script>var Module={print:function(t){console.log(t)},printErr:function(t){console.error(t)},canvas:document.getElementById("canvas"),setStatus:function(t){t&&(document.getElementById("status").textContent=t)},onRuntimeInitialized:function(){document.getElementById("status").textContent="Press H for controls"},totalDependencies:0,monitorRunDependencies:function(t){this.totalDependencies=Math.max(this.totalDependencies,t),t?Module.setStatus("Loading... ("+(this.totalDependencies-t)+"/"+this.totalDependencies+")"):Module.setStatus("")}};Module.setStatus("Downloading...")</script><script async src=sdl3_flecs_template.js></script></body></html>
|
||||
1
build-web/sdl3_flecs_template.js
Normal file
1
build-web/sdl3_flecs_template.js
Normal file
File diff suppressed because one or more lines are too long
BIN
build-web/sdl3_flecs_template.wasm
Executable file
BIN
build-web/sdl3_flecs_template.wasm
Executable file
Binary file not shown.
27
build_web.sh
Executable file
27
build_web.sh
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
#!/bin/bash
|
||||
# Build the Render Pipeline Visualizer for the web using Emscripten
|
||||
set -e
|
||||
|
||||
# Source emsdk if not already in PATH
|
||||
if ! command -v emcc &> /dev/null; then
|
||||
source ~/emsdk/emsdk_env.sh
|
||||
fi
|
||||
|
||||
BUILD_DIR="build-web"
|
||||
mkdir -p "$BUILD_DIR"
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
emcmake cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DSDL_SHARED=OFF \
|
||||
-DSDL_STATIC=ON \
|
||||
-DSDL_TEST=OFF \
|
||||
-DSDL_X11=OFF \
|
||||
-DSDL_WAYLAND=OFF
|
||||
|
||||
emmake make -j$(nproc)
|
||||
|
||||
echo ""
|
||||
echo "Build complete! Files are in $BUILD_DIR/"
|
||||
echo "To test locally: cd $BUILD_DIR && python3 -m http.server 8080"
|
||||
echo "Then open http://localhost:8080/sdl3_flecs_template.html"
|
||||
11
include/components.hpp
Normal file
11
include/components.hpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* @file components.hpp
|
||||
* @brief Master include for all ECS components
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "components/transform.hpp"
|
||||
#include "components/render.hpp"
|
||||
#include "components/input.hpp"
|
||||
#include "components/common.hpp"
|
||||
24
include/components/common.hpp
Normal file
24
include/components/common.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* @file common.hpp
|
||||
* @brief Common/utility components
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Name {
|
||||
std::string value;
|
||||
};
|
||||
|
||||
// Tag component - empty struct marks entities for destruction
|
||||
struct ToDestroy {};
|
||||
|
||||
struct Lifetime {
|
||||
float remaining = 0.0f;
|
||||
};
|
||||
|
||||
struct Health {
|
||||
float current = 100.0f;
|
||||
float max = 100.0f;
|
||||
};
|
||||
21
include/components/input.hpp
Normal file
21
include/components/input.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* @file input.hpp
|
||||
* @brief Input-related components
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct PlayerControlled {
|
||||
int player_id = 0;
|
||||
};
|
||||
|
||||
struct InputState {
|
||||
bool move_up = false;
|
||||
bool move_down = false;
|
||||
bool move_left = false;
|
||||
bool move_right = false;
|
||||
bool action_primary = false;
|
||||
bool action_secondary = false;
|
||||
float mouse_x = 0.0f;
|
||||
float mouse_y = 0.0f;
|
||||
};
|
||||
32
include/components/render.hpp
Normal file
32
include/components/render.hpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* @file render.hpp
|
||||
* @brief Rendering-related components
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <cstdint>
|
||||
|
||||
struct Color {
|
||||
uint8_t r = 255;
|
||||
uint8_t g = 255;
|
||||
uint8_t b = 255;
|
||||
uint8_t a = 255;
|
||||
};
|
||||
|
||||
struct Sprite {
|
||||
SDL_Texture* texture = nullptr;
|
||||
SDL_FRect src_rect{};
|
||||
int z_order = 0;
|
||||
bool visible = true;
|
||||
};
|
||||
|
||||
struct RectShape {
|
||||
float width = 0.0f;
|
||||
float height = 0.0f;
|
||||
};
|
||||
|
||||
struct CircleShape {
|
||||
float radius = 0.0f;
|
||||
};
|
||||
25
include/components/transform.hpp
Normal file
25
include/components/transform.hpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* @file transform.hpp
|
||||
* @brief Transform components for position, velocity, and rotation
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct Position {
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
};
|
||||
|
||||
struct Velocity {
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
};
|
||||
|
||||
struct Rotation {
|
||||
float angle = 0.0f;
|
||||
};
|
||||
|
||||
struct Scale {
|
||||
float x = 1.0f;
|
||||
float y = 1.0f;
|
||||
};
|
||||
52
include/game.hpp
Normal file
52
include/game.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* @file game.hpp
|
||||
* @brief Main game state and context
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <flecs.h>
|
||||
|
||||
struct WindowConfig {
|
||||
const char *title = "RIDGE RACER 37";
|
||||
int width = 1280;
|
||||
int height = 720;
|
||||
SDL_WindowFlags flags = 0;
|
||||
};
|
||||
|
||||
struct PipelineState;
|
||||
|
||||
/**
|
||||
* @brief Game context containing all core resources
|
||||
*/
|
||||
struct GameContext {
|
||||
// SDL Resources
|
||||
SDL_Window *window = nullptr;
|
||||
SDL_Renderer *renderer = nullptr;
|
||||
|
||||
// FLECS World (C++ wrapper)
|
||||
flecs::world ecs;
|
||||
|
||||
// Timing
|
||||
uint64_t last_time = 0;
|
||||
float delta_time = 0.0f;
|
||||
|
||||
// State
|
||||
bool running = true;
|
||||
bool paused = false;
|
||||
|
||||
// Window info
|
||||
int window_width = 0;
|
||||
int window_height = 0;
|
||||
|
||||
// Pipeline visualization
|
||||
PipelineState *pipeline = nullptr;
|
||||
};
|
||||
|
||||
bool game_init(GameContext &ctx, const WindowConfig &config);
|
||||
void game_shutdown(GameContext &ctx);
|
||||
void game_process_events(GameContext &ctx);
|
||||
void game_update(GameContext &ctx);
|
||||
void game_render(GameContext &ctx);
|
||||
bool game_loop(GameContext &ctx);
|
||||
129
include/math3d.hpp
Normal file
129
include/math3d.hpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/**
|
||||
* @file math3d.hpp
|
||||
* @brief Minimal 3D math library for render pipeline visualization
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
constexpr float PI = 3.14159265358979323846f;
|
||||
|
||||
struct Vec3 {
|
||||
float x = 0, y = 0, z = 0;
|
||||
Vec3 operator+(Vec3 o) const { return {x+o.x, y+o.y, z+o.z}; }
|
||||
Vec3 operator-(Vec3 o) const { return {x-o.x, y-o.y, z-o.z}; }
|
||||
Vec3 operator*(float s) const { return {x*s, y*s, z*s}; }
|
||||
float dot(Vec3 o) const { return x*o.x + y*o.y + z*o.z; }
|
||||
Vec3 cross(Vec3 o) const { return {y*o.z - z*o.y, z*o.x - x*o.z, x*o.y - y*o.x}; }
|
||||
float length() const { return std::sqrt(x*x + y*y + z*z); }
|
||||
Vec3 normalized() const {
|
||||
float l = length();
|
||||
return l > 0 ? Vec3{x/l, y/l, z/l} : Vec3{0, 0, 0};
|
||||
}
|
||||
};
|
||||
|
||||
inline Vec3 lerp(Vec3 a, Vec3 b, float t) {
|
||||
return {a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t};
|
||||
}
|
||||
|
||||
inline float smoothstep(float t) {
|
||||
return t * t * (3.0f - 2.0f * t);
|
||||
}
|
||||
|
||||
struct Vec4 {
|
||||
float x = 0, y = 0, z = 0, w = 1;
|
||||
Vec3 xyz() const { return {x, y, z}; }
|
||||
Vec3 perspDiv() const {
|
||||
return w != 0 ? Vec3{x/w, y/w, z/w} : Vec3{x, y, z};
|
||||
}
|
||||
};
|
||||
|
||||
struct Mat4 {
|
||||
float m[4][4] = {};
|
||||
|
||||
static Mat4 identity() {
|
||||
Mat4 r;
|
||||
r.m[0][0] = r.m[1][1] = r.m[2][2] = r.m[3][3] = 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
Vec4 operator*(Vec4 v) const {
|
||||
return {
|
||||
m[0][0]*v.x + m[0][1]*v.y + m[0][2]*v.z + m[0][3]*v.w,
|
||||
m[1][0]*v.x + m[1][1]*v.y + m[1][2]*v.z + m[1][3]*v.w,
|
||||
m[2][0]*v.x + m[2][1]*v.y + m[2][2]*v.z + m[2][3]*v.w,
|
||||
m[3][0]*v.x + m[3][1]*v.y + m[3][2]*v.z + m[3][3]*v.w,
|
||||
};
|
||||
}
|
||||
|
||||
Mat4 operator*(const Mat4& o) const {
|
||||
Mat4 r;
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int j = 0; j < 4; j++)
|
||||
for (int k = 0; k < 4; k++)
|
||||
r.m[i][j] += m[i][k] * o.m[k][j];
|
||||
return r;
|
||||
}
|
||||
|
||||
static Mat4 translate(float x, float y, float z) {
|
||||
Mat4 r = identity();
|
||||
r.m[0][3] = x; r.m[1][3] = y; r.m[2][3] = z;
|
||||
return r;
|
||||
}
|
||||
|
||||
static Mat4 scale(float sx, float sy, float sz) {
|
||||
Mat4 r = identity();
|
||||
r.m[0][0] = sx; r.m[1][1] = sy; r.m[2][2] = sz;
|
||||
return r;
|
||||
}
|
||||
|
||||
static Mat4 rotateX(float angle) {
|
||||
float c = std::cos(angle), s = std::sin(angle);
|
||||
Mat4 r = identity();
|
||||
r.m[1][1] = c; r.m[1][2] = -s;
|
||||
r.m[2][1] = s; r.m[2][2] = c;
|
||||
return r;
|
||||
}
|
||||
|
||||
static Mat4 rotateY(float angle) {
|
||||
float c = std::cos(angle), s = std::sin(angle);
|
||||
Mat4 r = identity();
|
||||
r.m[0][0] = c; r.m[0][2] = s;
|
||||
r.m[2][0] = -s; r.m[2][2] = c;
|
||||
return r;
|
||||
}
|
||||
|
||||
static Mat4 rotateZ(float angle) {
|
||||
float c = std::cos(angle), s = std::sin(angle);
|
||||
Mat4 r = identity();
|
||||
r.m[0][0] = c; r.m[0][1] = -s;
|
||||
r.m[1][0] = s; r.m[1][1] = c;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Standard OpenGL-style perspective (right-handed, -Z forward)
|
||||
static Mat4 perspective(float fov, float aspect, float near, float far) {
|
||||
float t = std::tan(fov * 0.5f);
|
||||
Mat4 r;
|
||||
r.m[0][0] = 1.0f / (aspect * t);
|
||||
r.m[1][1] = 1.0f / t;
|
||||
r.m[2][2] = -(far + near) / (far - near);
|
||||
r.m[2][3] = -(2.0f * far * near) / (far - near);
|
||||
r.m[3][2] = -1.0f;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Standard lookAt (right-handed)
|
||||
static Mat4 lookAt(Vec3 eye, Vec3 target, Vec3 up) {
|
||||
Vec3 f = (target - eye).normalized();
|
||||
Vec3 r = f.cross(up).normalized();
|
||||
Vec3 u = r.cross(f);
|
||||
Mat4 mat = identity();
|
||||
mat.m[0][0] = r.x; mat.m[0][1] = r.y; mat.m[0][2] = r.z; mat.m[0][3] = -r.dot(eye);
|
||||
mat.m[1][0] = u.x; mat.m[1][1] = u.y; mat.m[1][2] = u.z; mat.m[1][3] = -u.dot(eye);
|
||||
mat.m[2][0] = -f.x; mat.m[2][1] = -f.y; mat.m[2][2] = -f.z; mat.m[2][3] = f.dot(eye);
|
||||
return mat;
|
||||
}
|
||||
};
|
||||
129
include/pipeline.hpp
Normal file
129
include/pipeline.hpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/**
|
||||
* @file pipeline.hpp
|
||||
* @brief Render pipeline visualization state and interface
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "math3d.hpp"
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
struct GameContext;
|
||||
|
||||
constexpr int NUM_CUBE_VERTS = 8;
|
||||
constexpr int NUM_CUBE_EDGES = 12;
|
||||
constexpr int NUM_TRI_VERTS = 3;
|
||||
constexpr int NUM_TRI_EDGES = 3;
|
||||
|
||||
struct PipelineState {
|
||||
int current_stage = 0; // 0-5
|
||||
int prev_stage = 0;
|
||||
float transition_t = 1.0f; // 0→1 animation progress (1 = complete)
|
||||
float fade_alpha = 1.0f; // for 3D↔2D mode transitions
|
||||
|
||||
// Interpolated vertices for rendering during transitions
|
||||
Vec3 display_verts[NUM_CUBE_VERTS];
|
||||
|
||||
// Vertex selection
|
||||
int selected_vertex = -1; // -1 = none, 0-7 = selected vertex index
|
||||
bool pending_click = false;
|
||||
float click_x = 0, click_y = 0;
|
||||
|
||||
// Object transform parameters (user-adjustable)
|
||||
float obj_rot_y = 0.0f;
|
||||
float obj_pos_x = 2.0f;
|
||||
float obj_pos_y = 1.0f;
|
||||
float obj_pos_z = -3.0f;
|
||||
float obj_scale = 1.0f;
|
||||
bool auto_rotate = false;
|
||||
|
||||
// Display camera (orbit around scene for visualization)
|
||||
float cam_orbit_pitch = 25.0f; // degrees
|
||||
float cam_orbit_yaw = -30.0f; // degrees
|
||||
float cam_distance = 10.0f;
|
||||
|
||||
// Pipeline camera (the one being visualized in the pipeline)
|
||||
float pipe_cam_x = 0.0f;
|
||||
float pipe_cam_y = 2.0f;
|
||||
float pipe_cam_z = 5.0f;
|
||||
float pipe_cam_fov = 1.0f; // radians (~57 degrees)
|
||||
float pipe_cam_near = 0.1f;
|
||||
float pipe_cam_far = 100.0f;
|
||||
|
||||
// Mouse drag state
|
||||
bool mouse_down = false;
|
||||
float mouse_start_x = 0, mouse_start_y = 0;
|
||||
float orbit_start_pitch = 0, orbit_start_yaw = 0;
|
||||
|
||||
// Slider drag state
|
||||
int dragging_slider = -1; // -1 = none, 0+ = slider index
|
||||
bool show_sliders = true;
|
||||
|
||||
// First-person camera view (picture-in-picture)
|
||||
bool show_first_person = false;
|
||||
|
||||
// Matrix color breakdown
|
||||
bool show_matrix_breakdown = false;
|
||||
|
||||
// Vertex shader simulation
|
||||
bool shader_mode = false;
|
||||
float shader_amplitude = 0.3f;
|
||||
float shader_frequency = 2.0f;
|
||||
float shader_time = 0.0f;
|
||||
|
||||
// Help overlay
|
||||
bool show_help = false;
|
||||
|
||||
// Clipping visualizer
|
||||
bool show_clipping = false;
|
||||
struct ClippedFace {
|
||||
static constexpr int MAX_VERTS = 24;
|
||||
Vec4 verts[MAX_VERTS];
|
||||
int count = 0;
|
||||
bool was_clipped = false;
|
||||
};
|
||||
ClippedFace clipped_faces[6];
|
||||
|
||||
// Depth buffer / Z-fighting demo
|
||||
bool show_depth_viz = false;
|
||||
bool show_zfight_plane = false;
|
||||
float zfight_plane_z = -3.0f;
|
||||
static constexpr int NUM_PLANE_VERTS = 4;
|
||||
Vec4 plane_world[4];
|
||||
Vec3 plane_screen[4];
|
||||
Vec3 plane_display[4];
|
||||
|
||||
// Input flags (set per event, cleared after processing)
|
||||
bool key_left = false;
|
||||
bool key_right = false;
|
||||
bool key_space = false;
|
||||
bool key_r = false;
|
||||
bool key_num[6] = {};
|
||||
|
||||
// Computed transforms
|
||||
Mat4 model_matrix;
|
||||
Mat4 view_matrix;
|
||||
Mat4 proj_matrix;
|
||||
|
||||
// Transformed vertices at each pipeline stage (cube)
|
||||
Vec4 verts_object[NUM_CUBE_VERTS];
|
||||
Vec4 verts_world[NUM_CUBE_VERTS];
|
||||
Vec4 verts_view[NUM_CUBE_VERTS];
|
||||
Vec4 verts_clip[NUM_CUBE_VERTS];
|
||||
Vec3 verts_ndc[NUM_CUBE_VERTS];
|
||||
Vec3 verts_screen[NUM_CUBE_VERTS];
|
||||
|
||||
// Second shape: triangle
|
||||
bool show_triangle = false;
|
||||
Vec4 tri_object[NUM_TRI_VERTS];
|
||||
Vec4 tri_world[NUM_TRI_VERTS];
|
||||
Vec4 tri_view[NUM_TRI_VERTS];
|
||||
Vec4 tri_clip[NUM_TRI_VERTS];
|
||||
Vec3 tri_ndc[NUM_TRI_VERTS];
|
||||
Vec3 tri_screen[NUM_TRI_VERTS];
|
||||
Vec3 tri_display[NUM_TRI_VERTS];
|
||||
};
|
||||
|
||||
void pipeline_handle_event(PipelineState& ps, const SDL_Event& event);
|
||||
void pipeline_update(PipelineState& ps, GameContext& ctx);
|
||||
void pipeline_render(PipelineState& ps, GameContext& ctx);
|
||||
16
include/systems.hpp
Normal file
16
include/systems.hpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* @file systems.hpp
|
||||
* @brief ECS System registration
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <flecs.h>
|
||||
|
||||
// Forward declaration
|
||||
struct GameContext;
|
||||
|
||||
/**
|
||||
* @brief Register all components and systems with the ECS world
|
||||
*/
|
||||
void register_systems(flecs::world& ecs, GameContext* ctx);
|
||||
38
remove_comments.py
Normal file
38
remove_comments.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Remove all C/C++ comments from a file."""
|
||||
|
||||
import sys
|
||||
import re
|
||||
|
||||
|
||||
def remove_comments(source):
|
||||
pattern = re.compile(
|
||||
r'//.*?$|/\*.*?\*/|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'',
|
||||
re.DOTALL | re.MULTILINE,
|
||||
)
|
||||
|
||||
def replacer(match):
|
||||
s = match.group(0)
|
||||
if s.startswith("/"):
|
||||
return " " if s.startswith("/*") else ""
|
||||
return s
|
||||
|
||||
return pattern.sub(replacer, source)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print(f"Usage: {sys.argv[0]} <file> [--inplace]", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
path = sys.argv[1]
|
||||
inplace = "--inplace" in sys.argv
|
||||
|
||||
with open(path) as f:
|
||||
result = remove_comments(f.read())
|
||||
|
||||
if inplace:
|
||||
with open(path, "w") as f:
|
||||
f.write(result)
|
||||
else:
|
||||
print(result, end="")
|
||||
BIN
renderpip-web.zip
Normal file
BIN
renderpip-web.zip
Normal file
Binary file not shown.
155
src/game.cpp
Normal file
155
src/game.cpp
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/**
|
||||
* @file game.cpp
|
||||
* @brief Main game implementation
|
||||
*/
|
||||
|
||||
#include "game.hpp"
|
||||
#include "systems.hpp"
|
||||
#include "components.hpp"
|
||||
#include "pipeline.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
bool game_init(GameContext& ctx, const WindowConfig& config) {
|
||||
// Initialize SDL3
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) {
|
||||
SDL_Log("Failed to initialize SDL: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create window
|
||||
ctx.window = SDL_CreateWindow(
|
||||
config.title,
|
||||
config.width,
|
||||
config.height,
|
||||
config.flags
|
||||
);
|
||||
|
||||
if (!ctx.window) {
|
||||
SDL_Log("Failed to create window: %s", SDL_GetError());
|
||||
game_shutdown(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
ctx.window_width = config.width;
|
||||
ctx.window_height = config.height;
|
||||
|
||||
// Create renderer
|
||||
ctx.renderer = SDL_CreateRenderer(ctx.window, nullptr);
|
||||
if (!ctx.renderer) {
|
||||
SDL_Log("Failed to create renderer: %s", SDL_GetError());
|
||||
game_shutdown(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable VSync
|
||||
SDL_SetRenderVSync(ctx.renderer, 1);
|
||||
|
||||
// Register components and systems
|
||||
register_systems(ctx.ecs, &ctx);
|
||||
|
||||
// Initialize timing
|
||||
ctx.last_time = SDL_GetPerformanceCounter();
|
||||
ctx.delta_time = 0.0f;
|
||||
|
||||
// Set running state
|
||||
ctx.running = true;
|
||||
ctx.paused = false;
|
||||
|
||||
SDL_Log("Game initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
void game_shutdown(GameContext& ctx) {
|
||||
if (ctx.renderer) {
|
||||
SDL_DestroyRenderer(ctx.renderer);
|
||||
ctx.renderer = nullptr;
|
||||
}
|
||||
|
||||
if (ctx.window) {
|
||||
SDL_DestroyWindow(ctx.window);
|
||||
ctx.window = nullptr;
|
||||
}
|
||||
|
||||
SDL_Quit();
|
||||
SDL_Log("Game shutdown complete");
|
||||
}
|
||||
|
||||
void game_process_events(GameContext& ctx) {
|
||||
SDL_Event event;
|
||||
|
||||
while (SDL_PollEvent(&event)) {
|
||||
// Forward events to pipeline visualization (skip while paused to avoid queuing)
|
||||
if (ctx.pipeline && !ctx.paused) {
|
||||
pipeline_handle_event(*ctx.pipeline, event);
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
ctx.running = false;
|
||||
break;
|
||||
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
if (event.key.key == SDLK_ESCAPE) {
|
||||
ctx.running = false;
|
||||
}
|
||||
if (event.key.key == SDLK_P) {
|
||||
ctx.paused = !ctx.paused;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_WINDOW_RESIZED:
|
||||
ctx.window_width = event.window.data1;
|
||||
ctx.window_height = event.window.data2;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void game_update(GameContext& ctx) {
|
||||
// Calculate delta time
|
||||
uint64_t current_time = SDL_GetPerformanceCounter();
|
||||
uint64_t freq = SDL_GetPerformanceFrequency();
|
||||
ctx.delta_time = static_cast<float>(current_time - ctx.last_time) / static_cast<float>(freq);
|
||||
ctx.last_time = current_time;
|
||||
|
||||
// Clamp delta time to prevent spiral of death
|
||||
if (ctx.delta_time > 0.25f) {
|
||||
ctx.delta_time = 0.25f;
|
||||
}
|
||||
|
||||
// Clear screen
|
||||
SDL_SetRenderDrawColor(ctx.renderer, 30, 30, 40, 255);
|
||||
SDL_RenderClear(ctx.renderer);
|
||||
|
||||
if (!ctx.paused) {
|
||||
// Update pipeline and ECS only when not paused
|
||||
if (ctx.pipeline) {
|
||||
pipeline_update(*ctx.pipeline, ctx);
|
||||
}
|
||||
ctx.ecs.progress(ctx.delta_time);
|
||||
}
|
||||
|
||||
// Always render so the display doesn't blank while paused
|
||||
if (ctx.pipeline) {
|
||||
pipeline_render(*ctx.pipeline, ctx);
|
||||
}
|
||||
|
||||
SDL_RenderPresent(ctx.renderer);
|
||||
}
|
||||
|
||||
void game_render(GameContext& ctx) {
|
||||
// Rendering is handled in game_update()
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
bool game_loop(GameContext& ctx) {
|
||||
game_process_events(ctx);
|
||||
game_update(ctx);
|
||||
game_render(ctx);
|
||||
|
||||
return ctx.running;
|
||||
}
|
||||
82
src/main.cpp
Normal file
82
src/main.cpp
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* @file main.cpp
|
||||
* @brief Godot Render Pipeline Visualizer
|
||||
*
|
||||
* Interactive tool to visualize the 6 stages of the render pipeline:
|
||||
* Object Space -> World Space -> View Space -> Clip Space -> NDC -> Screen Space
|
||||
*
|
||||
* Controls:
|
||||
* Left/Right arrows: Navigate between pipeline stages
|
||||
* 1-6: Jump to a specific stage
|
||||
* WASD: Move object (X/Z plane)
|
||||
* Q/E: Move object (Y axis)
|
||||
* Mouse drag: Orbit the display camera
|
||||
* Scroll wheel: Zoom in/out
|
||||
* Space: Toggle auto-rotation
|
||||
* R: Reset all transforms
|
||||
* P: Pause
|
||||
* ESC: Quit
|
||||
*/
|
||||
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
#include "game.hpp"
|
||||
#include "pipeline.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
// Global context for emscripten callback
|
||||
static GameContext* g_ctx = nullptr;
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
static void em_main_loop() {
|
||||
if (!game_loop(*g_ctx)) {
|
||||
emscripten_cancel_main_loop();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
SDL_SetMainReady();
|
||||
|
||||
static GameContext ctx;
|
||||
static PipelineState pipeline;
|
||||
ctx.pipeline = &pipeline;
|
||||
g_ctx = &ctx;
|
||||
|
||||
WindowConfig config{
|
||||
.title = "Render Pipeline Visualizer",
|
||||
.width = 1280,
|
||||
.height = 720,
|
||||
.flags = SDL_WINDOW_RESIZABLE
|
||||
};
|
||||
|
||||
if (!game_init(ctx, config)) {
|
||||
std::fprintf(stderr, "Failed to initialize\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::printf("Render Pipeline Visualizer running.\n");
|
||||
std::printf("Use Left/Right arrows to navigate stages, WASD/QE to move object.\n");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
emscripten_set_main_loop(em_main_loop, 0, 1);
|
||||
#else
|
||||
while (game_loop(ctx)) {
|
||||
// Game loop handles everything
|
||||
}
|
||||
#endif
|
||||
|
||||
game_shutdown(ctx);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
2661
src/pipeline.cpp
Normal file
2661
src/pipeline.cpp
Normal file
File diff suppressed because it is too large
Load diff
72
src/systems.cpp
Normal file
72
src/systems.cpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* @file systems.cpp
|
||||
* @brief ECS System implementations using FLECS C++ API
|
||||
*/
|
||||
|
||||
#include "systems.hpp"
|
||||
#include "components.hpp"
|
||||
#include "game.hpp"
|
||||
|
||||
void register_systems(flecs::world &ecs, GameContext *ctx) {
|
||||
// Movement system - updates positions based on velocities
|
||||
ecs.system<Position, const Velocity>("MovementSystem")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](flecs::iter &it, size_t, Position &pos, const Velocity &vel) {
|
||||
pos.x += vel.x * it.delta_time();
|
||||
pos.y += vel.y * it.delta_time();
|
||||
});
|
||||
|
||||
// Lifetime system - decrements lifetime and destroys expired entities
|
||||
ecs.system<Lifetime>("LifetimeSystem")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](flecs::iter &it, size_t i, Lifetime &life) {
|
||||
life.remaining -= it.delta_time();
|
||||
if (life.remaining <= 0.0f) {
|
||||
it.entity(i).add<ToDestroy>();
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup system - destroys entities marked with ToDestroy
|
||||
ecs.system<const ToDestroy>("CleanupSystem")
|
||||
.kind(flecs::PostUpdate)
|
||||
.each([](flecs::entity e, const ToDestroy &) { e.destruct(); });
|
||||
|
||||
// Render rect system
|
||||
ecs.system<const Position, const RectShape, const Color>("RenderRectSystem")
|
||||
.kind(flecs::OnStore)
|
||||
.ctx(ctx)
|
||||
.each([](flecs::iter &it, size_t, const Position &pos,
|
||||
const RectShape &rect, const Color &color) {
|
||||
auto *game_ctx = static_cast<GameContext *>(it.ctx());
|
||||
if (!game_ctx || !game_ctx->renderer)
|
||||
return;
|
||||
|
||||
SDL_SetRenderDrawColor(game_ctx->renderer, color.r, color.g, color.b,
|
||||
color.a);
|
||||
|
||||
SDL_FRect dst{pos.x - rect.width / 2.0f, pos.y - rect.height / 2.0f,
|
||||
rect.width, rect.height};
|
||||
|
||||
SDL_RenderFillRect(game_ctx->renderer, &dst);
|
||||
});
|
||||
|
||||
// Render sprite system
|
||||
ecs.system<const Position, const Sprite>("RenderSpriteSystem")
|
||||
.kind(flecs::OnStore)
|
||||
.ctx(ctx)
|
||||
.each([](flecs::iter &it, size_t, const Position &pos,
|
||||
const Sprite &sprite) {
|
||||
auto *game_ctx = static_cast<GameContext *>(it.ctx());
|
||||
if (!game_ctx || !game_ctx->renderer)
|
||||
return;
|
||||
if (!sprite.visible || !sprite.texture)
|
||||
return;
|
||||
|
||||
SDL_FRect dst{pos.x - sprite.src_rect.w / 2.0f,
|
||||
pos.y - sprite.src_rect.h / 2.0f, sprite.src_rect.w,
|
||||
sprite.src_rect.h};
|
||||
|
||||
SDL_RenderTexture(game_ctx->renderer, sprite.texture, &sprite.src_rect,
|
||||
&dst);
|
||||
});
|
||||
}
|
||||
68
web/shell.html
Normal file
68
web/shell.html
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Render Pipeline Visualizer</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
background: #1e1e28;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
font-family: 'Courier New', monospace;
|
||||
color: #aaa;
|
||||
}
|
||||
canvas#canvas {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: block;
|
||||
object-fit: contain;
|
||||
}
|
||||
#output {
|
||||
display: none;
|
||||
}
|
||||
#status {
|
||||
position: fixed;
|
||||
bottom: 4px;
|
||||
right: 8px;
|
||||
font-size: 0.75em;
|
||||
color: #444;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1" width="1280" height="720"></canvas>
|
||||
<div id="status">Loading...</div>
|
||||
<textarea id="output" rows="8" style="display:none"></textarea>
|
||||
|
||||
<script>
|
||||
var Module = {
|
||||
print: function(text) { console.log(text); },
|
||||
printErr: function(text) { console.error(text); },
|
||||
canvas: document.getElementById('canvas'),
|
||||
setStatus: function(text) {
|
||||
if (text) document.getElementById('status').textContent = text;
|
||||
},
|
||||
onRuntimeInitialized: function() {
|
||||
document.getElementById('status').textContent = 'Press H for controls';
|
||||
},
|
||||
totalDependencies: 0,
|
||||
monitorRunDependencies: function(left) {
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
if (left) {
|
||||
Module.setStatus('Loading... (' + (this.totalDependencies - left) + '/' + this.totalDependencies + ')');
|
||||
} else {
|
||||
Module.setStatus('');
|
||||
}
|
||||
}
|
||||
};
|
||||
Module.setStatus('Downloading...');
|
||||
</script>
|
||||
{{{ SCRIPT }}}
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue