Skip to content

Commit f463ddc

Browse files
zzigakliminchen
authored andcommitted
Fixed XPBD base and costraint solvers to work for multiple solve steps
1 parent d9c084b commit f463ddc

9 files changed

Lines changed: 88 additions & 25 deletions

File tree

12_pbd_cloth/constraints/bending.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
def solve_bending_constraints(
66
compliance: ti.f64, dt: ti.f64, num_bending_constraints: ti.i32,
77
pos: ti.template(), bending_ids: ti.template(), bending_lengths: ti.template(),
8-
inv_mass: ti.template()
8+
inv_mass: ti.template(), lambdas: ti.template()
99
):
1010
alpha = compliance / (dt * dt)
1111
for i in range(num_bending_constraints):
@@ -19,6 +19,7 @@ def solve_bending_constraints(
1919
if dist == 0.0: continue
2020
grad = delta / dist
2121
C = dist - bending_lengths[i]
22-
s = -C / (w_sum + alpha)
23-
pos[id0] += s * w0 * grad
24-
pos[id1] -= s * w1 * grad
22+
dlambda = -(C + alpha * lambdas[i]) / (w_sum + alpha)
23+
lambdas[i] += dlambda
24+
pos[id0] += dlambda * w0 * grad
25+
pos[id1] -= dlambda * w1 * grad

12_pbd_cloth/constraints/stretching.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
def solve_stretching_constraints(
66
compliance: ti.f64, dt: ti.f64, num_stretching_constraints: ti.i32,
77
pos: ti.template(), stretching_ids: ti.template(), stretching_lengths: ti.template(),
8-
inv_mass: ti.template()
8+
inv_mass: ti.template(), lambdas: ti.template()
99
):
1010
alpha = compliance / (dt * dt)
1111
for i in range(num_stretching_constraints):
@@ -19,6 +19,7 @@ def solve_stretching_constraints(
1919
if dist == 0.0: continue
2020
grad = delta / dist
2121
C = dist - stretching_lengths[i]
22-
s = -C / (w_sum + alpha)
23-
pos[id0] += s * w0 * grad
24-
pos[id1] -= s * w1 * grad
22+
dlambda = -(C + alpha * lambdas[i]) / (w_sum + alpha)
23+
lambdas[i] += dlambda
24+
pos[id0] += dlambda * w0 * grad
25+
pos[id1] -= dlambda * w1 * grad

12_pbd_cloth/imgui.ini

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[Window][Debug##Default]
2+
Pos=60,60
3+
Size=400,400
4+
Collapsed=0
5+
6+
[Window][Controls]
7+
Pos=51,51
8+
Size=307,409
9+
Collapsed=0
10+

12_pbd_cloth/main.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def save(self):
7878
dt = 1.0 / 60.0
7979
num_substeps = 15
8080
sdt = dt / num_substeps
81-
solver_iterations = 1
81+
solver_iterations = 1 # can increase
8282

8383
export_enabled = False
8484
export_frame_count = 0
@@ -112,6 +112,10 @@ def save(self):
112112
stretching_lengths = ti.field(dtype=ti.f64, shape=num_stretching_constraints)
113113
bending_lengths = ti.field(dtype=ti.f64, shape=num_bending_constraints)
114114

115+
# XPBD accumulated lambdas (one per constraint, reset each substep)
116+
stretching_lambdas = ti.field(dtype=ti.f64, shape=num_stretching_constraints)
117+
bending_lambdas = ti.field(dtype=ti.f64, shape=num_bending_constraints)
118+
115119
# Visualization fields
116120
ground_vertices = ti.Vector.field(3, dtype=ti.f64, shape=4)
117121
ground_indices = ti.field(ti.i32, shape=6)
@@ -133,12 +137,28 @@ def save(self):
133137
# Simulation Substep Function
134138
# ============================================================================
135139

140+
@ti.kernel
141+
def reset_constraint_lambdas(
142+
num_s: ti.i32, num_b: ti.i32,
143+
s_lambdas: ti.template(), b_lambdas: ti.template()
144+
):
145+
for i in range(num_s):
146+
s_lambdas[i] = 0.0
147+
for i in range(num_b):
148+
b_lambdas[i] = 0.0
149+
150+
136151
def substep(grab_id, grab_x, grab_y, grab_z):
137152
"""Perform one simulation substep."""
138153
pre_solve(sdt, num_particles, gravity, pos, prev_pos, vel, inv_mass)
154+
# XPBD: reset lambdas once per substep (they accumulate across solver iterations)
155+
reset_constraint_lambdas(
156+
num_stretching_constraints, num_bending_constraints,
157+
stretching_lambdas, bending_lambdas
158+
)
139159
for _ in range(solver_iterations):
140-
solve_stretching_constraints(stretching_compliance, sdt, num_stretching_constraints, pos, stretching_ids, stretching_lengths, inv_mass)
141-
solve_bending_constraints(bending_compliance, sdt, num_bending_constraints, pos, bending_ids, bending_lengths, inv_mass)
160+
solve_stretching_constraints(stretching_compliance, sdt, num_stretching_constraints, pos, stretching_ids, stretching_lengths, inv_mass, stretching_lambdas)
161+
solve_bending_constraints(bending_compliance, sdt, num_bending_constraints, pos, bending_ids, bending_lengths, inv_mass, bending_lambdas)
142162
apply_grab(grab_id, grab_x, grab_y, grab_z, pos, vel, grab_indicator_pos)
143163
post_solve(sdt, num_particles, pos, prev_pos, vel, inv_mass)
144164

12_pbd_cloth/utils/cloth_utils.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ def initialize_colors(
2323
):
2424

2525
for i in range(num_particles):
26-
x_coord = int((pos[i].x + 1.0) * 5) % 2
27-
z_coord = int((pos[i].z + 1.0) * 5) % 2
26+
x_coord = int((pos[i].x + 1.0) * 8) % 2
27+
y_coord = int((pos[i].y + 1.0) * 8) % 2
2828

29-
if (x_coord + z_coord) % 2 == 0:
30-
vertex_colors[i] = ti.Vector([1.0, 1.0, 1.0])
29+
if (x_coord + y_coord) % 2 == 0:
30+
vertex_colors[i] = ti.Vector([0.15, 0.35, 0.75]) # deep blue
3131
else:
32-
vertex_colors[i] = ti.Vector([0.1, 0.1, 0.1])
32+
vertex_colors[i] = ti.Vector([0.95, 0.92, 0.85]) # light cream
3333

3434

3535
@ti.kernel

13_pbd_mesh/constraints/edge.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
@ti.kernel
55
def solve_edges(
66
compliance: ti.f64, dt: ti.f64, num_edges: ti.i32,
7-
pos: ti.template(), edge_ids: ti.template(), edge_lengths: ti.template(), inv_mass: ti.template()
7+
pos: ti.template(), edge_ids: ti.template(), edge_lengths: ti.template(),
8+
inv_mass: ti.template(), lambdas: ti.template()
89
):
910
alpha = compliance / (dt * dt)
1011
for i in range(num_edges):
@@ -17,6 +18,7 @@ def solve_edges(
1718
if dist == 0.0: continue
1819
grad = delta / dist
1920
C = dist - edge_lengths[i]
20-
s = -C / (w_sum + alpha)
21-
pos[id0] += s * w0 * grad
22-
pos[id1] -= s * w1 * grad
21+
dlambda = -(C + alpha * lambdas[i]) / (w_sum + alpha)
22+
lambdas[i] += dlambda
23+
pos[id0] += dlambda * w0 * grad
24+
pos[id1] -= dlambda * w1 * grad

13_pbd_mesh/constraints/volume.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
def solve_volumes(
1313
compliance: ti.f64, dt: ti.f64, num_tets: ti.i32,
1414
pos: ti.template(), tet_ids: ti.template(), rest_vol: ti.template(), inv_mass: ti.template(),
15-
vol_id_order: ti.template()
15+
vol_id_order: ti.template(), lambdas: ti.template()
1616
):
1717
alpha = compliance / (dt * dt)
1818
for i in range(num_tets):
@@ -27,6 +27,7 @@ def solve_volumes(
2727
w_sum += inv_mass[p_indices[j]] * grad.norm_sqr()
2828
if w_sum == 0.0: continue
2929
C = get_tet_volume(p_indices, pos) - rest_vol[i]
30-
s = -C / (w_sum + alpha)
30+
dlambda = -(C + alpha * lambdas[i]) / (w_sum + alpha)
31+
lambdas[i] += dlambda
3132
for j in ti.static(range(4)):
32-
pos[p_indices[j]] += s * inv_mass[p_indices[j]] * grads[j, :]
33+
pos[p_indices[j]] += dlambda * inv_mass[p_indices[j]] * grads[j, :]

13_pbd_mesh/imgui.ini

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[Window][Debug##Default]
2+
Pos=60,60
3+
Size=400,400
4+
Collapsed=0
5+
6+
[Window][Controls]
7+
Pos=51,51
8+
Size=307,256
9+
Collapsed=0
10+

13_pbd_mesh/main.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ def save(self):
154154
tet_ids = ti.field(ti.i32, shape=(num_tets, 4))
155155
edge_ids = ti.field(ti.i32, shape=(num_edges, 2))
156156

157+
# XPBD accumulated lambdas (one per constraint, reset each substep)
158+
edge_lambdas = ti.field(dtype=ti.f64, shape=num_edges)
159+
vol_lambdas = ti.field(dtype=ti.f64, shape=num_tets)
160+
157161
# Visual mesh fields
158162
vis_mesh_rest_pos = ti.Vector.field(3, dtype=ti.f64, shape=num_vis_verts)
159163
vis_mesh_pos = ti.Vector.field(3, dtype=ti.f64, shape=num_vis_verts)
@@ -194,11 +198,24 @@ def save(self):
194198
# Simulation Substep Function
195199
# ============================================================================
196200

201+
@ti.kernel
202+
def reset_constraint_lambdas(
203+
num_e: ti.i32, num_v: ti.i32,
204+
e_lambdas: ti.template(), v_lambdas: ti.template()
205+
):
206+
for i in range(num_e):
207+
e_lambdas[i] = 0.0
208+
for i in range(num_v):
209+
v_lambdas[i] = 0.0
210+
211+
197212
def substep():
198213
pre_solve(sdt, 1, num_particles, gravity, box_min, box_max, pos, prev_pos, vel, inv_mass)
214+
# XPBD: reset lambdas once per substep (they accumulate across solver iterations)
215+
reset_constraint_lambdas(num_edges, num_tets, edge_lambdas, vol_lambdas)
199216
for _ in range(solver_iterations):
200-
solve_edges(edge_compliance, sdt, num_edges, pos, edge_ids, edge_lengths, inv_mass)
201-
solve_volumes(vol_compliance, sdt, num_tets, pos, tet_ids, rest_vol, inv_mass, vol_id_order)
217+
solve_edges(edge_compliance, sdt, num_edges, pos, edge_ids, edge_lengths, inv_mass, edge_lambdas)
218+
solve_volumes(vol_compliance, sdt, num_tets, pos, tet_ids, rest_vol, inv_mass, vol_id_order, vol_lambdas)
202219
post_solve(sdt, num_particles, pos, prev_pos, vel, inv_mass)
203220

204221

@@ -284,6 +301,7 @@ def substep():
284301

285302
scene.mesh(vis_mesh_pos, indices=vis_mesh_indices, per_vertex_color=vis_mesh_colors)
286303
scene.mesh(ground_vertices, indices=ground_indices, per_vertex_color=ground_colors)
304+
canvas.set_background_color((1.0, 1.0, 1.0)) # white
287305

288306
canvas.scene(scene)
289307
window.show()

0 commit comments

Comments
 (0)