Skip to content

pogoPotato/StupaEngineGL

Repository files navigation

STUPA Engine GL

"Written at 5am, drunk, sleep-deprived, and somehow it works."


⚠️ LEGACY / ARCHIVED PROJECT — NO FURTHER UPDATES

This engine is no longer in active development.

STUPA Engine GL was a personal learning project built to understand real-time rendering, ECS architecture, physics integration, and engine editor design from scratch. That goal was accomplished.

Development has fully moved to a private Vulkan engine — a ground-up rewrite with proper architecture, modern GPU techniques, and none of the accumulated mess you'll find here. The Vulkan engine is my primary project. This one is archived and open sourced as a learning reference.

What this means practically:

  • No new features will be added
  • No bug fixes are planned (audio and animation are broken and will stay that way)
  • Pull requests may be merged if they fix something critical and don't break anything else, but don't count on it
  • Issues will not be actively monitored

If you're here to learn how an OpenGL engine is structured, how ECS works in practice, or how to wire up Bullet3/Assimp/ImGui together — this is a useful reference. If you want a production engine, look elsewhere.

— built for learning. learned. moving on.


A real-time 3D game engine built on OpenGL, born from a personal journey — started as a learning project, survived multiple rewrites, a brief Vulkan detour, a homemade version control tool called VCT, and eventually arrived here: open source, cross-platform, and genuinely usable as a reference.

This is STUPAEngineGL. The OpenGL branch. There is a Vulkan engine too — that one is private, actively maintained, and where all future work lives.


Table of Contents


Background

This engine started as a drunk 5am experiment. The original "version control" was a homemade tool called VCT — hand-rolled, chaotic, and eventually abandoned in favour of just using Git like a normal person. The commit history would tell a story if it wasn't so embarrassing.

At some point a fully separate Vulkan engine was built from scratch, which taught more about GPU architecture in two months than years of OpenGL tutorials. That engine is polished, modern, and private. This one — the OpenGL one — is what you're looking at now.

Why open source it? Because the codebase is good enough to be useful. Because binary releases had zero traction. Because maybe someone else is also building a game engine at 5am and wants a real working reference that isn't a tutorial project.

To the reader: please don't judge the slang. Half of this engine was drunk-written. The other half was written in a fugue state of sleep deprivation. It works anyway.


Features

Rendering

  • OpenGL 3.3 Core Profile renderer
  • PBR-style material system — roughness, metalness, emissive, ambient occlusion
  • Multi-texture support — diffuse, normal map, roughness/metalness packed map, emissive map
  • Environment mapping — HDR and LDR equirectangular skybox
  • Screen Space Reflections (SSR) — ping-pong FBO based, configurable strength, step size, max distance
  • Shadow mapping — up to 4 simultaneous shadow-casting lights
  • Grid overlay — editor mode reference grid
  • Wireframe mode toggle
  • Viewport FBO — scene renders into an offscreen framebuffer displayed in the editor panel
  • Camera preview — secondary camera entities render into a live inspector thumbnail

Lighting

  • Point lights — omni-directional with configurable range and intensity
  • Spot lights — cone-shaped with inner/outer cutoff angles
  • Directional lights — sun-style with shadow casting
  • Up to 4 shadow-casting lights per scene
  • Lights bound per-entity via LightComponent

Entity Component System

  • EnTT — fast, header-only ECS
  • Components: Transform, Mesh, Material, Collider, Rigidbody, Name, CameraComponent, LightComponent, Animator, ScriptComponent, Parent, Children, LocalOffset, ModelPath, MovementCommand
  • Hierarchy system — parent/child relationships with local offset preservation
  • Drag-and-drop parenting in the scene graph
  • Unparent support

Physics

  • Bullet3 physics integration
  • Rigid bodies — dynamic and static
  • Colliders — box colliders with offset and extents
  • Trigger volumes
  • Rotation axis freeze — per-axis X/Y/Z locking
  • MovementCommand component for character controller style movement (set velocity per-frame)
  • Physics resets cleanly on play/stop cycle

Model Loading

  • Assimp — supports .obj, .fbx, .gltf, .glb, .dae and more
  • Multi-mesh model import — parts auto-parented to root entity
  • Bone/skinning data imported and stored in Animator component
  • Auto-attach Animator component on import for animated formats (fbx, dae, gltf, glb)
  • Per-mesh material assignment on import

Animation

⚠️ Animation is currently broken. The system exists, the importer runs, bone palettes upload to the GPU, and the timeline/graph workspace UI is present — but playback has known issues. Treat it as a work-in-progress foundation, not a shipped feature. PRs welcome.

  • AnimationSystem — timeline-based clip playback with speed and loop control
  • AnimationWorkspace — docked timeline editor and graph editor UI panels
  • Blend weight support (infrastructure exists, not fully wired)
  • Play/Pause/Stop controls in inspector and animation panel

Audio

⚠️ Audio is currently broken. The AudioSystem initialises and the mixer UI renders, but playback reliability is inconsistent across platforms. The bus mixer, asset library, and quick-load test panel are all wired up in the UI — the underlying audio backend needs work. Do not ship audio in production builds using the current state.

  • STUPA::AudioSystem — miniaudio-backed audio engine
  • Asset loading and caching by handle
  • One-shot and looping playback
  • Volume, pitch, randomization options
  • Listener position/direction follows editor camera
  • Bus mixer UI — Master, SFX, Music, UI buses (UI only, backend routing not complete)
  • 3D positional audio infrastructure (not fully implemented)

Editor UI

  • Dear ImGui — docking-ready, custom dark theme with rust/teal/gold accent palette
  • ImGuizmo — translate/rotate/scale gizmos with world/local space toggle
  • Scene Graph — hierarchical node tree, drag-to-parent, inline rename, context menus
  • Inspector — transform, material, physics, scripts, animator per-entity editing
  • Material Editor — panel with library, per-material surface/texture/shader properties, apply-to-selected
  • Animation Editor — docked timeline and graph panels
  • Audio Mixer — bus faders, asset library, quick-load test section
  • Model Loader panel — path input, scale, physics toggle, import button
  • Spawn Pad — quick-spawn buttons for primitives and lights
  • Console — runtime log and build log tabs with level-coloured output
  • Scene IO panel — save/load with path field, status toast
  • Viewport — resizable scene view with camera position overlay and gizmo manipulation
  • Camera view panel — live thumbnail of scene cameras

Scripting

  • C++ scripting via ScriptBaseOnCreate() / OnUpdate(float deltaTime) virtual interface
  • Scripts registered globally in RegisterAllScripts() in main.cpp
  • Attach scripts to entities at runtime from the editor Inspector
  • New script template creation from the editor
  • Safe deferred attachment — avoids EnTT sparse set assertion that fired in older versions
  • Scripts survive play/stop cycle (re-initialized on each play)

Scene Serialization

  • Save/load scenes as JSON (via nlohmann/json)
  • Editor camera position saved/loaded alongside scene (.editorcam.json sidecar)
  • Material library saved/loaded as .matlib.json sidecar
  • Scene path configurable at runtime

Build System

  • CMake — cross-platform, supports Linux and Windows
  • Editor buildmain / main.exe
  • Game buildgame / game.exe (stripped of editor UI, scene path baked in)
  • GameBuilder — in-editor build and launch buttons (calls CMake/compiler under the hood)
  • Output lands in windows_build/ or linux_build/ depending on platform
  • setup_deps.sh — bash script, clones and builds all external libraries (Linux/WSL/Git Bash)
  • setup_deps.ps1 — PowerShell script, same for Windows (vcpkg/winget OpenSSL support)

Debug

  • DebugOverlay — F3 toggle for HUD, collider boxes, velocity vectors, entity info, screen-space labels
  • Logger — timestamped log levels: INFO, SUCCESS, WARNING, ERROR, DEBUG
  • Physics debug visualisation toggle in toolbar
  • Play mode indicator with pulsing animation

Known Broken / Work In Progress

System Status Notes
Audio ⚠️ Broken Initialises, UI renders, playback unreliable. Backend needs work.
Animation ⚠️ Broken Bone data imports, GPU upload works, playback has known bugs.
Skybox auto-load 🔧 Partial Windows path list commented out in main.cpp. Uncomment and adjust paths for your setup.
SSR ✅ Works Ping-pong FBO approach. Roughness threshold at 0.75.
Linux skybox 🔧 Manual No auto-discovery on Linux yet. Load via absolute path manually.
Multi-material models 🔧 Partial Multi-mesh import works, sub-meshes auto-parented. Full per-part material editing via inspector.
Audio bus routing ❌ UI only Fader values exist, backend routing to buses not wired.
Animation blending ❌ UI only Infrastructure exists, blend weight not fully wired.

Architecture Overview

main.cpp                  — GLFW window, OpenGL init, main loop
│
├── Renderer              — FBO management, geometry, shader programs, camera preview
├── SceneManager          — Editor UI (handleImGui), camera input, scene logic, physics orchestration
├── LightingSystem        — Shadow pass rendering, light uniform binding
├── AnimationSystem       — Clip playback, bone transform palette upload
├── AnimationWorkspace    — Timeline + graph editor UI panels
├── AudioSystem           — miniaudio wrapper, asset management
├── BulletPhysics         — Bullet3 wrapper, rigid body lifecycle
├── ScriptBase / ScriptManager / ScriptSystem — C++ scripting layer
├── SceneSerializer       — JSON save/load for scenes, camera, material library
├── ModelLoader           — Assimp wrapper, mesh/bone/texture extraction
├── Resources             — Texture loading (stb_image)
├── Logger                — In-memory log with levels
├── DebugOverlay          — Collider/velocity/label debug rendering
├── GuizmoManager         — ImGuizmo wrapper
└── InputManager          — GLFW input abstraction

ECS data flows through entt::registry. Systems iterate views each frame. Physics, scripts, animations, and hierarchy sync all run in SceneManager::handleSceneLogic() during the main loop.


Getting Started

Prerequisites

Windows:

  • Git — https://git-scm.com
  • CMake 3.20+ — https://cmake.org
  • A C++17 compiler — LLVM/MinGW (recommended), MSVC 2022, or MSYS2/MinGW-w64
  • PowerShell 5.1+ (included in Windows 10/11)

Install LLVM/MinGW via winget:

winget install MartinStorsjo.LLVM-MinGW.UCRT

Linux (Ubuntu/Debian):

sudo apt update
sudo apt install -y build-essential cmake git \
    libgl1-mesa-dev libx11-dev libxrandr-dev \
    libxinerama-dev libxcursor-dev libxi-dev \
    libssl-dev pkg-config

Linux (Arch):

sudo pacman -S --needed base-devel cmake git mesa \
    libx11 libxrandr libxinerama libxcursor libxi openssl

Linux (Fedora):

sudo dnf install -y gcc-c++ cmake git mesa-libGL-devel \
    libX11-devel libXrandr-devel libXinerama-devel \
    libXcursor-devel libXi-devel openssl-devel

Clone the Repository

git clone https://github.com/yourusername/STUPAEngineGL.git
cd STUPAEngineGL

Install External Dependencies

Run the setup script once. It clones and builds all required external libraries into External/ and copies built artifacts into lib/.

Windows (PowerShell):

# Allow local scripts if needed (run once as admin)
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned

.\setup_deps.ps1

Optional flags:

.\setup_deps.ps1 -SkipBullet
.\setup_deps.ps1 -SkipAssimp
.\setup_deps.ps1 -SkipOpenSSL
.\setup_deps.ps1 -Help

Linux / WSL / Git Bash:

chmod +x setup_deps.sh
./setup_deps.sh

Optional flags:

./setup_deps.sh --skip-bullet
./setup_deps.sh --skip-assimp
./setup_deps.sh --skip-openssl
./setup_deps.sh --help

The script is idempotent — re-running it skips anything already built.


Build

After running the setup script:

Editor (Linux):

mkdir -p build && cd build
cmake .. -DSTUPA_BUILD_EDITOR=ON -DCMAKE_BUILD_TYPE=Release
cmake --build . --parallel
# Output: linux_build/main

Editor (Windows):

mkdir build; cd build
cmake .. -DSTUPA_BUILD_EDITOR=ON -DCMAKE_BUILD_TYPE=Release
cmake --build . --config Release --parallel
# Output: windows_build/main.exe

Game build:

cmake .. -DSTUPA_BUILD_GAME=ON -DSTUPA_GAME_SCENE="../Scenes/game.json" -DCMAKE_BUILD_TYPE=Release
cmake --build . --parallel

Both at once:

cmake .. -DSTUPA_BUILD_EDITOR=ON -DSTUPA_BUILD_GAME=ON -DCMAKE_BUILD_TYPE=Release
cmake --build . --parallel

For detailed build options see BUILD.md.


Editor Usage

Navigation

The viewport is in the centre panel. Right-click and hold to enable camera movement:

Input Action
Right Mouse + W/A/S/D Fly camera
Right Mouse + Shift Sprint (3× speed)
Right Mouse drag Look around
T Translate gizmo
R Rotate gizmo
S Scale gizmo
F3 Toggle debug overlay

Scene Graph

The left panel lists all entities in the scene hierarchy.

  • Click an entity to select it
  • Double-click to rename inline
  • Drag one entity onto another to parent it
  • Right-click for: rename, focus, duplicate, unparent, add animator, edit animation, remove
  • Root-level entities without a Parent component appear at the top level
  • Child entities are indented under their parent with a tree arrow

Inspector

The right panel shows components for the selected entity.

Transform — position, rotation (Euler degrees), scale. Editable via drag sliders. Gizmo manipulation in viewport updates these values live.

Material — base colour, roughness, metalness, emissive intensity. Texture slots for diffuse, normal, roughness/metalness packed, and emissive maps. Set a path and click Set to load. Click X to clear.

Physics — add/remove Collider (offset, extents, trigger flag) and Rigidbody (mass, gravity, kinematic, freeze rotation axes). Static and Dynamic add buttons.

Scripts — attach registered C++ scripts to the entity. New Script creates a template file. Attach Script opens a selector of all registered scripts.

Animator — clip list count, playback speed, loop toggle, current clip name and scrub bar. Play/Pause/Stop controls. "Open Anim Editor" docks the animation workspace panel.


Toolbar

Left to right:

  • T / R / S — gizmo mode (translate/rotate/scale)
  • Wld / Loc — world/local space toggle
  • Grid / Wire / Phys — viewport overlays
  • Focus — snap camera to selected entity
  • Reset — reset camera to default position
  • Anim / Mat / Audio — toggle overlay panels
  • Play/Stop — enter and exit play mode
  • Build / Launch — compile game build and run it
  • F3 Debug — toggle debug overlay

Play Mode

Clicking ▶ Play:

  • Snapshots all Transform components
  • Resets the physics world
  • Re-initialises all scripts (calls OnCreate)
  • Starts animation playback on any Animator with a clip selected
  • Enables the debug overlay HUD

Clicking ■ Stop:

  • Restores all Transform snapshots
  • Resets physics
  • Pauses and rewinds all animators
  • Re-initialises scripts (ready for next play)
  • Hides the debug overlay HUD

Scripts, physics, and animations only run in play mode. The scene graph and inspector remain editable in edit mode.


Build & Launch

The Build button in the toolbar:

  1. Saves the current scene to Scenes/build_game.json
  2. Invokes GameBuilder::BuildGame() which runs the CMake game build
  3. Displays build output in the Build tab of the console panel
  4. Sets the launch button active if build succeeded

The Launch button runs windows_build/game.exe or linux_build/game with the built scene path.

You can also build manually from the command line — see BUILD.md.


Scripting

Scripts are C++ classes that inherit from ScriptBase. They get OnCreate() called once when play mode starts, and OnUpdate(float deltaTime) every frame.

1. Create a script file (or use the editor New Script button):

// scripts/MyScript.h
#pragma once
#include "ScriptBase.h"

class MyScript : public ScriptBase {
public:
    void OnCreate() override;
    void OnUpdate(float deltaTime) override;
};
// scripts/MyScript.cpp
#include "MyScript.h"
#include "components.h"

void MyScript::OnCreate() {
    // runs once when play starts
}

void MyScript::OnUpdate(float dt) {
    auto& t = GetComponent<Transform>();
    t.pos.y += dt * 2.0f; // float upward
}

2. Register it in main.cpp:

void RegisterAllScripts() {
    auto& sm = ScriptManager::Instance();
    sm.RegisterScript<MyScript>("MyScript");
}

3. Attach it to an entity from the Inspector Scripts section, or from code:

auto& sc = registry.get_or_emplace<ScriptComponent>(entity);
auto script = ScriptManager::Instance().CreateScript("MyScript");
script->SetEntity(entity, &registry);
sc.scripts.push_back(std::move(script));
sc.scriptPaths.push_back("MyScript");

Scripts can access sibling components via GetComponent<T>() (provided by ScriptBase). They can also call GetEntity() to get their own entt::entity handle.


Adding Game Scripts to the Build

Game scripts must be compiled into the game binary. In CMakeLists.txt, find the game target section and add your .cpp files:

target_sources(game PRIVATE
    ${CMAKE_SOURCE_DIR}/scripts/MyScript.cpp
    ${CMAKE_SOURCE_DIR}/scripts/PlayerController.cpp
)

Then rebuild:

cmake --build . --config Release --parallel

Scenes

Scenes are saved as JSON files. The serializer saves all entities with their components. Three files are written when saving:

File Contents
scene.json All entities and components
scene.editorcam.json Editor camera position and orientation
scene.matlib.json Material library (all materials created in Material Editor)

Load and save from the Scene panel (bottom right), the File menu, or Ctrl+S / Ctrl+O.

The game build bakes in the scene path at compile time via -DSTUPA_GAME_SCENE=.... At runtime the game loads that scene directly without any editor UI.


External Libraries

All external libraries live in External/ and are managed by the setup scripts. None are vendored into this repository — the scripts clone and build them on first run.

Library Version Purpose
GLFW 3.4 Window creation, OpenGL context, input
glad OpenGL function loader (bundled in src/)
GLM Math (bundled in include/)
EnTT latest Entity Component System
Dear ImGui docking Editor UI
ImGuizmo latest 3D transform gizmos
Assimp v5.4.3 Model loading
Bullet3 3.25 Physics simulation
nlohmann/json latest Scene serialization
OpenSSL 3.x Networking / crypto (future use)
stb_image Texture loading (bundled in include/)

Project Structure

STUPAEngineGL/
│
│   — Root files —
├── .vctignore                      — Legacy: ignore file for VCT (old homemade version control)
├── CMakeLists.txt                  — Cross-platform build (Linux + Windows, editor + game targets)
├── BUILD.md                        — Detailed build instructions for both platforms
├── Quickstart.md                   — Short version: clone → setup → build → run
├── setup_deps.ps1                  — Windows: clone + build all external libs (PowerShell)
├── setup_deps.sh                   — Linux/WSL/Git Bash: same, for Unix
│
├── src/
│   ├── main.cpp                    — Editor entry point
│   ├── game_main.cpp               — Game entry point (no editor UI)
│   ├── glad.c                      — OpenGL function loader
│   ├── Engine/
│   │   ├── renderer.cpp/.h         — FBO, geometry, camera preview
│   │   ├── camera.cpp/.h
│   │   ├── components.h            — All ECS component structs
│   │   ├── inputManager.cpp/.h
│   │   ├── logger.cpp/.h
│   │   ├── modelloader.cpp/.h      — Assimp wrapper
│   │   ├── resources.cpp/.h        — Texture loading (stb_image)
│   │   ├── sceneserializer.cpp/.h  — JSON save/load
│   │   └── DebugOverlay.cpp/.h     — F3 HUD, collider boxes, velocity vectors
│   ├── UI/
│   │   ├── scenemanager.cpp/.h     — All editor UI (~3000 lines, known mess)
│   │   ├── imguiManager.cpp/.h
│   │   └── guizmoManager.cpp/.h    — ImGuizmo wrapper
│   ├── Shaders/
│   │   └── shaders.cpp/.h          — Inline GLSL (PBR, depth, sky, simple)
│   ├── Physics/
│   │   └── bulletPhysics.cpp/.h    — Bullet3 wrapper
│   ├── Lights/
│   │   └── LightingSystem.cpp/.h   — Shadow passes, light uniform binding
│   ├── Animation/
│   │   ├── AnimationImporter.cpp/.h
│   │   ├── AnimationSystem.cpp/.h  — ⚠️ Broken
│   │   ├── AnimationWorkspace.cpp/.h
│   │   ├── AnimationWorkspace_Timeline.cpp
│   │   └── AnimationWorkspace_Graph.cpp
│   ├── Audio/
│   │   └── audioSystem.cpp/.h      — ⚠️ Broken
│   └── ScriptBase/
│       ├── ScriptBase.cpp/.h
│       ├── ScriptManager.h
│       └── ScriptSystem.h
│
├── scripts/                        — User game scripts (.cpp/.h files go here)
├── include/                        — GLM, stb_image, glad headers
├── lib/                            — Built library outputs (GLFW dll/lib/a)
├── External/                       — Auto-populated by setup scripts
│   ├── entt/
│   ├── json/
│   ├── imgui/
│   ├── gusmo/                      — ImGuizmo
│   ├── glfw/
│   ├── assimp/
│   ├── bullet3-3.25/
│   └── ssl/
├── Assets/                         — Textures, models, audio files
├── Scenes/                         — JSON scene files + sidecars
├── windows_build/                  — main.exe / game.exe (Windows)
└── linux_build/                    — main / game (Linux)

Contributing

This project is archived. That said, if you fix something real and submit a clean PR, it may get merged.

  • Audio and animation are the two most broken systems. If you fix either, that's the most valuable possible contribution.
  • scenemanager.cpp is enormous and messy. Splitting it into smaller files would be welcome.
  • The codebase mixes human-written and AI-assisted code — some files have header comments labelling which is which. Don't be surprised by the tone differences.
  • Please do not remove the original file header comments. They're part of the history of this project.
  • Don't expect responses to issues or active review of PRs. This is an archive.

Platform Support

Platform Compiler Status
Windows 10/11 LLVM/MinGW (UCRT) ✅ Tested
Windows 10/11 MSVC 2022 ✅ Should work
Ubuntu 22.04+ GCC 11+ ✅ Tested
Arch Linux GCC / Clang ✅ Tested
WSL2 (Ubuntu) GCC ⚠️ Works, audio likely won't
macOS ❌ Not supported, not planned

Skybox note: The auto-discovery path list in main.cpp is commented out. On Linux, uncomment the vector and adjust paths for your setup, or load via absolute path. This was never fixed and won't be.


License

MIT License. See LICENSE for details.

Use it, fork it, learn from it. That's the point.


Repository Files at a Glance

File Purpose
CMakeLists.txt Build system — Linux + Windows, editor + game
setup_deps.sh Unix: clone + build all external libraries
setup_deps.ps1 Windows: same, PowerShell
BUILD.md Full build instructions for both platforms
Quickstart.md TL;DR version of BUILD.md
.vctignore Relic from VCT (old homemade version control)

Started at 5am. Survived VCT. Survived the Vulkan rewrite. Open sourced, archived, done. The Vulkan engine is where I live now. — sleep driven raccoon

About

Real-time OpenGL engine built at 5am over 1.5 years. ECS, PBR, Bullet3, ImGui editor. Archived — moved to Vulkan. Still works tho.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors