Repository navigation
Cloth Simulation
SparkEngine provides a position-based dynamics (PBD) cloth simulator for flags, capes, curtains, and other deformable surfaces. The system supports structural, shear, and bending constraints, pin constraints, wind and gravity forces, collision with simple shapes, and an optional soft body system for volumetric deformable objects.
Source: SparkEngine/Source/Physics/ClothSimulation.h
The cloth simulation uses a particle-constraint model where a grid of mass points (particles) is connected by distance constraints. Each simulation step applies external forces, integrates particle positions using Verlet integration, iteratively solves constraints to maintain structural integrity, and resolves collisions with the environment.
+-------------------+
| ClothSimulation | Owns and manages all cloth instances
| Update(dt) |
+--------+----------+
|
| for each enabled instance:
v
+-------------------+ +-------------------+
| ClothInstance #1 | | ClothInstance #2 |
| particles[] | | particles[] |
| constraints[] | | constraints[] |
| colliders[] | | colliders[] |
+--------+----------+ +-------------------+
|
| SimulateCloth(dt)
v
+-------------------------------------------+
| 1. ApplyForces() — gravity + wind |
| 2. IntegratePositions() — Verlet step |
| 3. SolveConstraints() — N iterations |
| 4. HandleCollisions() — sphere/plane |
+-------------------------------------------+
deltaTime
|
v
+----------+----------+
| ApplyForces() |
| gravity + wind |
| -> acceleration |
+----------+----------+
|
v
+----------+----------+
| IntegratePositions()|
| Verlet integration |
| position += vel*dt |
| + accel*dt^2 |
+----------+----------+
|
v
+----------+----------+
| SolveConstraints() | <---+
| distance correction| | repeat N times
| for each constraint| | (solverIterations)
+----------+----------+ ----+
|
v
+----------+----------+
| HandleCollisions() |
| sphere, capsule, |
| plane collision |
+----------+----------+
|
v
Particles ready for
rendering upload
#include "Physics/ClothSimulation.h"
// All types live in the Spark::Physics namespace
using namespace Spark::Physics;Required headers pulled in by ClothSimulation.h:
| Header | Purpose |
|---|---|
Core/Platform.h |
Cross-platform math type stubs |
<DirectXMath.h> |
XMFLOAT3, XMFLOAT4 (Windows only) |
<string> |
Console status output |
<vector> |
Particle, constraint, and collider storage |
<unordered_map> |
Instance lookup by ID |
<cstdint> |
uint32_t for particle indices and cloth IDs |
A single point mass in the cloth simulation grid.
struct ClothParticle
{
DirectX::XMFLOAT3 position{0, 0, 0}; ///< Current world position
DirectX::XMFLOAT3 prevPosition{0, 0, 0}; ///< Previous frame position (for Verlet integration)
DirectX::XMFLOAT3 velocity{0, 0, 0}; ///< Current velocity (derived from position delta)
DirectX::XMFLOAT3 acceleration{0, 0, 0}; ///< Accumulated forces divided by mass
float inverseMass = 1.0f; ///< Inverse mass (0 = pinned/infinite mass)
bool pinned = false; ///< Whether this particle is pinned to a fixed position
};Field Details:
| Field | Type | Description |
|---|---|---|
position |
XMFLOAT3 |
Current world-space position. Updated each frame by integration and constraint solving. |
prevPosition |
XMFLOAT3 |
Position from the previous frame. Used by Verlet integration to implicitly encode velocity. |
velocity |
XMFLOAT3 |
Derived velocity computed after integration for external queries (rendering, audio). Not used internally by the integrator. |
acceleration |
XMFLOAT3 |
Sum of all forces (gravity, wind) divided by particle mass. Reset and recomputed each frame in ApplyForces(). |
inverseMass |
float |
Reciprocal of the particle mass. Set to 0 for pinned particles (effectively infinite mass). Used during constraint solving to distribute corrections proportionally. |
pinned |
bool |
When true, the particle's position is fixed and unaffected by forces, integration, or constraints. |
A distance constraint between two particles that maintains their rest distance.
struct ClothConstraint
{
uint32_t particleA = 0; ///< Index of the first particle
uint32_t particleB = 0; ///< Index of the second particle
float restLength = 0.0f; ///< Target distance between particles
float stiffness = 1.0f; ///< Constraint stiffness (0.0 = no correction, 1.0 = full correction)
};The constraint solver computes the current distance between particleA and particleB, then pushes or pulls them toward the restLength. The stiffness parameter controls how much of the error is corrected each iteration. Multiple solver iterations with stiffness < 1.0 converge to a stable solution while allowing some elasticity.
Constraint types created during cloth initialization:
| Type | Connection Pattern | Rest Length | Stiffness |
|---|---|---|---|
| Structural (horizontal) |
(x, y) to (x+1, y)
|
spacing |
desc.stiffness |
| Structural (vertical) |
(x, y) to (x, y+1)
|
spacing |
desc.stiffness |
| Shear (diagonal) |
(x, y) to (x+1, y+1)
|
spacing * 1.414 |
desc.stiffness * 0.8 |
| Bending (horizontal) |
(x, y) to (x+2, y)
|
spacing * 2.0 |
desc.bendStiffness |
| Bending (vertical) |
(x, y) to (x, y+2)
|
spacing * 2.0 |
desc.bendStiffness |
Constraint Layout for a 4x4 Grid:
P---P---P---P P = particle
|\ /|\ /|\ /| - = structural (horizontal)
| X | X | X | | = structural (vertical)
|/ \|/ \|/ \| \ / = shear (diagonal)
P---P---P---P Bending constraints skip one particle
|\ /|\ /|\ /| (not shown for clarity)
| X | X | X |
|/ \|/ \|/ \|
P---P---P---P
|\ /|\ /|\ /|
| X | X | X |
|/ \|/ \|/ \|
P---P---P---P
A simple shape used for cloth-to-environment collision detection.
struct ClothCollider
{
enum class Type
{
Sphere, ///< Spherical collider
Capsule, ///< Capsule collider (sphere-swept line)
Plane ///< Infinite half-plane collider
};
Type type = Type::Sphere;
DirectX::XMFLOAT3 position{0, 0, 0}; ///< Center position (sphere/capsule) or point on plane
DirectX::XMFLOAT3 normal{0, 1, 0}; ///< Normal for plane; axis direction for capsule
float radius = 0.5f; ///< Radius for sphere/capsule
float height = 1.0f; ///< Total height for capsule
};Collider Type Details:
| Type |
position Meaning |
normal Meaning |
radius |
height |
|---|---|---|---|---|
Sphere |
Center of sphere | Unused | Sphere radius | Unused |
Capsule |
Center of capsule | Capsule axis direction (unit vector) | Capsule radius | Total capsule height |
Plane |
Any point on the plane | Plane normal (points toward valid side) | Unused | Unused |
Collision Response:
- Sphere: If a particle is inside the sphere, it is projected onto the sphere surface along the vector from the sphere center to the particle.
- Plane: If a particle is on the negative side of the plane (dot product with normal < 0), it is projected onto the plane surface.
- Capsule: Treated as the union of a cylinder and two hemisphere end caps.
Configuration parameters for creating a new cloth instance.
struct ClothDescriptor
{
int width = 10; ///< Particles in X direction
int height = 10; ///< Particles in Y direction
float spacing = 0.1f; ///< Distance between adjacent particles (meters)
float mass = 1.0f; ///< Total cloth mass (kg)
float stiffness = 0.9f; ///< Structural constraint stiffness (0.0-1.0)
float bendStiffness = 0.5f; ///< Bending constraint stiffness (0.0-1.0)
float damping = 0.01f; ///< Velocity damping factor (0.0-1.0)
DirectX::XMFLOAT3 origin{0, 0, 0}; ///< World-space origin of the cloth grid
int solverIterations = 4; ///< Constraint solver iterations per frame
};Parameter Guide:
| Field | Default | Range | Effect of Increasing |
|---|---|---|---|
width |
10 | 2-100 | More particles horizontally; higher detail but more CPU cost |
height |
10 | 2-100 | More particles vertically; higher detail but more CPU cost |
spacing |
0.1 | 0.01-1.0 | Larger cloth area; particles farther apart |
mass |
1.0 | 0.01-100 | Heavier cloth; sags more under gravity, less affected by wind |
stiffness |
0.9 | 0.0-1.0 | Stiffer cloth; maintains shape better, less stretchy |
bendStiffness |
0.5 | 0.0-1.0 | More resistance to bending; cloth appears thicker/stiffer |
damping |
0.01 | 0.0-0.5 | More energy loss; cloth settles faster, less oscillation |
solverIterations |
4 | 1-32 | More accurate constraint solving; stiffer appearance |
Runtime state of a single cloth simulation, created by CreateCloth().
struct ClothInstance
{
uint32_t id = 0; ///< Unique instance handle
std::vector<ClothParticle> particles; ///< All particles in the cloth grid
std::vector<ClothConstraint> constraints; ///< All constraints (structural, shear, bending)
std::vector<ClothCollider> colliders; ///< Colliders for this cloth instance
DirectX::XMFLOAT3 gravity{0, -9.81f, 0}; ///< Gravity vector
DirectX::XMFLOAT3 wind{0, 0, 0}; ///< Wind force vector
float damping = 0.01f; ///< Velocity damping
int solverIterations = 4; ///< Constraint iterations per step
int width = 0; ///< Grid width (particles)
int height = 0; ///< Grid height (particles)
bool enabled = true; ///< Whether simulation is active
};Particle indexing: Particles are stored in row-major order. The particle at grid position (x, y) has index y * width + x. This is important when using PinParticle() and UnpinParticle().
Configuration for creating a volumetric soft body (tetrahedral mesh).
struct SoftBodyDescriptor
{
std::vector<DirectX::XMFLOAT3> vertices; ///< Initial vertex positions
std::vector<uint32_t> tetrahedra; ///< Tetrahedral mesh connectivity (4 indices per tet)
float mass = 5.0f; ///< Total body mass (kg)
float stiffness = 0.8f; ///< Distance constraint stiffness
float volumeStiffness = 0.9f; ///< Volume preservation constraint strength
float damping = 0.02f; ///< Velocity damping
int solverIterations = 8; ///< Constraint iterations per step
};Soft bodies use the same PBD framework as cloth but add volume preservation constraints that prevent the tetrahedral mesh from collapsing.
class ClothSimulation
{
public:
ClothSimulation();
~ClothSimulation() = default;
// --- Lifecycle ---
uint32_t CreateCloth(const ClothDescriptor& desc);
void DestroyCloth(uint32_t clothId);
// --- Particle control ---
void PinParticle(uint32_t clothId, uint32_t particleIndex, const DirectX::XMFLOAT3& position);
void UnpinParticle(uint32_t clothId, uint32_t particleIndex);
// --- Forces ---
void SetWind(uint32_t clothId, const DirectX::XMFLOAT3& wind);
// --- Colliders ---
void AddCollider(uint32_t clothId, const ClothCollider& collider);
// --- Simulation ---
void Update(float deltaTime);
// --- Queries ---
const std::vector<ClothParticle>& GetParticles(uint32_t clothId) const;
void GetClothDimensions(uint32_t clothId, int& width, int& height) const;
size_t GetInstanceCount() const;
// --- Console ---
std::string Console_GetStatus() const;
};Method Reference:
| Method | Return | Description |
|---|---|---|
CreateCloth(desc) |
uint32_t |
Creates a new cloth from the descriptor. Returns a handle for all subsequent operations. Particles are laid out in a flat grid at desc.origin. |
DestroyCloth(id) |
void |
Removes and destroys a cloth instance. The handle becomes invalid. |
PinParticle(id, idx, pos) |
void |
Pins a particle to a fixed world position. Sets inverseMass = 0 and pinned = true. The particle will not move during simulation. |
UnpinParticle(id, idx) |
void |
Unpins a particle, restoring inverseMass = 1.0 and allowing it to move freely. |
SetWind(id, wind) |
void |
Sets the wind force vector for a cloth instance. Wind is applied as a constant acceleration to all non-pinned particles. |
AddCollider(id, collider) |
void |
Adds a collision shape to a cloth instance. Multiple colliders can be added to a single cloth. |
Update(deltaTime) |
void |
Steps all enabled cloth instances forward by deltaTime seconds. Calls SimulateCloth() for each. |
GetParticles(id) |
const vector<ClothParticle>& |
Returns a read-only reference to the particle array for rendering. Returns an empty static vector if the ID is invalid. |
GetClothDimensions(id, w, h) |
void |
Outputs the grid dimensions. Sets both to 0 if the ID is invalid. |
GetInstanceCount() |
size_t |
Returns the number of active cloth instances. |
Console_GetStatus() |
std::string |
Returns a formatted string listing all instances with particle/constraint/collider counts. |
These methods implement the core PBD simulation loop:
| Method | Description |
|---|---|
SimulateCloth(cloth, dt) |
Orchestrates the full simulation step: forces, integration, constraints, collisions. |
ApplyForces(cloth, dt) |
Computes acceleration = gravity + wind for each non-pinned particle. |
IntegratePositions(cloth, dt) |
Verlet integration: newPos = pos + (pos - prevPos) * dampFactor + accel * dt^2. Also derives velocity for external queries. |
SolveConstraints(cloth) |
Iterates over all constraints, correcting particle positions to satisfy rest lengths. Corrections are weighted by inverse mass ratios. |
HandleCollisions(cloth) |
Projects particles out of collider volumes (sphere surface projection, plane half-space projection). |
ClothSimulation cloth;
// Create a 10x10 particle cloth (100 particles, ~490 constraints)
ClothDescriptor desc;
desc.width = 10;
desc.height = 10;
desc.spacing = 0.1f;
desc.mass = 0.5f;
desc.stiffness = 0.9f;
desc.bendStiffness = 0.5f;
desc.damping = 0.01f;
desc.solverIterations = 4;
auto id = cloth.CreateCloth(desc);
// Pin top-left and top-right corners (row 0, columns 0 and 9)
cloth.PinParticle(id, 0, {0, 2, 0}); // particle (0,0)
cloth.PinParticle(id, 9, {0.9f, 2, 0}); // particle (9,0)
// Apply wind blowing in the +X direction
cloth.SetWind(id, {1.0f, 0, 0.5f});
// Per frame in your game loop:
cloth.Update(deltaTime);
// Get particles for rendering
const auto& particles = cloth.GetParticles(id);
// Upload particle positions to a dynamic vertex bufferParticles are stored in row-major order: index = y * width + x.
Grid layout for width=5, height=4:
Row 0: [0] [1] [2] [3] [4] <- top row
Row 1: [5] [6] [7] [8] [9]
Row 2: [10] [11] [12] [13] [14]
Row 3: [15] [16] [17] [18] [19] <- bottom row
Flag (pinned along left edge):
for (int y = 0; y < desc.height; ++y)
{
uint32_t idx = static_cast<uint32_t>(y * desc.width);
float yPos = 2.0f - static_cast<float>(y) * desc.spacing;
cloth.PinParticle(id, idx, {0, yPos, 0});
}Curtain (pinned along top edge):
for (int x = 0; x < desc.width; ++x)
{
float xPos = static_cast<float>(x) * desc.spacing;
cloth.PinParticle(id, static_cast<uint32_t>(x), {xPos, 3.0f, 0});
}Cape (pinned at two shoulder points):
cloth.PinParticle(id, 0, shoulderLeft);
cloth.PinParticle(id, static_cast<uint32_t>(desc.width - 1), shoulderRight);Tablecloth (pinned at four corners):
uint32_t w = static_cast<uint32_t>(desc.width);
uint32_t h = static_cast<uint32_t>(desc.height);
cloth.PinParticle(id, 0, cornerTL); // top-left
cloth.PinParticle(id, w - 1, cornerTR); // top-right
cloth.PinParticle(id, (h - 1) * w, cornerBL); // bottom-left
cloth.PinParticle(id, (h - 1) * w + w - 1, cornerBR); // bottom-rightAdd simple colliders to prevent cloth from passing through objects:
// Sphere collider (e.g., a ball the cloth drapes over)
ClothCollider sphere;
sphere.type = ClothCollider::Type::Sphere;
sphere.position = {0, 1, 0};
sphere.radius = 0.3f;
cloth.AddCollider(id, sphere);
// Ground plane
ClothCollider ground;
ground.type = ClothCollider::Type::Plane;
ground.position = {0, 0, 0};
ground.normal = {0, 1, 0};
cloth.AddCollider(id, ground);
// Capsule collider (e.g., a character limb)
ClothCollider capsule;
capsule.type = ClothCollider::Type::Capsule;
capsule.position = {0.5f, 1.0f, 0};
capsule.normal = {0, 1, 0}; // Capsule axis direction
capsule.radius = 0.1f;
capsule.height = 0.6f;
cloth.AddCollider(id, capsule);The collision handler runs after constraint solving. For each collider, every non-pinned particle is tested:
Sphere collision:
if distance(particle, sphere.center) < sphere.radius:
push particle to sphere surface along the center-to-particle direction
Plane collision:
signed_distance = dot(particle.pos - plane.pos, plane.normal)
if signed_distance < 0:
particle.pos -= plane.normal * signed_distance
Colliders do not apply friction or restitution in the current implementation. Particles that penetrate a collider are simply projected to the collider surface.
The cloth simulation uses Verlet integration rather than Euler integration because it is more stable for constraint-based systems and implicitly handles velocity.
Verlet integration formula:
newPosition = position + (position - prevPosition) * dampFactor + acceleration * dt^2
Where dampFactor = 1.0 - damping. The damping term removes a fraction of the implicit velocity each frame, causing the cloth to settle over time rather than oscillating indefinitely.
Advantages over Euler integration:
- Velocity is implicit (no separate velocity integration step)
- More stable under large time steps
- Constraint corrections automatically affect velocity
- Simpler code with fewer parameters
Velocity derivation:
After integration, velocity is computed as (position - prevPosition) / dt for external systems that need it (audio wind effects, rendering motion blur).
The constraint solver uses Gauss-Seidel relaxation: it iterates over all constraints sequentially, correcting particle positions to satisfy each constraint's rest length.
Correction formula for a single constraint:
dx = particleB.position - particleA.position
distance = length(dx)
correction = (distance - restLength) / distance * stiffness
ratioA = inverseMassA / (inverseMassA + inverseMassB)
ratioB = inverseMassB / (inverseMassA + inverseMassB)
particleA.position += dx * correction * ratioA (if not pinned)
particleB.position -= dx * correction * ratioB (if not pinned)
The inverse mass ratio ensures that lighter particles move more than heavier ones. Pinned particles (inverseMass = 0) do not move at all, and the entire correction is applied to the other particle.
Convergence: With stiffness = 1.0, each constraint is fully satisfied in one iteration, but interacting constraints may conflict. Multiple solver iterations allow the system to converge to a globally consistent state.
| Iterations | Behavior |
|---|---|
| 1 | Very stretchy cloth; constraints barely enforced |
| 2-4 | Soft, elastic cloth; good for capes and curtains |
| 8-16 | Stiff cloth; good for canvas and rigid fabric |
| 32+ | Nearly rigid; diminishing returns on quality |
| Operation | Complexity | Notes |
|---|---|---|
| Force application | O(N) | N = particle count |
| Verlet integration | O(N) | N = particle count |
| Constraint solving | O(C * I) | C = constraint count, I = solver iterations |
| Collision detection | O(N * K) | N = particles, K = colliders |
| Total per frame | O(N * K + C * I) | Dominated by constraints and collisions |
For a W x H cloth grid:
| Constraint Type | Count |
|---|---|
| Structural (horiz) | (W-1) * H |
| Structural (vert) | W * (H-1) |
| Shear (diagonal) | (W-1) * (H-1) |
| Bending (horiz) | (W-2) * H |
| Bending (vert) | W * (H-2) |
| Total | Approximately 5*W*H
|
Example: A 20x20 cloth has 400 particles and roughly 2,000 constraints. With 4 solver iterations, that is 8,000 constraint evaluations per frame.
| Concern | Recommendation |
|---|---|
| Distant cloth appears identical to high-res | Reduce width/height for distant instances (LOD) |
| Too many solver iterations | Use 2-4 iterations for most cloth; reserve 8+ for hero cloth only |
| Many colliders per cloth | Keep collider count under 5; use simplified shapes |
| Self-collision needed | Avoid if possible; it is O(N^2) without spatial hashing |
| Many cloth instances | Disable simulation for off-screen cloth (enabled = false) |
| Large time steps cause instability | Subdivide deltaTime: cloth.Update(deltaTime / 2); cloth.Update(deltaTime / 2);
|
| Component | Size Per Element | Count (20x20 cloth) | Total |
|---|---|---|---|
| ClothParticle | ~64 bytes | 400 | ~25 KB |
| ClothConstraint | ~16 bytes | ~2,000 | ~32 KB |
| ClothCollider | ~40 bytes | ~3 | ~120 bytes |
| Total per instance | ~57 KB |
After Update(), particle positions need to be uploaded to the GPU for rendering. The typical pattern:
// Get particle data
const auto& particles = cloth.GetParticles(clothId);
int w, h;
cloth.GetClothDimensions(clothId, w, h);
// Build vertex buffer
std::vector<Vertex> vertices(particles.size());
for (size_t i = 0; i < particles.size(); ++i)
{
vertices[i].position = particles[i].position;
// Compute UVs from grid position
int x = static_cast<int>(i) % w;
int y = static_cast<int>(i) / w;
vertices[i].uv.x = static_cast<float>(x) / static_cast<float>(w - 1);
vertices[i].uv.y = static_cast<float>(y) / static_cast<float>(h - 1);
}
// Build index buffer (triangle grid)
std::vector<uint32_t> indices;
for (int y = 0; y < h - 1; ++y)
{
for (int x = 0; x < w - 1; ++x)
{
uint32_t tl = static_cast<uint32_t>(y * w + x);
uint32_t tr = tl + 1;
uint32_t bl = tl + static_cast<uint32_t>(w);
uint32_t br = bl + 1;
// Two triangles per quad
indices.insert(indices.end(), {tl, bl, tr, tr, bl, br});
}
}
// Upload to dynamic vertex buffer each frame
UpdateDynamicBuffer(clothVertexBuffer, vertices);To use cloth with the entity component system, attach cloth to entities:
// Create a cloth component
struct ClothComponent
{
uint32_t clothId = 0;
uint32_t meshEntity = 0; // Entity with the MeshComponent to update
};
// In your cloth system update:
void ClothECSSystem::Update(float dt, ClothSimulation& clothSim, entt::registry& registry)
{
clothSim.Update(dt);
auto view = registry.view<ClothComponent, TransformComponent>();
for (auto [entity, clothComp, transform] : view.each())
{
// Update pinned particles to follow the entity's transform
// Upload particles to the associated mesh
}
}cloth_status # Show active cloth instances and particle counts
cloth_wind # Show/set global wind direction
cloth_debug # Toggle cloth debug visualization (wireframe, constraints, colliders)
=== Cloth Simulation ===
Instances: 3
Cloth 1: 100 particles, 490 constraints, 2 colliders
Cloth 2: 400 particles, 1960 constraints, 1 colliders
Cloth 3: 25 particles, 110 constraints, 0 colliders [DISABLED]
| Symptom | Likely Cause | Fix |
|---|---|---|
| Cloth falls through ground | No plane collider added | Add a ClothCollider::Type::Plane at ground level |
| Cloth stretches excessively | Stiffness too low or too few solver iterations | Increase stiffness to 0.95+ or increase solverIterations to 8+ |
| Cloth explodes / particles fly off |
deltaTime too large (> 33ms) or damping too low |
Subdivide time step; increase damping to 0.05+ |
| Cloth does not respond to wind | Wind vector is zero or particles are all pinned | Check SetWind() was called with non-zero vector; verify not all particles are pinned |
GetParticles() returns empty vector |
Invalid cloth ID | Verify CreateCloth() returned a valid handle; check the ID was not destroyed |
| Cloth appears as a flat plane | No pins set; cloth falls instantly under gravity | Pin at least one particle before the first Update() call |
| Cloth tunnels through sphere collider | Particle velocity too high relative to collider radius | Reduce time step, increase damping, or use larger collider radius |
| Performance is poor with many instances | Too many particles or solver iterations | Use LOD (lower resolution for distant cloth); disable off-screen instances |
- Physics -- Rigid body simulation with Jolt Physics
- Animation -- Character capes and cloth attachments
- Rendering and Graphics -- Rendering deformable meshes
- Entity Component System -- Integrating cloth with ECS
Published from 2b03dc797148. 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
- 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
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers