Repository navigation
Animation
SparkEngine contains runtime skeletal-animation types and evaluators for bone hierarchies, state machines, multi-layer blending, inverse kinematics, and root-motion extraction.
stable-v1 support boundary:
stable-v1is blocked and uncertified; its exact host is Windows 11 x64. This page documents source-level runtime surfaces, not a certified animation authoring or import workflow.
Source: SparkEngine/Source/Engine/Animation/AnimationSystem.h
AnimationManager (singleton asset cache)
├── Skeleton (bone hierarchy loaded from Spark .skel data or a skinned glTF)
└── AnimationClip (keyframe data loaded from Spark .sanim data or glTF animations)
Per-entity runtime data:
AnimationInstance
├── AnimationStateMachine (state transitions, crossfade)
├── AnimationLayer[] (blend layer stack)
├── IKChain[] (IK post-processing)
└── BlendResult (final bone matrices → GPU)
Processing pipeline (per frame):
AnimationEvaluator::SampleClip() → local bone transforms
AnimationEvaluator::BlendTransforms() → blended local transforms
AnimationEvaluator::ComputeSkinningMatrices() → GPU skinning matrices
AnimationEvaluator::Solve*IK() → IK corrections
The animation system supports:
- Skeletal animation with bone hierarchies
- Keyframe animation clips with position, rotation, and scale tracks
- Animation state machines with cross-fading transitions
- Multi-layer blending (override, additive, layered with per-bone masks)
- Inverse kinematics (two-bone, look-at, FABRIK)
- Root motion extraction for locomotion
- Spark
.skelskeleton and.sanimanimation binary loaders, plus fail-closed glTF skeleton and LINEAR/STEP TRS joint-animation import for.gltf/.glb; FBX skeletons and clips are not loaded byAnimationManager
Each bone in a skeleton is represented by the Bone struct:
struct Bone
{
std::string name; // Matches export name from 3D tool (e.g. "Bip01_R_Hand")
int32_t parentIndex = -1; // Parent index (-1 for root bone)
XMFLOAT4X4 offsetMatrix; // Inverse bind pose (mesh-to-bone space)
XMFLOAT4X4 localBindPose; // Rest pose relative to parent
};| Field | Description |
|---|---|
name |
Human-readable name matching the authoring tool export |
parentIndex |
Index of parent bone (-1 for root); forms a tree hierarchy |
offsetMatrix |
Inverse bind pose matrix; transforms vertices from model space to bone space |
localBindPose |
Bone's transform relative to parent in the rest/bind pose |
struct Skeleton
{
std::string name;
std::vector<Bone> bones; // Ordered parent-before-child
std::unordered_map<std::string, int32_t> boneNameToIndex; // O(1) lookup
int32_t FindBone(const std::string& boneName) const; // Returns -1 if not found
size_t GetBoneCount() const; // Number of bones
};The bones array is ordered such that every bone appears AFTER its parent. This guarantees that when computing transforms in index order, a bone's parent world transform is always available.
auto& animMgr = Spark::Animation::AnimationManager::GetInstance();
// Load a skeleton (cached by filepath)
auto skeleton = animMgr.LoadSkeleton("Assets/Animations/Soldier.skel");
// Query bone information
int boneCount = skeleton->GetBoneCount(); // e.g. 65
int headIdx = skeleton->FindBone("Head"); // Returns index or -1
int spineIdx = skeleton->FindBone("Bip01_Spine"); // For bone masksstruct VectorKey
{
float time; // Time in seconds (sorted ascending)
XMFLOAT3 value; // 3D vector (position or scale)
};
struct QuatKey
{
float time; // Time in seconds (sorted ascending)
XMFLOAT4 value; // Quaternion rotation (x, y, z, w), normalized
};| Keyframe Type | Interpolation | Use |
|---|---|---|
VectorKey |
Linear (LERP) | Position and scale tracks |
QuatKey |
Spherical linear (SLERP) | Rotation tracks (avoids gimbal lock) |
struct BoneAnimation
{
std::string boneName; // Must match a bone in the target Skeleton
int32_t boneIndex = -1; // Pre-resolved index (cached for performance)
std::vector<VectorKey> positionKeys; // Position track (may be empty)
std::vector<QuatKey> rotationKeys; // Rotation track (may be empty)
std::vector<VectorKey> scaleKeys; // Scale track (may be empty)
XMFLOAT3 InterpolatePosition(float time) const; // Binary search + LERP
XMFLOAT4 InterpolateRotation(float time) const; // Binary search + SLERP
XMFLOAT3 InterpolateScale(float time) const; // Binary search + LERP
};Empty tracks default to the bone's bind pose value, allowing partial animation clips that only animate a subset of channels.
struct AnimationClip
{
std::string name; // Registration key (e.g. "Run", "Idle")
float duration = 0.0f; // Total duration in seconds
float ticksPerSecond = 24.0f; // Source asset playback rate (24, 30, or 60)
std::vector<BoneAnimation> channels; // Per-bone animation data
bool loop = true; // Auto-loop at end (false for one-shot)
const BoneAnimation* FindChannel(const std::string& boneName) const;
};auto& animMgr = Spark::Animation::AnimationManager::GetInstance();
// Load all clips from an animation file
auto clips = animMgr.LoadAnimations("Assets/Animations/Soldier_Anims.sanim");
// Register each clip by name for later lookup
for (auto& clip : clips)
animMgr.RegisterClip(clip->name, clip);
// Retrieve a clip by name
auto walkClip = animMgr.GetClip("Walk");
float duration = walkClip->duration; // e.g. 1.2 seconds
bool loops = walkClip->loop; // true
// Register a custom clip
auto customClip = std::make_shared<Spark::Animation::AnimationClip>();
customClip->name = "CustomIdle";
customClip->duration = 2.0f;
customClip->loop = true;
animMgr.RegisterClip("CustomIdle", customClip);The AnimationManager ensures each unique asset is loaded once. All runtime instances share clips and skeletons via shared_ptr.
| Method | Description |
|---|---|
GetInstance() |
Access the global singleton |
LoadSkeleton(filepath) |
Load/cache a skinned .gltf/.glb skin through LoadGLTFSkinnedMesh() (the file's geometry and weights must also pass), or a Spark SKEL/.skel skeleton binary; failed loads are not cached; the .skel reader rejects a name-length prefix outside [0, 256), a non-finite matrix, or a parentIndex that does not refer to an already-read bone |
LoadAnimations(filepath) |
Load clips from a skinned .gltf/.glb through LoadGLTFAnimationClips() (one clip per glTF animation; any rejected animation returns no clips) or from a Spark ANIM/.sanim binary (does NOT auto-register); a name-length prefix outside [0, 256) aborts the load (previously the loader skipped the name without consuming its bytes, desynced, and still reported success) |
There is no partial parse. Any corruption — a rejected length prefix, an out-of-range count, a
non-finite matrix, an invalid parent index, or truncation anywhere inside a channel's keyframes —
aborts the whole file: LoadAnimations returns zero clips and LoadSkeleton returns an empty
skeleton, both logging at ERROR with explicit failure wording and no Loaded N ... success line. A
failed skeleton load is not cached, so a repaired asset can be reloaded in the same process.
Callers must not treat a non-null shared_ptr as proof of a successful load — check the contents.
| RegisterClip(name, clip) | Register a clip by name for lookup |
| GetClip(name) | Retrieve a cached clip (nullptr if not found) |
| GetSkeleton(name) | Retrieve a cached skeleton |
| Clear() | Release all cached assets (shared_ptr holders retain data) |
| Console_ListAnimations() | List all registered clip names |
| Console_ListSkeletons() | List all loaded skeleton names |
Animation state machines manage transitions between clips with configurable cross-fading.
┌───────┐ speed > 0.1 ┌───────┐
│ Idle │ ──────────────── │ Walk │
│ │ ◄─────────────── │ │
└───────┘ speed < 0.05 └───────┘
│ │
│ jump trigger │ speed > 5.0
▼ ▼
┌───────┐ ┌───────┐
│ Jump │ │ Run │
└───────┘ └───────┘
struct AnimationState
{
std::string name; // Unique name (e.g. "Idle", "Run", "Shoot")
std::string clipName; // AnimationClip to play (must be registered)
float speed = 1.0f; // Playback speed multiplier (1.5 for sprint)
bool loop = true; // Loop the clip while in this state
};struct AnimationTransition
{
std::string fromState; // Source state ("*" matches any)
std::string toState; // Destination state
float duration = 0.2f; // Crossfade blend duration (seconds)
std::function<bool()> condition; // Predicate (first satisfied wins)
bool hasExitTime = false; // Wait for clip to reach exitTime
float exitTime = 1.0f; // Normalized exit time [0, 1]
};| Field | Description |
|---|---|
fromState |
Source state name; "*" matches any state |
toState |
Destination state name |
duration |
Crossfade blend time (0.1-0.2s for action, 0.3-0.5s for locomotion) |
condition |
Lambda returning true to trigger the transition |
hasExitTime |
If true, wait until the clip reaches exitTime before transitioning |
exitTime |
Normalized time threshold [0, 1]; 1.0 = must reach end of clip |
Spark::Animation::AnimationStateMachine sm;
// Add states (each maps to an animation clip)
sm.AddState({"Idle", "idle_anim", 1.0f, true});
sm.AddState({"Walk", "walk_anim", 1.0f, true});
sm.AddState({"Run", "run_anim", 1.0f, true});
sm.AddState({"Jump", "jump_anim", 1.0f, false}); // one-shot
sm.SetDefaultState("Idle");
// Add condition-triggered transitions
sm.AddTransition({"Idle", "Walk", 0.2f, [&]{ return speed > 0.1f; }});
sm.AddTransition({"Walk", "Idle", 0.2f, [&]{ return speed < 0.05f; }});
sm.AddTransition({"Walk", "Run", 0.15f, [&]{ return speed > 5.0f; }});
sm.AddTransition({"Run", "Walk", 0.15f, [&]{ return speed <= 5.0f; }});
// Exit-time transition: wait for jump to finish before returning to Idle
sm.AddTransition({"Jump", "Idle", 0.1f, nullptr, true, 0.9f});
// Drive the state machine each frame
sm.Update(deltaTime);
// Query current state
std::string current = sm.GetCurrentStateName(); // "Idle"
bool blending = sm.IsTransitioning(); // false
float blend = sm.GetBlendFactor(); // 0.0| Method | Description |
|---|---|
AddState(state) |
Register a state |
AddTransition(transition) |
Register a transition |
SetDefaultState(name) |
Set the initial/entry state |
Update(deltaTime) |
Advance the machine one frame |
GetCurrentStateName() |
Name of the active state |
GetCurrentTime() |
Playback time in the current clip |
GetBlendFactor() |
Crossfade progress [0, 1] |
IsTransitioning() |
Whether a crossfade is active |
GetTargetStateName() |
Target state during crossfade |
GetTargetTime() |
Playback time in the target clip |
GetCurrentClipName() |
Clip name for the current state |
GetTargetClipName() |
Clip name for the target state |
ForceState(name) |
Immediately switch state (no crossfade) |
Console_GetStateInfo() |
Debug info string |
Multiple animation layers can be combined. Layers are processed bottom-to-top (index 0 = base).
enum class BlendMode
{
Override, // Fully replace lower layers for affected bones
Additive, // Add delta from bind pose on top of lower layers
Layered // Linearly blend with lower layers using weight
};| Mode | Description | Use Case |
|---|---|---|
| Override | Completely replaces lower layers | Upper body shooting over lower body walk |
| Additive | Adds animation delta on top of base | Breathing cycle, hit reactions |
| Layered | Weight-based blend with lower layers | Smooth transitions, partial overrides |
struct AnimationLayer
{
std::string clipName; // Clip to play
float weight = 1.0f; // Blend weight [0, 1] (Layered mode)
float currentTime = 0.0f; // Playback position (seconds)
float speed = 1.0f; // Speed multiplier (negative = reverse)
BlendMode blendMode = BlendMode::Override;
bool playing = true; // Whether playback is advancing
bool loop = true; // Loop at end of clip
std::vector<int32_t> boneMask; // Affected bone indices (empty = all)
};Spark::Animation::AnimationInstance instance;
instance.skeleton = animMgr.GetSkeleton("soldier").get();
// Base layer: full-body walk cycle
Spark::Animation::AnimationLayer baseLayer;
baseLayer.clipName = "Walk";
baseLayer.weight = 1.0f;
baseLayer.blendMode = Spark::Animation::BlendMode::Override;
baseLayer.playing = true;
baseLayer.loop = true;
instance.layers.push_back(baseLayer);
// Upper-body layer: reload animation (only affects spine and arms)
Spark::Animation::AnimationLayer upperLayer;
upperLayer.clipName = "Reload";
upperLayer.weight = 1.0f;
upperLayer.blendMode = Spark::Animation::BlendMode::Override;
upperLayer.playing = true;
upperLayer.loop = false;
upperLayer.boneMask = {
skeleton->FindBone("Spine"), skeleton->FindBone("Spine1"),
skeleton->FindBone("Spine2"),
skeleton->FindBone("LeftArm"), skeleton->FindBone("LeftForeArm"),
skeleton->FindBone("LeftHand"),
skeleton->FindBone("RightArm"), skeleton->FindBone("RightForeArm"),
skeleton->FindBone("RightHand")
};
instance.layers.push_back(upperLayer);
// Additive breathing layer
Spark::Animation::AnimationLayer breathLayer;
breathLayer.clipName = "Breathing";
breathLayer.weight = 0.3f;
breathLayer.blendMode = Spark::Animation::BlendMode::Additive;
breathLayer.playing = true;
breathLayer.loop = true;
instance.layers.push_back(breathLayer);
// Update each frame
instance.Update(deltaTime);Three IK solvers are available, applied as a post-processing pass after animation blending.
enum class IKType
{
TwoBone, // Analytical 2-joint solver (arms, legs) — fast and exact
LookAt, // Single-bone rotation to face target (head tracking, turrets)
FABRIK // Iterative multi-joint solver for arbitrary chain length
};struct IKChain
{
std::string name; // Debug name (e.g. "RightArmIK")
IKType type = IKType::TwoBone; // Solver algorithm
std::vector<int32_t> boneIndices; // Ordered root-to-end-effector
XMFLOAT3 targetPosition{0, 0, 0}; // World-space target
XMFLOAT3 poleVector{0, 1, 0}; // Joint bend hint (TwoBone only)
float weight = 1.0f; // IK influence [0, 1]
bool enabled = true; // Skip when false
int maxIterations = 10; // FABRIK iterations per frame
float tolerance = 0.01f; // FABRIK convergence threshold (metres)
};For limbs (arms, legs). Requires exactly 3 bone indices: [root, middle, end-effector].
Shoulder ─── Elbow ─── Hand → Target
Spark::Animation::IKChain rightArm;
rightArm.name = "RightArmIK";
rightArm.type = Spark::Animation::IKType::TwoBone;
rightArm.boneIndices = {skeleton->FindBone("RightArm"),
skeleton->FindBone("RightForeArm"),
skeleton->FindBone("RightHand")};
rightArm.targetPosition = weaponGripPos;
rightArm.poleVector = elbowHintPos; // Determines elbow bend direction
rightArm.weight = 1.0f;
rightArm.enabled = true;
instance.ikChains.push_back(rightArm);Rotates a single bone to face a target. Requires exactly 1 bone index.
Head bone → Look at target position
Spark::Animation::IKChain lookAt;
lookAt.name = "HeadLookAt";
lookAt.type = Spark::Animation::IKType::LookAt;
lookAt.boneIndices = {skeleton->FindBone("Head")};
lookAt.targetPosition = enemyPosition;
lookAt.weight = 0.8f; // Partial blend with base animation
lookAt.enabled = true;
instance.ikChains.push_back(lookAt);Iterative multi-joint solver for chains of any length (2+ bones). Converges quickly (typically 5-10 iterations).
Root ─── Joint1 ─── Joint2 ─── ... ─── End Effector → Target
Spark::Animation::IKChain tail;
tail.name = "TailIK";
tail.type = Spark::Animation::IKType::FABRIK;
tail.boneIndices = {skeleton->FindBone("Tail1"), skeleton->FindBone("Tail2"),
skeleton->FindBone("Tail3"), skeleton->FindBone("Tail4")};
tail.targetPosition = swayTarget;
tail.maxIterations = 10; // More iterations = more accurate
tail.tolerance = 0.01f; // Stop early if within 1cm
tail.weight = 1.0f;
tail.enabled = true;
instance.ikChains.push_back(tail);The AnimationEvaluator is a static utility class with pure functions for per-frame animation computation. The AnimationUpdateSystem calls these methods in order:
| Method | Input | Output | Description |
|---|---|---|---|
SampleClip() |
Clip + Skeleton + time | localTransforms[] |
Interpolate keyframes for all bones |
BlendTransforms() |
Two pose arrays + factor | Blended localTransforms[]
|
Component-wise LERP for crossfade |
ComputeSkinningMatrices() |
Skeleton + local transforms | finalTransforms[] |
Walk hierarchy, multiply offset matrices |
SolveTwoBoneIK() |
Local transforms + IKChain | Modified transforms | Analytical 2-joint solution |
SolveLookAtIK() |
Local transforms + IKChain | Modified transforms | Single-bone rotation toward target |
SolveFABRIK() |
Local transforms + IKChain | Modified transforms | Iterative multi-joint solution |
Each animated entity has one AnimationInstance:
struct AnimationInstance
{
const Skeleton* skeleton = nullptr; // Shared skeleton reference
AnimationStateMachine stateMachine; // Clip selection controller
std::vector<AnimationLayer> layers; // Blend layer stack
std::vector<IKChain> ikChains; // IK post-processing chains
BlendResult blendResult; // Output bone matrices
XMFLOAT3 rootMotionDelta{0, 0, 0}; // Root bone translation delta
XMFLOAT4 rootMotionRotationDelta{0, 0, 0, 1}; // Root bone rotation delta (quat)
bool enableRootMotion = false; // Extract root bone motion
void Update(float deltaTime); // Full per-entity pipeline
void UpdateLayers(float deltaTime); // Layer playback and blending
};struct BlendResult
{
std::vector<XMFLOAT4X4> localTransforms; // Per-bone local transforms (intermediate)
std::vector<XMFLOAT4X4> finalTransforms; // GPU-ready skinning matrices
};finalTransforms is uploaded to the vertex shader's bone constant buffer each frame.
Root motion extraction moves the character based on animation data rather than gameplay code:
instance.enableRootMotion = true;
instance.Update(deltaTime);
// Apply root motion delta to the character's transform
XMFLOAT3 moveDelta = instance.rootMotionDelta;
transform.position.x += moveDelta.x;
transform.position.y += moveDelta.y;
transform.position.z += moveDelta.z;
// Apply rotation delta for turn-in-place
XMFLOAT4 rotDelta = instance.rootMotionRotationDelta;
// ... apply quaternion multiplication to transform.rotationBenefits:
- Translation from the root bone drives character movement (prevents foot sliding)
- Rotation from the root bone drives character facing
- Useful for realistic walk/run cycles, combat animations, and dance moves
Use AnimationController on entities (see Entity Component System):
auto& anim = world.AddComponent<AnimationController>(entity);
anim.currentAnimation = "Idle";
anim.playbackSpeed = 1.0f;
anim.isPlaying = true;
anim.loop = true;
anim.blendFactor = 1.0f;The AnimationUpdateSystem evaluates state machines and blends poses each frame. It:
- Locates or creates an
AnimationInstancefor each entity withAnimationController - Advances the state machine and blend layer playback times
- Samples clips and blends transforms
- Computes GPU-ready skinning matrices
- Solves enabled IK chains
- Uploads bone matrices to the per-entity GPU constant buffer
LoadSkeleton() and LoadAnimations() accept the engine's binary SKEL/.skel and ANIM/.sanim layouts, and skinned .gltf/.glb files through the fail-closed cgltf importers in Graphics/. Registration of clips stays with the caller.
| Source surface | Current animation handoff |
|---|---|
Skinned glTF (LoadGLTFSkinnedMesh()) |
LoadSkeleton("x.glb") returns the file's one skin as a parent-first Skeleton (inverse binds as offsetMatrix; the root's bind pose folds in non-joint ancestors) and caches it by path |
glTF animations (LoadGLTFAnimationClips()) |
LoadAnimations("x.glb") returns one AnimationClip per glTF animation, keyed in seconds (ticksPerSecond 1), with each channel bound by name and index to the skeleton bone of its joint |
Native FBXImporter
|
Exposes bone and animation result structures, but the mesh path does not convert or register them with AnimationManager
|
| DAE/Collada | No current loader-to-AnimationManager path is present |
Spark .skel / .sanim
|
Formats consumed directly by AnimationManager
|
The glTF clip importer accepts LINEAR and STEP translation, rotation and scale channels on skin joints only. STEP samplers are stored as LINEAR keys with an extra hold key one float step before each following key, so the evaluator returns the previous key until the next key time. Values keep glTF's right-handed Y-up coordinates and (x, y, z, w) quaternions unchanged, in the row-vector matrix layout of the skinned importer, so no axis conversion happens anywhere between the file and the skinning matrices. A channel replaces the bone's whole local transform when sampled and an empty track samples as identity, so paths the file does not animate are filled with the joint's rest value; rotations are renormalized. It rejects, and returns no clips for the whole file: CUBICSPLINE samplers, morph weights channels, channels on non-joint nodes, animated joints that use a matrix, an animated root joint under a non-identity non-joint ancestor (apply the armature transform before export), key times that are not finite, non-negative and strictly increasing, output counts that differ from the key count, wrong accessor types, zero-length rotations, duplicate channels for one joint path, animations without channels, duplicate animation names, and more than 1,000,000 keys in one sampler. The file must be the skinned file itself: LoadGLTFAnimationClips() first runs LoadGLTFSkinnedMesh() on it (skin, geometry, joints and weights), the importer LoadSkeleton() uses, so a file whose skin is valid but whose weights or joint indices are not yields no clips, just as it yields no skeleton. Animation-only glTF files are rejected.
Evidence: GLTF_Animation_* (ctest GLTFAnimationImport, exact count) builds every GLB in-test and drives AnimationManager::LoadSkeleton/LoadAnimations/RegisterClip and the portable MeshAsset. The posed-vertex tests sample an imported LINEAR+STEP clip through AnimationEvaluator::SampleClip and ComputeSkinningMatrices, apply linear blend skinning to the imported vertices, and compare them at several times (start, between keys, on and just before a STEP key, end for a one-shot clip, wrap for a looping clip) against a reference written in the test from the glTF node TRS in closed form (Rodrigues rotations, column-vector glTF matrices, no engine math); a mutated keyframe must fail that comparison. That reference exposed that the non-Windows DirectXMath stub XMQuaternionSlerp was a plain unnormalized lerp, which skewed every blended rotation on Linux/macOS; it now follows DirectXMath's shortest-arc slerp. This is CPU correctness only. No skinned draw path consumes the bone data (GPUSkinning has no production caller and no shader reads BLENDINDICES/BLENDWEIGHT), the Windows D3D11 MeshAsset still rejects skinned glTF, and there is no Blender-authored skinned or animated fixture. FBX validation tests do not establish an animated-character import workflow. See Asset Pipeline for the mesh-format limits.
The animation system is main thread only. All state machine updates, blending, and IK solving must occur on the main update thread. The AnimationManager singleton is not thread-safe for concurrent reads and writes.
- Entity Component System -- AnimationController component
- Asset Pipeline -- Model-format and runtime animation boundaries
- SparkEditor -- Animation timeline editor
- Rendering and Graphics -- Skinned mesh rendering
- AI and Navigation -- NPC animation integration
- Physics -- Root motion and physics interaction
- Gameplay Systems -- Player and weapon animations
- Cinematic Sequencer -- Animation in cinematics
Published from 614bca17b41e. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Platform Certification
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Fuzz Policy and Parser Security
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Crash Reporting
- Golden Image Testing
- Utilities
- Testing
- Fuzz Policy and Parser Security
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- CI Controlled-Failure Rehearsal
- Release Publication Stages
- Session Handoff 2026-09-27
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Online Service Boundary
- Server Operations Runbook
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers