warp-lang 0.15.0__py3-none-manylinux2014_x86_64.whl → 1.0.0__py3-none-manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of warp-lang might be problematic. Click here for more details.
- warp/__init__.py +1 -0
- warp/codegen.py +7 -3
- warp/config.py +2 -1
- warp/constants.py +3 -0
- warp/context.py +44 -21
- warp/examples/assets/bunny.usd +0 -0
- warp/examples/assets/cartpole.urdf +110 -0
- warp/examples/assets/crazyflie.usd +0 -0
- warp/examples/assets/cube.usda +42 -0
- warp/examples/assets/nv_ant.xml +92 -0
- warp/examples/assets/nv_humanoid.xml +183 -0
- warp/examples/assets/quadruped.urdf +268 -0
- warp/examples/assets/rocks.nvdb +0 -0
- warp/examples/assets/rocks.usd +0 -0
- warp/examples/assets/sphere.usda +56 -0
- warp/examples/assets/torus.usda +105 -0
- warp/examples/core/example_dem.py +6 -6
- warp/examples/core/example_fluid.py +3 -3
- warp/examples/core/example_graph_capture.py +3 -6
- warp/examples/optim/example_bounce.py +9 -8
- warp/examples/optim/example_cloth_throw.py +12 -8
- warp/examples/optim/example_diffray.py +10 -12
- warp/examples/optim/example_drone.py +31 -14
- warp/examples/optim/example_spring_cage.py +10 -15
- warp/examples/optim/example_trajectory.py +7 -24
- warp/examples/sim/example_cartpole.py +3 -9
- warp/examples/sim/example_cloth.py +10 -10
- warp/examples/sim/example_granular.py +3 -3
- warp/examples/sim/example_granular_collision_sdf.py +9 -4
- warp/examples/sim/example_jacobian_ik.py +0 -10
- warp/examples/sim/example_particle_chain.py +4 -4
- warp/examples/sim/example_quadruped.py +15 -11
- warp/examples/sim/example_rigid_chain.py +13 -8
- warp/examples/sim/example_rigid_contact.py +4 -4
- warp/examples/sim/example_rigid_force.py +7 -7
- warp/examples/sim/example_rigid_soft_contact.py +4 -4
- warp/examples/sim/example_soft_body.py +3 -3
- warp/jax.py +45 -0
- warp/jax_experimental.py +339 -0
- warp/render/render_opengl.py +188 -95
- warp/render/render_usd.py +34 -10
- warp/sim/__init__.py +13 -4
- warp/sim/articulation.py +4 -5
- warp/sim/collide.py +320 -175
- warp/sim/import_mjcf.py +25 -30
- warp/sim/import_urdf.py +94 -63
- warp/sim/import_usd.py +51 -36
- warp/sim/inertia.py +3 -2
- warp/sim/integrator.py +233 -0
- warp/sim/integrator_euler.py +447 -469
- warp/sim/integrator_featherstone.py +1991 -0
- warp/sim/integrator_xpbd.py +1420 -640
- warp/sim/model.py +741 -487
- warp/sim/particles.py +2 -1
- warp/sim/render.py +18 -2
- warp/sim/utils.py +222 -11
- warp/stubs.py +1 -0
- warp/tape.py +6 -9
- warp/tests/test_examples.py +87 -20
- warp/tests/test_grad_customs.py +122 -0
- warp/tests/test_jax.py +254 -0
- warp/tests/test_options.py +13 -53
- warp/tests/test_quat.py +23 -0
- warp/tests/test_snippet.py +2 -0
- warp/tests/test_utils.py +31 -26
- warp/tests/test_verify_fp.py +65 -0
- warp/tests/unittest_suites.py +4 -0
- warp/utils.py +50 -1
- {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/METADATA +1 -1
- {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/RECORD +73 -64
- warp/examples/env/__init__.py +0 -0
- warp/examples/env/env_ant.py +0 -61
- warp/examples/env/env_cartpole.py +0 -63
- warp/examples/env/env_humanoid.py +0 -65
- warp/examples/env/env_usd.py +0 -97
- warp/examples/env/environment.py +0 -526
- warp/sim/optimizer.py +0 -138
- {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/LICENSE.md +0 -0
- {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/WHEEL +0 -0
- {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/top_level.txt +0 -0
warp/sim/particles.py
CHANGED
|
@@ -11,6 +11,7 @@ from .model import PARTICLE_FLAG_ACTIVE
|
|
|
11
11
|
|
|
12
12
|
@wp.func
|
|
13
13
|
def particle_force(n: wp.vec3, v: wp.vec3, c: float, k_n: float, k_d: float, k_f: float, k_mu: float):
|
|
14
|
+
# compute normal and tangential friction force for a single contact
|
|
14
15
|
vn = wp.dot(n, v)
|
|
15
16
|
jn = c * k_n
|
|
16
17
|
jd = min(vn, 0.0) * k_d
|
|
@@ -89,7 +90,7 @@ def eval_particle_forces_kernel(
|
|
|
89
90
|
|
|
90
91
|
|
|
91
92
|
def eval_particle_forces(model, state, forces):
|
|
92
|
-
if model.particle_max_radius > 0.0:
|
|
93
|
+
if model.particle_count > 1 and model.particle_max_radius > 0.0:
|
|
93
94
|
wp.launch(
|
|
94
95
|
kernel=eval_particle_forces_kernel,
|
|
95
96
|
dim=model.particle_count,
|
warp/sim/render.py
CHANGED
|
@@ -23,6 +23,7 @@ NAN = wp.constant(-1.0e8)
|
|
|
23
23
|
def compute_contact_points(
|
|
24
24
|
body_q: wp.array(dtype=wp.transform),
|
|
25
25
|
shape_body: wp.array(dtype=int),
|
|
26
|
+
contact_count: wp.array(dtype=int),
|
|
26
27
|
contact_shape0: wp.array(dtype=int),
|
|
27
28
|
contact_shape1: wp.array(dtype=int),
|
|
28
29
|
contact_point0: wp.array(dtype=wp.vec3),
|
|
@@ -32,6 +33,11 @@ def compute_contact_points(
|
|
|
32
33
|
contact_pos1: wp.array(dtype=wp.vec3),
|
|
33
34
|
):
|
|
34
35
|
tid = wp.tid()
|
|
36
|
+
count = contact_count[0]
|
|
37
|
+
if tid >= count:
|
|
38
|
+
contact_pos0[tid] = wp.vec3(NAN, NAN, NAN)
|
|
39
|
+
contact_pos1[tid] = wp.vec3(NAN, NAN, NAN)
|
|
40
|
+
return
|
|
35
41
|
shape_a = contact_shape0[tid]
|
|
36
42
|
shape_b = contact_shape1[tid]
|
|
37
43
|
if shape_a == shape_b:
|
|
@@ -124,6 +130,7 @@ def CreateSimRenderer(renderer):
|
|
|
124
130
|
shape_geo_thickness = model.shape_geo.thickness.numpy()
|
|
125
131
|
shape_geo_is_solid = model.shape_geo.is_solid.numpy()
|
|
126
132
|
shape_transform = model.shape_transform.numpy()
|
|
133
|
+
shape_visible = model.shape_visible.numpy()
|
|
127
134
|
|
|
128
135
|
p = np.zeros(3, dtype=np.float32)
|
|
129
136
|
q = np.array([0.0, 0.0, 0.0, 1.0], dtype=np.float32)
|
|
@@ -211,7 +218,9 @@ def CreateSimRenderer(renderer):
|
|
|
211
218
|
|
|
212
219
|
self.geo_shape[geo_hash] = shape
|
|
213
220
|
|
|
214
|
-
|
|
221
|
+
if shape_visible[s]:
|
|
222
|
+
# TODO support dynamic visibility
|
|
223
|
+
self.add_shape_instance(name, shape, body, X_bs.p, X_bs.q, scale, custom_index=s, visible=shape_visible[s])
|
|
215
224
|
self.instance_count += 1
|
|
216
225
|
|
|
217
226
|
if self.show_joints and model.joint_count:
|
|
@@ -275,7 +284,7 @@ def CreateSimRenderer(renderer):
|
|
|
275
284
|
self.instance_count += 1
|
|
276
285
|
|
|
277
286
|
if model.ground:
|
|
278
|
-
self.render_ground()
|
|
287
|
+
self.render_ground(plane=model.ground_plane_params)
|
|
279
288
|
|
|
280
289
|
if hasattr(self, "complete_setup"):
|
|
281
290
|
self.complete_setup()
|
|
@@ -284,6 +293,12 @@ def CreateSimRenderer(renderer):
|
|
|
284
293
|
return tab10_color_map(self.instance_count)
|
|
285
294
|
|
|
286
295
|
def render(self, state: warp.sim.State):
|
|
296
|
+
"""
|
|
297
|
+
Updates the renderer with the given simulation state.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
state (warp.sim.State): The simulation state to render.
|
|
301
|
+
"""
|
|
287
302
|
if self.skip_rendering:
|
|
288
303
|
return
|
|
289
304
|
|
|
@@ -354,6 +369,7 @@ def CreateSimRenderer(renderer):
|
|
|
354
369
|
inputs=[
|
|
355
370
|
state.body_q,
|
|
356
371
|
self.model.shape_body,
|
|
372
|
+
self.model.rigid_contact_count,
|
|
357
373
|
self.model.rigid_contact_shape0,
|
|
358
374
|
self.model.rigid_contact_shape1,
|
|
359
375
|
self.model.rigid_contact_point0,
|
warp/sim/utils.py
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import warp as wp
|
|
2
|
+
import numpy as np
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
PI_2 = wp.constant(1.57079632679)
|
|
4
|
+
from typing import Tuple, List
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
@wp.func
|
|
8
8
|
def velocity_at_point(qd: wp.spatial_vector, r: wp.vec3):
|
|
9
9
|
"""
|
|
10
10
|
Returns the velocity of a point relative to the frame with the given spatial velocity.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
qd (spatial_vector): The spatial velocity of the frame.
|
|
14
|
+
r (vec3): The position of the point relative to the frame.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
vec3: The velocity of the point.
|
|
11
18
|
"""
|
|
12
19
|
return wp.cross(wp.spatial_top(qd), r) + wp.spatial_bottom(qd)
|
|
13
20
|
|
|
@@ -52,7 +59,7 @@ def quat_decompose(q: wp.quat):
|
|
|
52
59
|
phi = wp.atan2(R[1, 2], R[2, 2])
|
|
53
60
|
sinp = -R[0, 2]
|
|
54
61
|
if wp.abs(sinp) >= 1.0:
|
|
55
|
-
theta =
|
|
62
|
+
theta = wp.HALF_PI * wp.sign(sinp)
|
|
56
63
|
else:
|
|
57
64
|
theta = wp.asin(-R[0, 2])
|
|
58
65
|
psi = wp.atan2(R[0, 1], R[0, 0])
|
|
@@ -63,7 +70,7 @@ def quat_decompose(q: wp.quat):
|
|
|
63
70
|
@wp.func
|
|
64
71
|
def quat_to_rpy(q: wp.quat):
|
|
65
72
|
"""
|
|
66
|
-
Convert a quaternion into
|
|
73
|
+
Convert a quaternion into Euler angles (roll, pitch, yaw)
|
|
67
74
|
roll is rotation around x in radians (counterclockwise)
|
|
68
75
|
pitch is rotation around y in radians (counterclockwise)
|
|
69
76
|
yaw is rotation around z in radians (counterclockwise)
|
|
@@ -90,11 +97,27 @@ def quat_to_rpy(q: wp.quat):
|
|
|
90
97
|
@wp.func
|
|
91
98
|
def quat_to_euler(q: wp.quat, i: int, j: int, k: int) -> wp.vec3:
|
|
92
99
|
"""
|
|
93
|
-
Convert a quaternion into
|
|
94
|
-
|
|
95
|
-
|
|
100
|
+
Convert a quaternion into Euler angles.
|
|
101
|
+
|
|
102
|
+
:math:`i, j, k` are the indices in :math:`[0, 1, 2]` of the axes to use
|
|
103
|
+
(:math:`i \\neq j, j \\neq k`).
|
|
104
|
+
|
|
105
|
+
Reference: https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0276302
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
q (quat): The quaternion to convert
|
|
109
|
+
i (int): The index of the first axis
|
|
110
|
+
j (int): The index of the second axis
|
|
111
|
+
k (int): The index of the third axis
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
vec3: The Euler angles (in radians)
|
|
96
115
|
"""
|
|
97
|
-
#
|
|
116
|
+
# i, j, k are actually assumed to follow 1-based indexing but
|
|
117
|
+
# we want to be compatible with quat_from_euler
|
|
118
|
+
i += 1
|
|
119
|
+
j += 1
|
|
120
|
+
k += 1
|
|
98
121
|
not_proper = True
|
|
99
122
|
if i == k:
|
|
100
123
|
not_proper = False
|
|
@@ -116,17 +139,54 @@ def quat_to_euler(q: wp.quat, i: int, j: int, k: int) -> wp.vec3:
|
|
|
116
139
|
t3 = 0.0
|
|
117
140
|
if wp.abs(t2) < 1e-6:
|
|
118
141
|
t3 = 2.0 * tp - t1
|
|
119
|
-
elif wp.abs(t2 -
|
|
142
|
+
elif wp.abs(t2 - wp.HALF_PI) < 1e-6:
|
|
120
143
|
t3 = 2.0 * tm + t1
|
|
121
144
|
else:
|
|
122
145
|
t1 = tp - tm
|
|
123
146
|
t3 = tp + tm
|
|
124
147
|
if not_proper:
|
|
125
|
-
t2 -=
|
|
148
|
+
t2 -= wp.HALF_PI
|
|
126
149
|
t3 *= e
|
|
127
150
|
return wp.vec3(t1, t2, t3)
|
|
128
151
|
|
|
129
152
|
|
|
153
|
+
@wp.func
|
|
154
|
+
def quat_from_euler(e: wp.vec3, i: int, j: int, k: int) -> wp.quat:
|
|
155
|
+
"""
|
|
156
|
+
Convert Euler angles to a quaternion.
|
|
157
|
+
|
|
158
|
+
:math:`i, j, k` are the indices in :math:`[0, 1, 2]` of the axes in which the Euler angles are provided
|
|
159
|
+
(:math:`i \\neq j, j \\neq k`), e.g. (0, 1, 2) for Euler sequence XYZ.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
e (vec3): The Euler angles (in radians)
|
|
163
|
+
i (int): The index of the first axis
|
|
164
|
+
j (int): The index of the second axis
|
|
165
|
+
k (int): The index of the third axis
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
quat: The quaternion
|
|
169
|
+
"""
|
|
170
|
+
# Half angles
|
|
171
|
+
half_e = e / 2.0
|
|
172
|
+
|
|
173
|
+
# Precompute sines and cosines of half angles
|
|
174
|
+
cr = wp.cos(half_e[i])
|
|
175
|
+
sr = wp.sin(half_e[i])
|
|
176
|
+
cp = wp.cos(half_e[j])
|
|
177
|
+
sp = wp.sin(half_e[j])
|
|
178
|
+
cy = wp.cos(half_e[k])
|
|
179
|
+
sy = wp.sin(half_e[k])
|
|
180
|
+
|
|
181
|
+
# Components of the quaternion based on the rotation sequence
|
|
182
|
+
return wp.quat(
|
|
183
|
+
(cy * sr * cp - sy * cr * sp),
|
|
184
|
+
(cy * cr * sp + sy * sr * cp),
|
|
185
|
+
(sy * cr * cp - cy * sr * sp),
|
|
186
|
+
(cy * cr * cp + sy * sr * sp),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
130
190
|
@wp.func
|
|
131
191
|
def transform_twist(t: wp.transform, x: wp.spatial_vector):
|
|
132
192
|
# Frank & Park definition 3.20, pg 100
|
|
@@ -181,6 +241,35 @@ def transform_inertia(t: wp.transform, I: wp.spatial_matrix):
|
|
|
181
241
|
return wp.mul(wp.mul(wp.transpose(T), I), T)
|
|
182
242
|
|
|
183
243
|
|
|
244
|
+
@wp.func
|
|
245
|
+
def boltzmann(a: float, b: float, alpha: float):
|
|
246
|
+
e1 = wp.exp(alpha * a)
|
|
247
|
+
e2 = wp.exp(alpha * b)
|
|
248
|
+
return (a * e1 + b * e2) / (e1 + e2)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@wp.func
|
|
252
|
+
def smooth_max(a: float, b: float, eps: float):
|
|
253
|
+
d = a - b
|
|
254
|
+
return 0.5 * (a + b + wp.sqrt(d * d + eps))
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@wp.func
|
|
258
|
+
def smooth_min(a: float, b: float, eps: float):
|
|
259
|
+
d = a - b
|
|
260
|
+
return 0.5 * (a + b - wp.sqrt(d * d + eps))
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@wp.func
|
|
264
|
+
def leaky_max(a: float, b: float):
|
|
265
|
+
return smooth_max(a, b, 1e-5)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@wp.func
|
|
269
|
+
def leaky_min(a: float, b: float):
|
|
270
|
+
return smooth_min(a, b, 1e-5)
|
|
271
|
+
|
|
272
|
+
|
|
184
273
|
@wp.func
|
|
185
274
|
def vec_min(a: wp.vec3, b: wp.vec3):
|
|
186
275
|
return wp.vec3(wp.min(a[0], b[0]), wp.min(a[1], b[1]), wp.min(a[2], b[2]))
|
|
@@ -191,9 +280,131 @@ def vec_max(a: wp.vec3, b: wp.vec3):
|
|
|
191
280
|
return wp.vec3(wp.max(a[0], b[0]), wp.max(a[1], b[1]), wp.max(a[2], b[2]))
|
|
192
281
|
|
|
193
282
|
|
|
283
|
+
@wp.func
|
|
284
|
+
def vec_leaky_min(a: wp.vec3, b: wp.vec3):
|
|
285
|
+
return wp.vec3(leaky_min(a[0], b[0]), leaky_min(a[1], b[1]), leaky_min(a[2], b[2]))
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
@wp.func
|
|
289
|
+
def vec_leaky_max(a: wp.vec3, b: wp.vec3):
|
|
290
|
+
return wp.vec3(leaky_max(a[0], b[0]), leaky_max(a[1], b[1]), leaky_max(a[2], b[2]))
|
|
291
|
+
|
|
292
|
+
|
|
194
293
|
@wp.func
|
|
195
294
|
def vec_abs(a: wp.vec3):
|
|
196
295
|
return wp.vec3(wp.abs(a[0]), wp.abs(a[1]), wp.abs(a[2]))
|
|
197
296
|
|
|
198
297
|
|
|
199
|
-
|
|
298
|
+
def load_mesh(filename: str, method: str = None):
|
|
299
|
+
"""
|
|
300
|
+
Loads a 3D triangular surface mesh from a file.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
filename (str): The path to the 3D model file (obj, and other formats supported by the different methods) to load.
|
|
304
|
+
method (str): The method to use for loading the mesh (default None). Can be either `"trimesh"`, `"meshio"`, `"pcu"`, or `"openmesh"`. If None, every method is tried and the first successful mesh import where the number of vertices is greater than 0 is returned.
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
Tuple of (mesh_points, mesh_indices), where mesh_points is a Nx3 numpy array of vertex positions (float32),
|
|
308
|
+
and mesh_indices is a Mx3 numpy array of vertex indices (int32) for the triangular faces.
|
|
309
|
+
"""
|
|
310
|
+
import os
|
|
311
|
+
|
|
312
|
+
if not os.path.exists(filename):
|
|
313
|
+
raise ValueError(f"File not found: {filename}")
|
|
314
|
+
|
|
315
|
+
def load_mesh_with_method(method):
|
|
316
|
+
if method == "meshio":
|
|
317
|
+
import meshio
|
|
318
|
+
|
|
319
|
+
m = meshio.read(filename)
|
|
320
|
+
mesh_points = np.array(m.points)
|
|
321
|
+
mesh_indices = np.array(m.cells[0].data, dtype=np.int32)
|
|
322
|
+
elif method == "openmesh":
|
|
323
|
+
import openmesh
|
|
324
|
+
|
|
325
|
+
m = openmesh.read_trimesh(filename)
|
|
326
|
+
mesh_points = np.array(m.points())
|
|
327
|
+
mesh_indices = np.array(m.face_vertex_indices(), dtype=np.int32)
|
|
328
|
+
elif method == "pcu":
|
|
329
|
+
import point_cloud_utils as pcu
|
|
330
|
+
|
|
331
|
+
mesh_points, mesh_indices = pcu.load_mesh_vf(filename)
|
|
332
|
+
mesh_indices = mesh_indices.flatten()
|
|
333
|
+
else:
|
|
334
|
+
import trimesh
|
|
335
|
+
|
|
336
|
+
m = trimesh.load(filename)
|
|
337
|
+
if hasattr(m, "geometry"):
|
|
338
|
+
# multiple meshes are contained in a scene; combine to one mesh
|
|
339
|
+
mesh_points = []
|
|
340
|
+
mesh_indices = []
|
|
341
|
+
index_offset = 0
|
|
342
|
+
for geom in m.geometry.values():
|
|
343
|
+
vertices = np.array(geom.vertices, dtype=np.float32)
|
|
344
|
+
faces = np.array(geom.faces.flatten(), dtype=np.int32)
|
|
345
|
+
mesh_points.append(vertices)
|
|
346
|
+
mesh_indices.append(faces + index_offset)
|
|
347
|
+
index_offset += len(vertices)
|
|
348
|
+
mesh_points = np.concatenate(mesh_points, axis=0)
|
|
349
|
+
mesh_indices = np.concatenate(mesh_indices)
|
|
350
|
+
else:
|
|
351
|
+
# a single mesh
|
|
352
|
+
mesh_points = np.array(m.vertices, dtype=np.float32)
|
|
353
|
+
mesh_indices = np.array(m.faces.flatten(), dtype=np.int32)
|
|
354
|
+
return mesh_points, mesh_indices
|
|
355
|
+
|
|
356
|
+
if method is None:
|
|
357
|
+
methods = ["trimesh", "meshio", "pcu", "openmesh"]
|
|
358
|
+
for method in methods:
|
|
359
|
+
try:
|
|
360
|
+
mesh = load_mesh_with_method(method)
|
|
361
|
+
if mesh is not None and len(mesh[0]) > 0:
|
|
362
|
+
return mesh
|
|
363
|
+
except Exception:
|
|
364
|
+
pass
|
|
365
|
+
raise ValueError(f"Failed to load mesh using any of the methods: {methods}")
|
|
366
|
+
else:
|
|
367
|
+
mesh = load_mesh_with_method(method)
|
|
368
|
+
if mesh is None or len(mesh[0]) == 0:
|
|
369
|
+
raise ValueError(f"Failed to load mesh using method {method}")
|
|
370
|
+
return mesh
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def visualize_meshes(
|
|
374
|
+
meshes: List[Tuple[list, list]], num_cols=0, num_rows=0, titles=[], scale_axes=True, show_plot=True
|
|
375
|
+
):
|
|
376
|
+
# render meshes in a grid with matplotlib
|
|
377
|
+
import matplotlib.pyplot as plt
|
|
378
|
+
from mpl_toolkits.mplot3d import Axes3D
|
|
379
|
+
|
|
380
|
+
num_cols = min(num_cols, len(meshes))
|
|
381
|
+
num_rows = min(num_rows, len(meshes))
|
|
382
|
+
if num_cols and not num_rows:
|
|
383
|
+
num_rows = int(np.ceil(len(meshes) / num_cols))
|
|
384
|
+
elif num_rows and not num_cols:
|
|
385
|
+
num_cols = int(np.ceil(len(meshes) / num_rows))
|
|
386
|
+
else:
|
|
387
|
+
num_cols = len(meshes)
|
|
388
|
+
num_rows = 1
|
|
389
|
+
|
|
390
|
+
vertices = [np.array(v).reshape((-1, 3)) for v, _ in meshes]
|
|
391
|
+
faces = [np.array(f, dtype=np.int32).reshape((-1, 3)) for _, f in meshes]
|
|
392
|
+
if scale_axes:
|
|
393
|
+
ranges = np.array([v.max(axis=0) - v.min(axis=0) for v in vertices])
|
|
394
|
+
max_range = ranges.max()
|
|
395
|
+
mid_points = np.array([v.max(axis=0) + v.min(axis=0) for v in vertices]) * 0.5
|
|
396
|
+
|
|
397
|
+
fig = plt.figure(figsize=(12, 6))
|
|
398
|
+
for i, (vertices, faces) in enumerate(meshes):
|
|
399
|
+
ax = fig.add_subplot(num_rows, num_cols, i + 1, projection="3d")
|
|
400
|
+
if i < len(titles):
|
|
401
|
+
ax.set_title(titles[i])
|
|
402
|
+
ax.plot_trisurf(vertices[:, 0], vertices[:, 1], vertices[:, 2], triangles=faces, edgecolor="k")
|
|
403
|
+
if scale_axes:
|
|
404
|
+
mid = mid_points[i]
|
|
405
|
+
ax.set_xlim(mid[0] - max_range, mid[0] + max_range)
|
|
406
|
+
ax.set_ylim(mid[1] - max_range, mid[1] + max_range)
|
|
407
|
+
ax.set_zlim(mid[2] - max_range, mid[2] + max_range)
|
|
408
|
+
if show_plot:
|
|
409
|
+
plt.show()
|
|
410
|
+
return fig
|
warp/stubs.py
CHANGED
|
@@ -84,6 +84,7 @@ from warp.context import is_peer_access_supported, is_peer_access_enabled, set_p
|
|
|
84
84
|
from warp.tape import Tape
|
|
85
85
|
from warp.utils import ScopedTimer, ScopedDevice, ScopedStream
|
|
86
86
|
from warp.utils import ScopedMempool, ScopedMempoolAccess, ScopedPeerAccess
|
|
87
|
+
from warp.utils import ScopedCapture
|
|
87
88
|
from warp.utils import transform_expand, quat_between_vectors
|
|
88
89
|
|
|
89
90
|
from warp.torch import from_torch, to_torch
|
warp/tape.py
CHANGED
|
@@ -95,16 +95,17 @@ class Tape:
|
|
|
95
95
|
# existing code before we added wp.array.grad attribute
|
|
96
96
|
if grads:
|
|
97
97
|
for a, g in grads.items():
|
|
98
|
-
a.grad
|
|
98
|
+
if a.grad is None:
|
|
99
|
+
a.grad = g
|
|
100
|
+
else:
|
|
101
|
+
# ensure we can capture this backward pass in a CUDA graph
|
|
102
|
+
a.grad.assign(g)
|
|
99
103
|
self.const_gradients.add(a)
|
|
100
104
|
|
|
101
105
|
# run launches backwards
|
|
102
106
|
for launch in reversed(self.launches):
|
|
103
107
|
if callable(launch):
|
|
104
|
-
|
|
105
|
-
if module_enable_backward is False:
|
|
106
|
-
msg = f"Running the tape backwards may produce incorrect gradients because recorded kernel {launch.__name__} is defined in a module with the option 'enable_backward=False' set."
|
|
107
|
-
wp.utils.warn(msg)
|
|
108
|
+
launch()
|
|
108
109
|
|
|
109
110
|
else:
|
|
110
111
|
# kernel option takes precedence over module option
|
|
@@ -118,10 +119,6 @@ class Tape:
|
|
|
118
119
|
msg = f"Running the tape backwards may produce incorrect gradients because recorded kernel {launch[0].key} is defined in a module with the option 'enable_backward=False' set."
|
|
119
120
|
wp.utils.warn(msg)
|
|
120
121
|
|
|
121
|
-
if callable(launch):
|
|
122
|
-
launch()
|
|
123
|
-
|
|
124
|
-
else:
|
|
125
122
|
kernel = launch[0]
|
|
126
123
|
dim = launch[1]
|
|
127
124
|
max_blocks = launch[2]
|
warp/tests/test_examples.py
CHANGED
|
@@ -128,29 +128,42 @@ class TestSimExamples(unittest.TestCase):
|
|
|
128
128
|
pass
|
|
129
129
|
|
|
130
130
|
|
|
131
|
+
warp_sim_modules = [
|
|
132
|
+
"warp.sim.integrator",
|
|
133
|
+
"warp.sim.integrator_euler",
|
|
134
|
+
"warp.sim.particles",
|
|
135
|
+
"warp.sim.collide",
|
|
136
|
+
"warp.sim.articulation",
|
|
137
|
+
"warp.sim.integrator_xpbd",
|
|
138
|
+
"warp.sim.integrator_featherstone",
|
|
139
|
+
"warp.sim.integrator_euler",
|
|
140
|
+
"warp.sim.integrator",
|
|
141
|
+
"warp.sim.utils",
|
|
142
|
+
]
|
|
143
|
+
|
|
131
144
|
add_example_test(
|
|
132
145
|
TestSimExamples,
|
|
133
146
|
name="optim.example_bounce",
|
|
134
147
|
devices=cuda_test_devices,
|
|
135
|
-
options={"load_modules":
|
|
148
|
+
options={"load_modules": warp_sim_modules},
|
|
136
149
|
)
|
|
137
150
|
add_example_test(
|
|
138
151
|
TestSimExamples,
|
|
139
152
|
name="sim.example_cartpole",
|
|
140
153
|
devices=cuda_test_devices,
|
|
141
|
-
options={"load_modules":
|
|
154
|
+
options={"load_modules": warp_sim_modules},
|
|
142
155
|
)
|
|
143
156
|
add_example_test(
|
|
144
157
|
TestSimExamples,
|
|
145
158
|
name="sim.example_cloth",
|
|
146
159
|
devices=cuda_test_devices,
|
|
147
|
-
options={"load_modules":
|
|
160
|
+
options={"load_modules": warp_sim_modules},
|
|
148
161
|
)
|
|
149
162
|
add_example_test(
|
|
150
163
|
TestSimExamples,
|
|
151
164
|
name="optim.example_cloth_throw",
|
|
152
165
|
devices=cuda_test_devices,
|
|
153
|
-
options={"load_modules":
|
|
166
|
+
options={"load_modules": warp_sim_modules},
|
|
154
167
|
)
|
|
155
168
|
add_example_test(TestSimExamples, name="optim.example_diffray", devices=cuda_test_devices)
|
|
156
169
|
add_example_test(
|
|
@@ -158,51 +171,105 @@ add_example_test(
|
|
|
158
171
|
name="optim.example_drone",
|
|
159
172
|
devices=cuda_test_devices,
|
|
160
173
|
options={
|
|
161
|
-
"load_modules":
|
|
174
|
+
"load_modules": warp_sim_modules + ["warp.optim.sgd"],
|
|
162
175
|
"num_frames": 1,
|
|
163
176
|
"drone_path": os.path.join(os.path.dirname(__file__), "..", "examples", "assets", "crazyflie.usd"),
|
|
164
177
|
},
|
|
165
178
|
)
|
|
166
|
-
add_example_test(
|
|
167
|
-
|
|
168
|
-
|
|
179
|
+
add_example_test(
|
|
180
|
+
TestSimExamples,
|
|
181
|
+
name="sim.example_granular",
|
|
182
|
+
devices=cuda_test_devices,
|
|
183
|
+
options={
|
|
184
|
+
"load_modules": warp_sim_modules,
|
|
185
|
+
},
|
|
186
|
+
)
|
|
187
|
+
add_example_test(
|
|
188
|
+
TestSimExamples,
|
|
189
|
+
name="sim.example_granular_collision_sdf",
|
|
190
|
+
devices=cuda_test_devices,
|
|
191
|
+
options={
|
|
192
|
+
"load_modules": warp_sim_modules,
|
|
193
|
+
},
|
|
194
|
+
)
|
|
195
|
+
add_example_test(
|
|
196
|
+
TestSimExamples,
|
|
197
|
+
name="optim.example_inverse_kinematics",
|
|
198
|
+
devices=cuda_test_devices,
|
|
199
|
+
options={
|
|
200
|
+
"load_modules": warp_sim_modules,
|
|
201
|
+
},
|
|
202
|
+
)
|
|
169
203
|
add_example_test(
|
|
170
204
|
TestSimExamples,
|
|
171
205
|
name="optim.example_inverse_kinematics_torch",
|
|
172
206
|
devices=cuda_test_devices,
|
|
173
|
-
options={
|
|
207
|
+
options={
|
|
208
|
+
"torch_cuda_required": True,
|
|
209
|
+
"load_modules": warp_sim_modules,
|
|
210
|
+
},
|
|
174
211
|
)
|
|
175
212
|
add_example_test(TestExamples, name="sim.example_jacobian_ik", devices=cuda_test_devices)
|
|
176
213
|
add_example_test(
|
|
177
214
|
TestSimExamples,
|
|
178
215
|
name="sim.example_particle_chain",
|
|
179
216
|
devices=cuda_test_devices,
|
|
180
|
-
options={"load_modules":
|
|
217
|
+
options={"load_modules": warp_sim_modules},
|
|
181
218
|
)
|
|
182
219
|
add_example_test(
|
|
183
220
|
TestSimExamples,
|
|
184
221
|
name="sim.example_quadruped",
|
|
185
222
|
devices=cuda_test_devices,
|
|
186
|
-
options={"load_modules":
|
|
223
|
+
options={"load_modules": warp_sim_modules},
|
|
187
224
|
)
|
|
188
225
|
add_example_test(
|
|
189
226
|
TestSimExamples,
|
|
190
227
|
name="sim.example_rigid_chain",
|
|
191
228
|
devices=cuda_test_devices,
|
|
192
|
-
options={"load_modules":
|
|
229
|
+
options={"load_modules": warp_sim_modules},
|
|
193
230
|
)
|
|
194
231
|
add_example_test(
|
|
195
232
|
TestSimExamples,
|
|
196
233
|
name="sim.example_rigid_contact",
|
|
197
234
|
devices=cuda_test_devices,
|
|
198
|
-
options={"load_modules":
|
|
199
|
-
)
|
|
200
|
-
add_example_test(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
235
|
+
options={"load_modules": warp_sim_modules},
|
|
236
|
+
)
|
|
237
|
+
add_example_test(
|
|
238
|
+
TestSimExamples,
|
|
239
|
+
name="sim.example_rigid_soft_contact",
|
|
240
|
+
devices=cuda_test_devices,
|
|
241
|
+
options={"load_modules": warp_sim_modules},
|
|
242
|
+
)
|
|
243
|
+
add_example_test(
|
|
244
|
+
TestSimExamples,
|
|
245
|
+
name="sim.example_rigid_force",
|
|
246
|
+
devices=cuda_test_devices,
|
|
247
|
+
options={"load_modules": warp_sim_modules},
|
|
248
|
+
)
|
|
249
|
+
add_example_test(
|
|
250
|
+
TestSimExamples,
|
|
251
|
+
name="sim.example_rigid_gyroscopic",
|
|
252
|
+
devices=cuda_test_devices,
|
|
253
|
+
options={"load_modules": warp_sim_modules},
|
|
254
|
+
)
|
|
255
|
+
add_example_test(
|
|
256
|
+
TestSimExamples,
|
|
257
|
+
name="sim.example_soft_body",
|
|
258
|
+
devices=cuda_test_devices,
|
|
259
|
+
options={"load_modules": warp_sim_modules},
|
|
260
|
+
)
|
|
261
|
+
add_example_test(
|
|
262
|
+
TestSimExamples,
|
|
263
|
+
name="optim.example_spring_cage",
|
|
264
|
+
devices=cuda_test_devices,
|
|
265
|
+
options={"load_modules": warp_sim_modules},
|
|
266
|
+
)
|
|
267
|
+
add_example_test(
|
|
268
|
+
TestSimExamples,
|
|
269
|
+
name="optim.example_trajectory",
|
|
270
|
+
devices=cuda_test_devices,
|
|
271
|
+
options={"load_modules": warp_sim_modules},
|
|
272
|
+
)
|
|
206
273
|
|
|
207
274
|
|
|
208
275
|
class TestFemExamples(unittest.TestCase):
|