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

440 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```bash
# 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
```cpp
void myFunction(int arg)
{
if (arg > 0) {
doSomething();
return;
}
for (int i = 0; i < arg; i++) {
process(i);
}
}
```
### Running clang-format
```bash
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:
```cpp
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`):
```cpp
// 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:
```cpp
// 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:
```cpp
// 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
- **OGRE3D Docs**: https://ogrecave.github.io/ogre/api/latest/
- **Flecs Manual**: https://www.flecs.dev/flecs/
- **Jolt Physics**: https://jrouwe.github.io/JoltPhysics/
- **Ink Narrative**: https://www.inklestudios.com/ink/