Link

Final Artifact – Cloth Simulator

Due: Monday, December 8th @ 11:00pm

This assignment was originally created by Jack Lowry with the help of Jason Kim for Summer 2025. It was recently updated by Jason Kim with the help of Julia Lundblad and Bowei Chen for Autumn 2025. The bug model asset used in this project was created and designed by Julia Lundblad.

Clone Repository from GitLab

Recommended reading: Xavier Provot, "Deformation Constraints in a Mass–Spring Model to Describe Rigid Cloth Behavior" (1995) .

Table of Contents

  1. Overview
  2. Getting Started
  3. Background
  4. Requirements (5 TODOs)
    1. TODO 1: Spring Force (Hooke's Law)
    2. TODO 2: Overextension Constraint
    3. TODO 3: Fixed Point Detection
    4. TODO 4: Spring Connection Offsets
    5. TODO 5: Physics Loop
  5. Provided Code
  6. Scenes & Expected Behavior
  7. Turn In

Overview

In this final artifact, you will implement a real-time cloth simulation in Unity using a mass–spring system with Verlet integration. You will model a 2D rectangular cloth as a grid of particles connected by springs, and simulate how it deforms under gravity, wind, and collisions with scene geometry.

By the end of the project, you will have:

  • A cloth that hangs, swings, and settles realistically under gravity.
  • Configurable pinning patterns (fixed corners) for multiple test scenes.
  • An overextension constraint to keep springs from stretching too far.
  • Interactive wind control in a sail-like scene.
  • A freefall scene where the cloth falls and drapes over a collider (the CSE 457 bug model).

Getting Started

Clone the repository using the button above or via command line:

git clone https://gitlab.cs.washington.edu/kimjason/cloth-simulator.git

All of your implementation will take place in Assets/Scripts/ClothObject.cs. Every place you need to write code is marked with // TODO comments.

The project is built to work on Unity 6 (v. 6000.0.42f1). Please make sure you are using this version (or later in the same major line) so that the project opens correctly.

Tip: Start with Scene_FourCornersFixed to test basic behavior, then progress to the other scenes. Cloth simulation can be subtle to debug—start early!

Background

Mass–Spring System

Cloth is modeled as a grid of particles connected by springs. Each particle has mass, position, and velocity. Springs connect particles and exert forces based on how much they are stretched or compressed relative to their rest length.

We use three types of springs to capture different cloth behaviors:

  • Structural springs: Connect immediate horizontal and vertical neighbors. These resist stretching.
  • Shear springs: Connect diagonal neighbors. These resist shearing (the cloth turning into a parallelogram).
  • Flexion springs: Connect particles two steps apart. These resist bending.
Diagram showing structural, shear, and flexion spring connections on a cloth grid
The three types of springs in a mass-spring cloth model.

Verlet Integration

Instead of explicit Euler integration, we use Verlet integration, which is more stable for stiff spring systems. Verlet integration computes the new position using the current position, previous position, and acceleration—without explicitly storing velocity:

\[ \mathbf{x}_{n+1} = 2\mathbf{x}_n - \mathbf{x}_{n-1} + \mathbf{a}_n \Delta t^2 \]

Overextension Constraint

Real cloth can stretch, but only to a limited extent. If springs behave like perfectly elastic rubber bands, the cloth will stretch unrealistically and may become numerically unstable.

To address this, we implement an overextension constraint that pulls particles back together when a spring stretches beyond a critical threshold.

Diagram showing how overextension constraint corrects particle positions
Overextension constraint: correcting particle positions when springs stretch too far

Requirements (5 TODOs)

Your implementation consists of 5 TODOs in ClothObject.cs:

TODO Summary

  1. GetForce() – Implement Hooke's Law for spring forces
  2. CalculateDynamicInverseConstraint() – Implement overextension constraint
  3. checkFixedPoint() – Determine which particles are pinned
  4. Spring connection arrays – Define structural, shear, and flexion offsets
  5. ComputeMovement() – Implement the three-phase physics loop

TODO 1: Spring Force (Hooke's Law)

In the SpringForce class, implement GetForce() using Hooke's Law:

\[ \mathbf{F} = k (d - d_\text{rest}) \hat{\mathbf{d}} \]

Where:

  • \(k\) is the spring constant (_k)
  • \(d\) is the current distance between the two particles
  • \(d_\text{rest}\) is the rest length (_restDist)
  • \(\hat{\mathbf{d}}\) is the unit direction vector from _p1 toward _p2

The force should pull _p1 toward _p2 when stretched, and push away when compressed.

TODO 2: Overextension Constraint

In CalculateDynamicInverseConstraint(), implement the constraint that prevents springs from stretching too far beyond their rest length.

Your implementation should:

  1. Check if the spring is overextended (stretched beyond _criticalDisplacementRatio of rest length)
  2. If overextended, calculate how much to move _p1 to bring the spring back within limits:
    • If _p2 is fixed: _p1 moves the full correction
    • If neither is fixed: _p1 moves half (the symmetric spring handles _p2)
  3. Accumulate the correction into _p1.inverseConstraintAccumulator

TODO 3: Fixed Point Detection

Implement checkFixedPoint(int x, int y) to determine if a particle should be pinned (immovable). The function should handle three configurations based on boolean flags:

Flag Fixed Corners
fourCornersFixed (0,0), (0, yParticles-1), (xParticles-1, 0), (xParticles-1, yParticles-1)
threeCornersFixed (0,0), (0, yParticles-1), (xParticles-1, 0)
twoCornersFixed (0,0), (0, yParticles-1)

The cloth grid coordinate system:

  (0,0) -------- (0,9)        ← top edge (x=0)
    |              |
    |              |
    |              |
  (9,0) -------- (9,9)        ← bottom edge (x=xParticles-1)

TODO 4: Spring Connection Offsets

In generateParticles(), define the offset arrays that specify how particles connect to their neighbors. Each offset is {i_offset, j_offset} from the current particle.

Define arrays for all three spring types:

  • Structural: Connect to immediate horizontal and vertical neighbors (4 connections)
  • Shear: Connect to diagonal neighbors (4 connections)
  • Flexion: Connect to particles 2 steps away in each cardinal direction (4 connections)

Example: An offset of {-1, 0} connects to the particle one step "left" in the i direction.

TODO 5: Physics Loop

Implement ComputeMovement(), the main physics update that runs each frame. The simulation happens in three phases, each requiring a nested loop over all particles:

Phase 1: Force Accumulation

For each non-fixed particle, sum all forces acting on it:

  • Spring forces (via p.GetForces())
  • Global forces like gravity and drag (loop through _globalForces)
  • Wind forces if enabled (via CalculateParticleNormal and GetWindAtPoint)

Add the total to p.forceAccumulator.

Phase 2: Verlet Integration + Collisions + Constraints

For each non-fixed particle:

  1. Compute acceleration from accumulated forces (F = ma)
  2. Clear the force accumulator for next frame
  3. Apply Verlet integration: newPos = 2*current - last + accel*dt²
    Important: Save lastPosition BEFORE updating Position!
  4. Update velocity for next frame's drag calculation
  5. Handle collisions with HandleCollisions(p)
  6. Calculate constraints with p.CalculateDynamicInverseConstraint()

Phase 3: Apply Constraints + Update Mesh

For each non-fixed particle:

  1. Apply constraint corrections with p.ApplyDynamicInverseConstraint()
  2. Copy particle position to mesh vertices (both front and back faces)

After the particle loop, transform all vertices to local space using _transform.InverseTransformPoint().

Provided Code

The following functionality is already implemented for you:

HandleCollisions(Particle p)

Handles collision response between a particle and scene colliders. When a particle penetrates a collider, it is pushed out to the surface and its velocity is adjusted based on restitution (bounciness) and friction.

CalculateParticleNormal(int i, int j)

Computes an approximate surface normal at a particle position by averaging cross products of edges from adjacent triangles. Used for wind calculations—wind only affects surfaces facing into it.

GetWindAtPoint(Vector3 position, Vector3 normal)

Calculates the wind force at a particle based on position and surface normal. The wind model includes Perlin noise-based turbulence for natural-looking gusts, and a dot product term so wind only pushes on surfaces facing into it (like a sail).

HandleWindControls()

Processes keyboard input to control wind in real-time:

  • WASD / Arrow keys: Change wind direction
  • Q / E: Decrease / Increase wind strength
  • Space: Toggle wind on/off

Spring Creation Loops

The loops that iterate through your connection offset arrays and create SpringForce objects are provided. You only need to define the offset arrays themselves.

Scenes & Expected Behavior

The Unity project includes several scenes that test different aspects of your simulator:

Scene – FourCornersFixed

All four corners pinned. The cloth should sag naturally under gravity and settle into a stable hammock shape. Start here to test your basic implementation.

Scene – ThreeCornersFixed

Three corners pinned (bottom-right free). The cloth should hang asymmetrically with the free corner drooping down.

Scene – TwoCornersFixed

Only the top edge pinned. The cloth should hang like a curtain, swinging freely.

Scene – Sail

Wind interaction scene. Use the keyboard controls to change wind direction and strength. The cloth should billow and respond dynamically to wind changes. Note, you may need to greatly incr

Scene – Freefall

The cloth starts above the CSE 457 bug model and falls under gravity. With collisions enabled, it should drape over the bug model without tunneling through.

NOTE: All the parameters in the scene have been defined for you. You can experiment with these values by selecting the Cloth GameObject in the Hierarchy and modifying the values in the Inspector panel. It should be under "Cloth Object (Script)".

Turn In

Files to Submit

  1. Unity Script: ClothObject.cs
  2. Video Demo: A recording named ClothSimulationDemo showing all five scenes:
    • FourCornersFixed
    • ThreeCornersFixed
    • TwoCornersFixed
    • Sail (demonstrate wind controls)
    • Freefall (cloth draping over bug model)

Submission Format

  • Create a folder named with your UW NetID (e.g., zoran)
  • Place your ClothObject.cs and video inside this folder
  • Zip the folder and upload to Canvas under Final Artifact – Cloth Simulator

Congratulations on making it to the end of CSE 457! We hope this project gave you a sense of how physics, numerical methods, and graphics come together to create compelling visual effects.