1
+ import taichi as ti
2
+ ti.init(arch=ti.vulkan) # Alternatively, ti.init(arch=ti.cpu)
3
+
4
+ n = 128
5
+ quad_size = 1.0 / n
6
+ dt = 4e-2 / n
7
+ substeps = int(1 / 60 // dt)
8
+
9
+ gravity = ti.Vector([0, -9.8, 0])
10
+ spring_Y = 3e4
11
+ dashpot_damping = 1e4
12
+ drag_damping = 1
13
+
14
+ ball_radius = 0.3
15
+ ball_center = ti.Vector.field(3, dtype=float, shape=(1, ))
16
+ ball_center[0] = [0, 0, 0]
17
+
18
+ x = ti.Vector.field(3, dtype=float, shape=(n, n))
19
+ v = ti.Vector.field(3, dtype=float, shape=(n, n))
20
+
21
+ num_triangles = (n - 1) * (n - 1) * 2
22
+ indices = ti.field(int, shape=num_triangles * 3)
23
+ vertices = ti.Vector.field(3, dtype=float, shape=n * n)
24
+ colors = ti.Vector.field(3, dtype=float, shape=n * n)
25
+
26
+ bending_springs = False
27
+
28
+ @ti.kernel
29
+ def initialize_mass_points():
30
+ random_offset = ti.Vector([ti.random() - 0.5, ti.random() - 0.5]) * 0.1
31
+
32
+ for i, j in x:
33
+ x[i, j] = [
34
+ i * quad_size - 0.5 + random_offset[0], 0.6,
35
+ j * quad_size - 0.5 + random_offset[1]
36
+ ]
37
+ v[i, j] = [0, 0, 0]
38
+
39
+
40
+ @ti.kernel
41
+ def initialize_mesh_indices():
42
+ for i, j in ti.ndrange(n - 1, n - 1):
43
+ quad_id = (i * (n - 1)) + j
44
+ # 1st triangle of the square
45
+ indices[quad_id * 6 + 0] = i * n + j
46
+ indices[quad_id * 6 + 1] = (i + 1) * n + j
47
+ indices[quad_id * 6 + 2] = i * n + (j + 1)
48
+ # 2nd triangle of the square
49
+ indices[quad_id * 6 + 3] = (i + 1) * n + j + 1
50
+ indices[quad_id * 6 + 4] = i * n + (j + 1)
51
+ indices[quad_id * 6 + 5] = (i + 1) * n + j
52
+
53
+ for i, j in ti.ndrange(n, n):
54
+ if (i // 4 + j // 4) % 2 == 0:
55
+ colors[i * n + j] = (0.22, 0.72, 0.52)
56
+ else:
57
+ colors[i * n + j] = (1, 0.334, 0.52)
58
+
59
+ initialize_mesh_indices()
60
+
61
+ spring_offsets = []
62
+ if bending_springs:
63
+ for i in range(-1, 2):
64
+ for j in range(-1, 2):
65
+ if (i, j) != (0, 0):
66
+ spring_offsets.append(ti.Vector([i, j]))
67
+
68
+ else:
69
+ for i in range(-2, 3):
70
+ for j in range(-2, 3):
71
+ if (i, j) != (0, 0) and abs(i) + abs(j) <= 2:
72
+ spring_offsets.append(ti.Vector([i, j]))
73
+
74
+ @ti.kernel
75
+ def substep():
76
+ for i in ti.grouped(x):
77
+ v[i] += gravity * dt
78
+
79
+ for i in ti.grouped(x):
80
+ force = ti.Vector([0.0, 0.0, 0.0])
81
+ for spring_offset in ti.static(spring_offsets):
82
+ j = i + spring_offset
83
+ if 0 <= j[0] < n and 0 <= j[1] < n:
84
+ x_ij = x[i] - x[j]
85
+ v_ij = v[i] - v[j]
86
+ d = x_ij.normalized()
87
+ current_dist = x_ij.norm()
88
+ original_dist = quad_size * float(i - j).norm()
89
+ # Spring force
90
+ force += -spring_Y * d * (current_dist / original_dist - 1)
91
+ # Dashpot damping
92
+ force += -v_ij.dot(d) * d * dashpot_damping * quad_size
93
+
94
+ v[i] += force * dt
95
+
96
+ for i in ti.grouped(x):
97
+ v[i] *= ti.exp(-drag_damping * dt)
98
+ offset_to_center = x[i] - ball_center[0]
99
+ if offset_to_center.norm() <= ball_radius:
100
+ # Velocity projection
101
+ normal = offset_to_center.normalized()
102
+ v[i] -= min(v[i].dot(normal), 0) * normal
103
+ x[i] += dt * v[i]
104
+
105
+ @ti.kernel
106
+ def update_vertices():
107
+ for i, j in ti.ndrange(n, n):
108
+ vertices[i * n + j] = x[i, j]
109
+
110
+ window = ti.ui.Window("Taichi Cloth Simulation on GGUI", (1024, 1024),
111
+ vsync=True)
112
+ canvas = window.get_canvas()
113
+ canvas.set_background_color((1, 1, 1))
114
+ scene = ti.ui.Scene()
115
+ camera = ti.ui.make_camera()
116
+
117
+ current_t = 0.0
118
+ initialize_mass_points()
119
+
120
+ while window.running:
121
+ if current_t > 1.5:
122
+ # Reset
123
+ initialize_mass_points()
124
+ current_t = 0
125
+
126
+ for i in range(substeps):
127
+ substep()
128
+ current_t += dt
129
+ update_vertices()
130
+
131
+ camera.position(0.0, 0.0, 3)
132
+ camera.lookat(0.0, 0.0, 0)
133
+ scene.set_camera(camera)
134
+
135
+ scene.point_light(pos=(0, 1, 2), color=(1, 1, 1))
136
+ scene.ambient_light((0.5, 0.5, 0.5))
137
+ scene.mesh(vertices,
138
+ indices=indices,
139
+ per_vertex_color=colors,
140
+ two_sided=True)
141
+
142
+ # Draw a smaller ball to avoid visual penetration
143
+ scene.particles(ball_center, radius=ball_radius * 0.95, color=(0.5, 0.42, 0.8))
144
+ canvas.scene(scene)
145
+ window.show()
0 commit comments