Files
ogre-prototype/AGENTS.md
T
2026-09-17 02:55:53 +03:00

25 KiB
Raw Blame History

World2 Game Project - Agent Guide

Project Overview

World2 is a 3D life simulation game built using the OGRE3D rendering engine. The project features an Entity-Component-System (ECS) architecture powered by Flecs, with Jolt Physics for simulation, Lua scripting for story content, and ImGui for user interfaces.

Technology Stack

Core Dependencies

  • Rendering: OGRE3D 14.x+ (Object-Oriented Graphics Rendering Engine)
  • ECS Framework: Flecs (Fast Lightweight Entity Component System)
  • Physics: Jolt Physics with double precision support (JPH_DOUBLE_PRECISION)
  • Scripting: Lua 5.4 with LPEG library
  • Audio: miniaudio (single-header audio library)
  • UI: ImGui (integrated via Ogre::ImGuiOverlay)
  • Model Loading: Assimp with pugixml
  • Navigation: Recast/Detour for crowd simulation
  • Procedural Generation: OgreProcedural
  • Profiling: Tracy frame profiler

Build Requirements

  • CMake 3.13+
  • C++17 compatible compiler
  • Blender (for asset processing, path configurable via BLENDER CMake variable)
  • OGRE SDK with components: Bites, Paging, Terrain, MeshLodGenerator

Project Structure

/
├── CMakeLists.txt          # Main build configuration
├── resources.cfg           # OGRE resource locations
├── .clang-format           # Linux kernel code style config
│
├── Game.cpp                # Main game executable entry point (legacy)
├── Editor.cpp              # World editor executable (legacy)
├── Procedural.cpp          # Procedural generation test / character controller demo (legacy)
├── Bootstrap.cpp           # Legacy character controller demo (legacy)
├── terrain.cpp             # Terrain system test executable (legacy)
│
├── src/                    # Source code
│   ├── gamedata/           # Core ECS modules and game logic (legacy)
│   │   ├── GameData.cpp/h  # ECS world setup and management (legacy)
│   │   ├── Components.h    # ECS component definitions (legacy)
│   │   ├── *Module.cpp/h   # Individual ECS modules (legacy)
│   │   └── items/          # Game item implementations (harbour, temple, town, etc.) (legacy)
│   ├── lua/                # Lua integration
│   │   ├── lua-5.4.8/      # Lua source code
│   │   └── lpeg-1.1.0/     # LPEG parsing library
│   ├── physics/            # Jolt Physics wrapper (physics.h/cpp) (legacy - for buildiung legacy code only)
│   ├── sound/              # Audio system (miniaudio)
│   ├── crowd/              # Recast/Detour navigation
│   │   ├── include/        # Crowd headers (OgreRecast, OgreDetourCrowd, etc.)
│   │   └── src/            # Crowd implementations
│   ├── editor/             # Editor-specific code (legacy)
│   │   ├── EditorGizmoModule.h/cpp
│   │   ├── EditorInputModule.h/cpp
│   │   └── main.cpp
│   ├── features/           # Feature modules
│   │   ├── characters/     # Character rendering test
│   │   ├── editScene/      # Scene editor with ECS, it has game mode and all features, current work is here
│   │   └── sceneEditor/    # some other editor code
│   ├── world/              # World generation and GOAP AI (legacy)
│   ├── terrain/            # Terrain system (legacy)
│   ├── tests/              # Test utilities
│   ├── text_editor/        # ImGui text editor component
│   ├── miniaudio/          # Audio library
│   ├── sceneloader/        # Scene loading utilitie (legacy)
│   ├── aitoolkit/          # AI utilities (GOAP, FSM, Behavior Trees) (legacy)
│   └── vehicles/           # Vehicle definitions (legacy)
│
├── assets/                 # Source assets (Blender files)
│   ├── blender/            # Blender source files
│   │   ├── buildings/      # Building models (.blend)
│   │   ├── characters/     # Character models and clothes (characters asset pipeline)
│   │   ├── vehicles/       # Vehicle models
│   │   ├── scripts/        # Blender Python scripts for export
│   │   └── world/          # World terrain models
│   ├── textures/           # Source textures
│   └── fonts/              # Game fonts
│
├── lua-scripts/            # Lua game scripts
│   ├── narrator/           # Ink narrative parser
│   ├── stories/            # Story scripts (.ink format, compiled to .lua)
│   └── json/               # JSON library for Lua
│
├── water/                  # Water rendering system (legacy)
│   ├── water.h/cpp         # Water class
│   ├── *.frag, *.vert      # GLSL shaders
│   └── water.compositor    # OGRE compositor script
│
├── skybox/                 # Sky rendering resources
├── resources/              # Runtime resources (build output)
├── audio/                  # Audio resources
├── morph/                  # Character morphing data
└── build/                  # build directory
└── build-vscode/           # VSCode build directory

Critical Constraints & Rules

  • The source code is the only source of truth. Please check code as documentation might be out of sync.
  • Do not modify legacy code. Current code is at src/features/editScene and its dependencies.
  • Please ask questions if anything is unclear.
  • Please update tests, documentation and examples every time API or modules and systems change.

Build Instructions

Prerequisites

  1. Install OGRE3D SDK with Bites, Paging, Terrain, MeshLodGenerator components
  2. Install dependencies: Jolt Physics, Flecs, Assimp, pugixml, OgreProcedural, Tracy, SDL2, ZLIB
  3. Set CMAKE_PREFIX_PATH to OGRE installation
  4. Configure Blender path in CMake (default: ${CMAKE_SOURCE_DIR}/../../blender-bin/bin/blender)

Build Commands

# Configure (adjust paths as needed)
cmake -B build -S . -DCMAKE_PREFIX_PATH=/path/to/ogre-sdk

# Build all targets
cmake --build build

# Build specific targets
cmake --build build --target Game           # Main game executable (legacy)
cmake --build build --target Editor         # World editor (legacy)
cmake --build build --target Procedural     # Procedural test (legacy)
cmake --build build --target TerrainTest    # Terrain test (legacy)
cmake --build build --target TerrainTest    # Terrain test (legacy)
cmake --build build --target editSceneEditor    # Current executable for scene editor and game
cmake --build build --target demoLuaSceneScript # Demo: game-mode inline scene/prefab Lua scripts
cmake --build build --target demoSceneSwitchingExtra # Demo: scene switching with CellGrid interior
cmake --build build --target demoSokoban      # Demo: forklift sokoban activity (vehicle + crates + quest HUD)
cmake --build build --target demoTaxi         # Demo: taxi driver (procedural town, sedans, NPC traffic, fare chain)

Build Outputs

  • build/Game - Main game executable (legacy)
  • build/Editor - World editor executable (legacy)
  • build/Procedural - Procedural generation test (legacy)
  • build/TerrainTest - Terrain system test (legacy)
  • build//src/features/editScene/editSceneEditor - current executable
  • build//src/features/editScene/demos/demo-lua-scene-script/demoLuaSceneScript - SceneScriptComponent demo (game mode, inline Lua); self-contained runtime staged in the same directory
  • build//src/features/editScene/demos/demo-scene-switching-extra/demoSceneSwitchingExtra - Scene-switching demo with a CellGrid interior (own-entity ProceduralMaterial/ProceduralTexture); self-contained runtime staged in the same directory
  • build//src/features/editScene/demos/demo-interior-exterior-dynamics/demoInteriorExteriorDynamics - Same scene-switching demo, but the exterior scene has a streaming terrain (baseNoise archipelago - a large island at the content site amid an ocean with more islands) + sky + water instead of the flat floor; self-contained runtime staged in the same directory
  • build//src/features/editScene/demos/demo-sokoban/demoSokoban - Forklift sokoban activity demo (drivable Jolt VehicleConstraint forklift, pushable crates, target pads, completion HUD); based on demo-interior-exterior-dynamics, see demos/demo-sokoban/PLAN.md; self-contained runtime staged in the same directory
  • build//src/features/editScene/demos/demo-taxi/demoTaxi - Taxi driver demo (procedurally generated town, drivable sedans, passenger/foot-traffic/vehicle-traffic NPCs, chained timed fares with doubling payout); based on demo-interior-exterior-dynamics with vehicle ideas from demo-sokoban, see demos/demo-taxi/PLAN.md; self-contained runtime staged in the same directory
  • build/resources/ - Staged game resources
  • build/characters/ - Processed character models
  • build/water/ - Water assets (legacy)

Code Style Guidelines

This project follows the Linux Kernel Coding Style for C++:

Formatting Rules

  • Indentation: 8-character tabs (UseTab: Always)
  • Line Length: 80 characters maximum
  • Braces:
    • Functions: Opening brace on new line
    • Control structures: Opening brace on same line
  • Pointer Alignment: Right (int *ptr not int* ptr)
  • Namespaces: No indentation inside namespaces

Example

void myFunction(int arg)
{
        if (arg > 0) {
                doSomething();
                return;
        }
        
        for (int i = 0; i < arg; i++) {
                process(i);
        }
}

Running clang-format

clang-format -i src/gamedata/*.cpp src/gamedata/*.h

Architecture

ECS (Entity Component System)

The game uses Flecs for ECS architecture. Core systems:

Components (src/gamedata/Components.h) (legacy)

  • GameData - Global game state
  • EngineData - Engine configuration (SceneManager, delta time, debug draw)
  • Input - Input state (keyboard, mouse, controls)
  • Camera - Camera node and configuration
  • RenderWindow - Window and DPI information
  • CollisionShape - Physics shape references
  • EditorSceneSwitch - Editor scene switching
  • GameState - Game running state

Modules (src/gamedata/*Module.h) (legacy)

Modules are ECS systems organized by functionality:

Module Purpose
AppModule Application context management
GUIModule ImGui interface management
EditorGUIModule Editor UI
PhysicsModule Jolt physics integration
CharacterModule Character spawning and management
CharacterManagerModule Player/NPC character management
CharacterAnimationModule Animation state machines
CharacterAIModule AI behavior (GOAP-based)
TerrainModule Terrain generation and rendering
WaterModule Water surface and physics
SunModule Day/night cycle and lighting
WorldMapModule World map overlay
QuestModule Quest system
EventModule Game event handling
EventTriggerModule Event triggers
SlotsModule Inventory slot management
BoatModule Boat physics and control
VehicleManagerModule Vehicle management
PlayerActionModule Player action handling
LuaModule Lua scripting integration
StaticGeometryModule Static geometry management

EditScene Modules (src/features/editScene)

The editor/game mode executable adds its own ECS components.

The editor/game mode executable adds its own ECS modules:

Module Purpose
CharacterSlotSystem Catalog-driven multi-slot character mesh builder
CharacterSpawnerSystem Distance-based spawn/despawn of registry characters
TerrainPrefabSpawnerSystem Distance-based terrain prefab spawn/despawn, terrain snap and compliance flattening
PlayerControllerSystem Player input, camera, locomotion and animation state
ActuatorSystem Player interaction prompts and smart-object/item actions
DoorSystem Swing animation and collider toggle for CellGrid door entities
EditorUISystem ImGui property panels and scene management
ProceduralTextureSystem Runtime procedural texture generation
ProceduralMaterialSystem Runtime procedural material creation
AnimationTreeRegistry Global named animation tree definitions
AnimationTreeSystem Runtime evaluation of registry-referenced animation trees
SceneScriptSystem Execute-once scene/prefab Lua scripts (SceneScriptComponent) and scene_loaded/prefab_loaded events
VehicleSystem Jolt VehicleConstraint vehicles (VehicleComponent): chassis constraint lifetime, driver input, wheel visual nodes

PlayerControllerSystem

PlayerControllerSystem reads the shared GameInputState, drives the TPS/FPS camera and sets the locomotion animation state (idle/walking/running and the swim equivalents) on the controlled entity. In game mode it adds the PlayerControlledComponent tag to the target so AI systems know to leave it alone.

When a controller's targetCharacterName points at an entity with CharacterSpawnerComponent, the system locks the spawner and forces an immediate spawn; the actual controlled entity is the spawned character instance, not the spawner.

The TPS camera boom is collision-clamped via a physics raycast from the pivot to the desired camera position (instant pull-in, eased recovery), and the character geometry is hidden when the clamped camera gets too close to the body — see src/features/editScene/AGENTS.md for details.

Player Character Resolution

Systems that need the live player character (inventory UI, character sheet, save/load, actuators) must not resolve the controller's targetCharacterName against EntityNameComponent directly – that returns the spawner entity when the controller targets a spawner. Instead use:

flecs::entity player = editorApp->getPlayerCharacterEntity();

EditorApp::getPlayerCharacterEntity() returns the controller's current target, spawning the character through CharacterSpawnerSystem if necessary. CharacterSpawnerSystem::getSpawnerForCharacter() can be used in save/load to keep the controller pointed at the spawner rather than the transient instance.

Game Mode Input

Game mode movement keys (W, A, S, D, left/right Shift) are handled inside keyPressed/keyReleased by keysym.sym. Repeated KEYDOWN events (evt.repeat != 0) are ignored for movement keys, because releasing Shift while W/A/S/D is held can generate a repeated keydown with a different symbol after the physical release, which would otherwise leave the key stuck. One-shot actions (E, F, I, Space) are also matched by keysym.sym, and their *Pressed edges ignore repeats too — otherwise holding E (e.g. the hold-E-to-exit a vehicle) re-fires the door actuators and re-enters.

CharacterSpawnerComponent

  • registryId - Character registry ID to spawn.
  • spawnDistanceSq - Squared distance to camera at which the character spawns (default 100²).
  • despawnDistanceSq - Squared distance to camera at which the character despawns (default 200²).

The spawner uses the entity's TransformComponent for spawn position/rotation. When the spawner itself moves/rotates/scales in the editor, the spawned character is updated to match; when the spawner is stationary, the character is left alone so it can move on its own. Spawned characters are created without EditorMarkerComponent and removed from the editor UI cache so they remain visible but are not selectable/editable in the editor. Registry changes that bump the character version trigger an immediate respawn. Scene serialization stores plain spawnDistance/despawnDistance values for readability while the component keeps squared values for comparisons.

AnimationTreeComponent

  • treeName - Name of the animation tree in AnimationTreeRegistry.
  • enabled - Whether the tree is evaluated.
  • useRootMotion - Whether root motion from the animation drives the character's linear velocity.

The actual tree definition (node hierarchy, state machines, animations) lives in the AnimationTreeRegistry, persisted to animation_tree.json. Trees are authored in the registry editor (Tools -> Animation Tree Registry), where each entry can also select a skeleton source from the Characters resource group (one representative mesh per skeleton) to populate animation-name dropdowns. Old scenes and prefabs that stored the tree inline or referenced an AnimationTreeTemplate are migrated automatically on load.

Road Region Streaming (M5)

When the terrain has streamingEnabled, RoadSystem persists the road graph per terrain page in heightmaps/<terrainId>/roads/<px>_<py>.json (managed by RoadRegionStore) instead of inline in the scene JSON — SceneSerializer::serializeTerrain then skips roadNodes/roadEdges and keeps only roadConfig. Region files store absolute world-space doubles; a cross-page edge is written to the region file of each endpoint page with the foreign endpoint repeated inline. On the first sync a non-empty inline graph is migrated to region files automatically. Merges match nodes by world XZ (0.01 eps) so reloaded pages never duplicate nodes; EditorUISystem::saveScene calls RoadSystem::flushRegionStore() before writing the scene. Page mesh rebuilds are gated by a rebase-invariant FNV-1a content signature (world positions quantized to cm), so a render-origin rebase shifts node render positions without re-dirtying pages; navmesh dirtying is debounced (~15 quiet frames) while streaming.

Terrain Streaming Architecture

The terrain can span up to 40,000,000 x 40,000,000 units (TerrainComponent::worldSizeUnits, default; 20000 x 20000 pages at the default 2000-unit page size, clamped to 32768 per axis for TerrainGroup's signed 16-bit slot packing). Streaming is opt-in per terrain via TerrainComponent::streamingEnabled.

Coordinate spaces. Three spaces coexist (see TerrainSystem.hpp "Coordinate spaces"):

  • WORLD space — absolute double-precision coordinates; authoritative. TransformComponent::worldX/Y/Z, region files, bookmarks and the Navigation panel all use it.
  • RENDER space — floats relative to the floating render origin: render = world - renderOrigin. Scene nodes, mouse rays and the editor camera live here. RenderOriginSystem (singleton, RenderOriginSystem::getInstance()) owns the origin and rebases when the camera moves further than REBASE_THRESHOLD (8192 render units) from it: the origin snaps to integer world coordinates near the camera and every render-space position shifts by the exact negated delta. Convert with RenderOriginSystem::worldToRender(x,y,z) / renderToWorld(Ogre::Vector3).
  • PHYSICS space — Jolt DVec3/RVec3 in absolute world coordinates via physics/physics.h; terrain colliders live here, so TerrainSystem::raycastTerrain converts its render-space ray to world space for the collider query.

TerrainSystem additionally exposes renderToWorldX/Y/Z, worldToRender, and visualToPhysicalX/Z / physicalToVisualX/Z for the physical heightmap space (physical X shifted by half a page, physical Z mirrored within each page).

Page streaming window. With streamingEnabled, pages are not defined up front; TerrainSystem::updateStreamingWindow() loads a square window of pageLoadRadius pages around the camera page each frame (2 loads/frame cap) and unloads pages beyond pageHoldRadius (4 unloads/frame cap), all synchronously to avoid WorkQueue shutdown hangs. Base heights come from procedural TerrainComponent::baseNoise (FastNoiseLite, double precision) instead of the legacy heightmap buffer; setHeightAt writes through the LRU-capped fixup chunk layer. Pages are indexed in PHYSICAL page coords [0, N-1]; the Ogre TerrainGroup slot for physical page (x, y) is (x, -y) because ALIGN_X_Z negates Z. The world is bounded — page indices and teleports clamp to [0, N-1].

Region storage. Per-page data lives under heightmaps/<terrainId>/: painted blend maps, aux maps, fixup chunks, plus the streamed content stores spawners/<px>_<py>.json (prefab spawner defs, see below) and roads/<px>_<py>.json (road graph, see "Road Region Streaming (M5)" above).

Editor navigation tools (M3). EditorCamera supports mouse-wheel zoom (handleMouseWheel), Shift boost (x10) and a configurable fly speed (setFlySpeed, 1..100000 units/s). Tools -> Navigation opens NavigationPanel (teleport by world X/Y/Z or by page index, clamped to world bounds; named world bookmarks stored in the scene JSON top-level bookmarks array, tolerated when missing on load). Tools -> World Map opens WorldMapPanel (ImGui canvas over WorldMapData: coarse heights, loaded pages, roads, spawners, bookmarks and camera overlays; drag pan, wheel zoom, double-click teleport). TerrainEditor also has a compact "Navigation" section sharing the same static teleport backend.

Streamed vs always-loaded spawners (M4). Scene-embedded TerrainPrefabSpawnerComponent entities behave as always-loaded spawners for small/non-streaming scenes. When the terrain streams, spawner definitions persist per page in heightmaps/<terrainId>/spawners/<px>_<py>.json (via SpawnerRegionStore); TerrainPrefabSpawnerSystem::syncStreamedSpawners() polls TerrainGroup::getTerrainSlots() and creates a region spawner entity (tagged StreamedSpawnerTag{pageX,pageY,defId}, excluded from scene JSON) when a page loads, destroying it and writing edits back (including page-boundary crossings) when it unloads. The editor "prefab spawn mode" click-to-place writes to the region store when streaming.

Physics System

Uses Jolt Physics with custom wrapper (legacy - src/physics/physics.h, current - src/features/editScene/physics/physics.h):

// Example: Creating a physics body
JoltPhysicsWrapper *physics = new JoltPhysicsWrapper(scnMgr, cameraNode);
JPH::ShapeRefC shape = physics->createBoxShape(Ogre::Vector3(1, 1, 1));
JPH::BodyID body = physics->createBody(shape, 1.0f, position, rotation, 
                                        JPH::EMotionType::Dynamic, Layers::MOVING);

Rendering Pipeline

  1. Main Render: Standard OGRE render with RTSS (Real Time Shader System)
  2. Water: Reflection/refraction with custom shaders
  3. Sky: Dynamic skybox with day/night cycle
  4. Shadows: Configurable shadow techniques

Asset Pipeline

  1. Source: Blender .blend files in assets/
  2. Export: Python scripts in assets/blender/scripts/
    • export_buildings.py - Building export to glTF
    • export_characters_ogre.py - Character export to OGRE format
    • export_vehicles.py - Vehicle export (car modeling contract: assets/blender/vehicles/CAR_MODELING.md)
    • gen_sokoban_assets.py - One-shot generator for the demo-sokoban forklift/crate/pad .blend files (blender2ogre names .mesh files after mesh data names — set both object and data names)
    • gen_taxi_assets.py - Generator for the demo-taxi sedan-a / sedan-taxi .blend files (body + hinge-local door/hood/trunk parts, shared wheel meshes; body colors baked as material variants)
    • gen_vehicle.py - Convention-based vehicle pipeline for hand-modeled cars (sedan-v2.blend is the reference): classifies parts by name (* Tire, * Door, * hood/* trunk, * Steering, seat_* empties), normalizes wheel/steering axes, unpacks texture atlases, and emits a ready-to-merge <name>.vehicle.json scene entity (contract: assets/blender/vehicles/CAR_MODELING.md)
    • import_vrm.py - VRM character import
  3. Build: CMake processes assets during build
  4. Runtime: Assets loaded from resources.cfg paths

Key Conventions

Module Registration

Modules register themselves with Flecs using the FLECS_CPP_NO_AUTO_REGISTRATION macro:

// In module header
struct MyModule {
    MyModule(flecs::world &ecs);
};

// In module implementation
MyModule::MyModule(flecs::world &ecs) {
    // Register systems, components, etc.
}

Scene Setup

Scenes are configured through ECS setup functions:

// Setup exterior game scene
ECS::setupExteriorScene(scnMgr, cameraNode, camera, renderWindow);

// Setup interior scene
ECS::setupInteriorScene(scnMgr, cameraNode, camera, renderWindow);

// Setup editor
ECS::setupEditor(scnMgr, cameraNode, camera, renderWindow);

Lua Scripting

Story content is written in Ink format and parsed to Lua:

  • Source: lua-scripts/stories/*.ink
  • Compiled: lua-scripts/stories/*.lua
  • Runtime: Narrator parser loads and executes stories

Development Workflow

Adding Assets

  1. Create/modify .blend file in assets/blender/
  2. Add export logic to relevant Python script if needed
  3. Add CMake custom command in CMakeLists.txt if new asset type
  4. Rebuild to process assets

Debugging

  • Use ECS::EngineData.enableDbgDraw for physics debug visualization
  • Tracy profiler integration for performance analysis
  • FPS and draw call stats printed to console

Security Considerations

  • Lua scripts run in sandboxed environment
  • File system access restricted to resource directories
  • No network functionality currently implemented

Additional Resources