Added src/features/editScene/AGENTS.md

This commit is contained in:
2026-06-22 00:50:21 +03:00
parent 68ca1f750d
commit d4974f5d90
+222
View File
@@ -0,0 +1,222 @@
# EditScene Feature - Agent Guide
This guide covers the `src/features/editScene` sub-project: the OGRE3D/Flecs scene
editor and the `--game` playable mode built on top of it.
## Overview
`editScene` is a self-contained executable (`editSceneEditor`) that can run in two
modes:
- **Editor mode** (default): load, edit and save JSON scenes through ImGui panels.
- **Game mode** (`--game`): present a startup menu, load a base scene, and let the
player control a character with save/load support.
The feature is built from `src/features/editScene/CMakeLists.txt` and links against
OGRE (Bites, Overlay, MeshLodGenerator), Flecs, nlohmann/json, SDL2, Jolt and
OgreProcedural. Recast/Detour is built from the copy in
`src/features/editScene/recastnavigation`.
## Build & Run
The CMake target is `editSceneEditor`. The working directory for the runtime must
be the executable's own directory because resources are copied next to it at build
time.
```bash
# From the project root
cmake -B build-vscode -S . -DCMAKE_PREFIX_PATH=/path/to/ogre-sdk
cmake --build build-vscode --target editSceneEditor -j4
# Run editor
cd build-vscode/src/features/editScene
./editSceneEditor
# Run game mode (loads the configured base scene through the startup menu)
./editSceneEditor --game
# Auto-load a scene in editor mode
./editSceneEditor /path/to/scene.json
```
The main test target is `component_lua_test`:
```bash
cmake --build build-vscode --target component_lua_test -j4
./build-vscode/src/features/editScene/component_lua_test
```
## Entry Point & Modes
- `main.cpp` parses `--game` and `--debug-buoyancy`, sets the mode on
`EditorApp`, calls `initApp()` and enters the OGRE render loop.
- `EditorApp` owns all systems and implements `frameRenderingQueued()`, which is
the per-frame update function.
- Modes/states are also exposed globally through `GameMode.hpp` so systems can
query `editScene::isGameMode()` / `editScene::isGamePlaying()` without a pointer
to `EditorApp`.
| Mode | Purpose |
|------|---------|
| `EditorApp::GameMode::Editor` | Free-camera scene editing, full UI panels |
| `EditorApp::GameMode::Game` | Playable mode with menu / playing / paused states |
| Game play state | Purpose |
|-----------------|---------|
| `Menu` | Startup menu, game not running yet |
| `Playing` | Player controller and gameplay systems active |
| `Paused` | Pause menu / character sheet open, gameplay systems frozen |
## Frame Update Order
`EditorApp::frameRenderingQueued()` updates systems in this order when not paused:
1. `PlayerControllerSystem` (only in `Playing`)
2. `CharacterSpawnerSystem`
3. `CharacterSlotSystem`
4. `AnimationTreeSystem` / `BehaviorTreeSystem`
5. `PathFollowingSystem`
6. `ProceduralMeshSystem`
7. `RoomLayoutSystem`
8. `CellGridSystem`
9. `NormalDebugSystem`
10. `NavMeshSystem`
11. `SmartObjectSystem`
12. `GoapPlannerSystem`
13. `GoapRunnerSystem`
14. `ActuatorSystem`
15. `EventHandlerSystem`
16. `CharacterSystem`
17. `BuoyancySystem`
18. `PhysicsSystem`
19. `HairPhysicsSystem` pose read-back
20. Rendering support systems (sun, skybox, water, light, LOD, etc.)
Systems that write animation state or velocity should respect systems that run
before them. In particular, `PlayerControllerSystem` runs first and is the only
system that should drive player locomotion animation.
## Important Subsystems
### PlayerControllerSystem
- Reads `GameInputState` and drives TPS/FPS camera and locomotion animation.
- Sets animation states on the `locomotion` state machine:
`idle` / `walking` / `running` and the swim equivalents.
- In game mode it adds `PlayerControlledComponent` to the target entity so AI
systems skip it.
- When `targetCharacterName` resolves to a `CharacterSpawnerComponent`, it locks
the spawner and forces a spawn; the controlled entity is the spawned instance,
not the spawner.
### CharacterSpawnerSystem
Distance-based spawn/despawn of registry characters. Special API for player
controller integration:
```cpp
void spawn(flecs::entity spawner);
void lockSpawner(flecs::entity spawner); // keep alive even out of range
void unlockSpawner(flecs::entity spawner);
flecs::entity getSpawnedCharacter(flecs::entity spawner) const;
flecs::entity getSpawnerForCharacter(flecs::entity character) const;
bool isSpawnerLocked(flecs::entity spawner) const;
```
Spawned characters have `EditorMarkerComponent` removed and are removed from
`EditorUISystem` caches so they don't appear in editor lists.
### Player Character Resolution
Never resolve the player by matching `PlayerControllerComponent::targetCharacterName`
against `EntityNameComponent` when the controller targets a spawner that returns
the spawner entity, not the character. Use the central helper:
```cpp
flecs::entity player = editorApp->getPlayerCharacterEntity();
```
This returns the live target (spawning through the spawner if necessary). It is
used by inventory/character-sheet UI, save/load and `ActuatorSystem`.
### Game Mode Input
Movement keys are polled each frame with `SDL_PumpEvents()` + `SDL_GetKeyboardState()`
to avoid stuck keys caused by missed `keyReleased` events. One-shot action flags
(`ePressed`, `fPressed`, `iPressed`) are still set from event handlers.
### AnimationTreeSystem & Root Motion
- Selects a root bone (`Root`, `mixamorig:Hips`, then `Spineroot`), freezes it,
and excludes it from animation via a blend mask.
- Computes `CharacterComponent::linearVelocity` from root-bone displacement when
`AnimationTreeComponent::useRootMotion` is true.
- `CharacterSystem` then applies that velocity to the Jolt physics character.
### ActuatorSystem
Handles player interaction prompts and executes smart-object/item actions. It
uses `EditorApp::getPlayerCharacterEntity()` to know which character performs the
action. Actions run through `BehaviorTreeSystem` and lock player input while
running (`PlayerControllerComponent::inputLocked`).
### Save/Load
Game-mode saves are JSON files in the OS user-data directory (see
`docs/SaveLoadSystem.md`). Key fields:
- `baseScene` the scene file loaded as the foundation
- `playerCharacterId` registry ID of the player character
- `characterRegistry` full character registry state
- `runtimeEntities` runtime-spawned entities (dropped items, etc.)
- `characterRuntimeData` per-character component overrides
- `luaData` data from Lua save callbacks
On load, if the saved character is owned by a spawner, the controller target is
restored to the spawner name so the character respawns correctly.
## Adding a New Component
1. Define the component struct in `components/MyComponent.hpp`.
2. Register it for UI editing in `components/MyComponentModule.cpp` using
`REGISTER_COMPONENT_GROUP(...)` and `registry.registerComponent<T>(...)`.
3. Implement an editor in `ui/MyComponentEditor.hpp` / `.cpp` deriving from
`ComponentEditor`.
4. If the component should be saved/loaded, add serialization support in
`systems/SceneSerializer.cpp`.
5. Add a Lua binding in `lua/LuaComponentApi.cpp` if needed.
6. Add a component-module test case in `tests/component_lua_test.cpp`.
## Adding a New System
1. Create `systems/MySystem.hpp` and `systems/MySystem.cpp`.
2. Add the source files to `CMakeLists.txt`.
3. Instantiate the system in `EditorApp::setup()` and wire any dependencies.
4. Call `update()` from `EditorApp::frameRenderingQueued()` in the appropriate
order.
5. Skip `PlayerControlledComponent` entities if the system would override player
animation or movement.
## Critical Constraints & Rules
- The source code is the only source of truth. Please check code as documentation might be out of sync.
- Please ask questions if anything is unclear.
- Please update tests, documentation and examples every time API or modules and systems change.
Make sure documentation, tests and examples are always in sync.
## Conventions & Caveats
- Use `editScene::isGameMode()` / `editScene::isGamePlaying()` for mode checks
when you don't have an `EditorApp` pointer.
- Systems that set animation states must not affect player-controlled entities;
check `e.has<PlayerControlledComponent>()` early.
- Spawner-targeted characters are transient instances; persistent player data
(inventory, identity) must live on the spawned instance, not the spawner.
- The editor uses `EditorMarkerComponent` to know which entities belong to the
scene file. Spawned/runtime characters usually don't have this tag.
- Keep changes in `EditorApp::frameRenderingQueued()` order-aware; systems later
in the list should not stomp state produced by earlier systems.
## Additional Docs
- `docs/SaveLoadSystem.md` save file format and API details
- Root `AGENTS.md` project-wide build, style and architecture notes