qbhf2's picture
added NvidiaWarp and GarmentCode repos
66c9c8a
import warp as wp
import igl
import scipy
PI = wp.constant(3.14159265359)
PI_2 = wp.constant(1.57079632679)
@wp.func
def velocity_at_point(qd: wp.spatial_vector, r: wp.vec3):
"""
Returns the velocity of a point relative to the frame with the given spatial velocity.
"""
return wp.cross(wp.spatial_top(qd), r) + wp.spatial_bottom(qd)
@wp.func
def quat_twist(axis: wp.vec3, q: wp.quat):
"""
Returns the twist around an axis.
"""
# project imaginary part onto axis
a = wp.vec3(q[0], q[1], q[2])
proj = wp.dot(a, axis)
a = proj * axis
# if proj < 0.0:
# # ensure twist points in same direction as axis
# a = -a
return wp.normalize(wp.quat(a[0], a[1], a[2], q[3]))
@wp.func
def quat_twist_angle(axis: wp.vec3, q: wp.quat):
"""
Returns the angle of the twist around an axis.
"""
return 2.0 * wp.acos(quat_twist(axis, q)[3])
@wp.func
def quat_decompose(q: wp.quat):
"""
Decompose a quaternion into a sequence of 3 rotations around x,y',z' respectively, i.e.: q = q_z''q_y'q_x.
"""
R = wp.mat33(
wp.quat_rotate(q, wp.vec3(1.0, 0.0, 0.0)),
wp.quat_rotate(q, wp.vec3(0.0, 1.0, 0.0)),
wp.quat_rotate(q, wp.vec3(0.0, 0.0, 1.0)),
)
# https://www.sedris.org/wg8home/Documents/WG80485.pdf
phi = wp.atan2(R[1, 2], R[2, 2])
sinp = -R[0, 2]
if wp.abs(sinp) >= 1.0:
theta = 1.57079632679 * wp.sign(sinp)
else:
theta = wp.asin(-R[0, 2])
psi = wp.atan2(R[0, 1], R[0, 0])
return -wp.vec3(phi, theta, psi)
@wp.func
def quat_to_rpy(q: wp.quat):
"""
Convert a quaternion into euler angles (roll, pitch, yaw)
roll is rotation around x in radians (counterclockwise)
pitch is rotation around y in radians (counterclockwise)
yaw is rotation around z in radians (counterclockwise)
"""
x = q[0]
y = q[1]
z = q[2]
w = q[3]
t0 = 2.0 * (w * x + y * z)
t1 = 1.0 - 2.0 * (x * x + y * y)
roll_x = wp.atan2(t0, t1)
t2 = 2.0 * (w * y - z * x)
t2 = wp.clamp(t2, -1.0, 1.0)
pitch_y = wp.asin(t2)
t3 = 2.0 * (w * z + x * y)
t4 = 1.0 - 2.0 * (y * y + z * z)
yaw_z = wp.atan2(t3, t4)
return wp.vec3(roll_x, pitch_y, yaw_z)
@wp.func
def quat_to_euler(q: wp.quat, i: int, j: int, k: int) -> wp.vec3:
"""
Convert a quaternion into euler angles
i, j, k are the indices in [1,2,3] of the axes to use
(i != j, j != k)
"""
# https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0276302
not_proper = True
if i == k:
not_proper = False
k = 6 - i - j # because i + j + k = 1 + 2 + 3 = 6
e = float((i - j) * (j - k) * (k - i)) / 2.0 # Levi-Civita symbol
a = q[0]
b = q[i]
c = q[j]
d = q[k] * e
if not_proper:
a -= q[j]
b += q[k] * e
c += q[0]
d -= q[i]
t2 = wp.acos(2.0 * (a * a + b * b) / (a * a + b * b + c * c + d * d) - 1.0)
tp = wp.atan2(b, a)
tm = wp.atan2(d, c)
t1 = 0.0
t3 = 0.0
if wp.abs(t2) < 1e-6:
t3 = 2.0 * tp - t1
elif wp.abs(t2 - PI_2) < 1e-6:
t3 = 2.0 * tm + t1
else:
t1 = tp - tm
t3 = tp + tm
if not_proper:
t2 -= PI_2
t3 *= e
return wp.vec3(t1, t2, t3)
@wp.func
def transform_twist(t: wp.transform, x: wp.spatial_vector):
# Frank & Park definition 3.20, pg 100
q = wp.transform_get_rotation(t)
p = wp.transform_get_translation(t)
w = wp.spatial_top(x)
v = wp.spatial_bottom(x)
w = wp.quat_rotate(q, w)
v = wp.quat_rotate(q, v) + wp.cross(p, w)
return wp.spatial_vector(w, v)
@wp.func
def transform_wrench(t: wp.transform, x: wp.spatial_vector):
q = wp.transform_get_rotation(t)
p = wp.transform_get_translation(t)
w = wp.spatial_top(x)
v = wp.spatial_bottom(x)
v = wp.quat_rotate(q, v)
w = wp.quat_rotate(q, w) + wp.cross(p, v)
return wp.spatial_vector(w, v)
@wp.func
def transform_inertia(t: wp.transform, I: wp.spatial_matrix):
"""
Computes adj_t^-T*I*adj_t^-1 (tensor change of coordinates).
(Frank & Park, section 8.2.3, pg 290)
"""
t_inv = wp.transform_inverse(t)
q = wp.transform_get_rotation(t_inv)
p = wp.transform_get_translation(t_inv)
r1 = wp.quat_rotate(q, wp.vec3(1.0, 0.0, 0.0))
r2 = wp.quat_rotate(q, wp.vec3(0.0, 1.0, 0.0))
r3 = wp.quat_rotate(q, wp.vec3(0.0, 0.0, 1.0))
R = wp.mat33(r1, r2, r3)
S = wp.mul(wp.skew(p), R)
T = wp.spatial_adjoint(R, S)
return wp.mul(wp.mul(wp.transpose(T), I), T)
@wp.func
def vec_min(a: wp.vec3, b: wp.vec3):
return wp.vec3(wp.min(a[0], b[0]), wp.min(a[1], b[1]), wp.min(a[2], b[2]))
@wp.func
def vec_max(a: wp.vec3, b: wp.vec3):
return wp.vec3(wp.max(a[0], b[0]), wp.max(a[1], b[1]), wp.max(a[2], b[2]))
@wp.func
def vec_abs(a: wp.vec3):
return wp.vec3(wp.abs(a[0]), wp.abs(a[1]), wp.abs(a[2]))
@wp.kernel
def compute_vertex_normal_unnormalized(
shape: wp.uint64,
vertex_normals: wp.array(dtype=wp.vec3)
):
"""
dim: num_faces
Args:
shape (wp.uint64): the mesh to compute vertex normals for
vertex_normals (wp.array, optional): output
"""
tid = wp.tid()
mesh = wp.mesh_get(shape)
indices = mesh.indices
vertices = mesh.points
f1 = indices[tid * 3 + 0]
f2 = indices[tid * 3 + 1]
f3 = indices[tid * 3 + 2]
x1 = vertices[f1]
x2 = vertices[f2]
x3 = vertices[f3]
n = wp.cross(x2 - x1, x3 - x1)
wp.atomic_add(vertex_normals, f1, n)
wp.atomic_add(vertex_normals, f2, n)
wp.atomic_add(vertex_normals, f3, n)
@wp.kernel
def normalize_vector_array(
vectors: wp.array(dtype=wp.vec3)
):
tid = wp.tid()
vectors[tid] = wp.normalize(vectors[tid])
def compute_vertex_normal(
shape: wp.Mesh,
vertex_normals: wp.array(dtype=wp.vec3)
):
num_faces = len(shape.indices) // 3
vertex_normals.zero_()
wp.launch(
kernel=compute_vertex_normal_unnormalized,
dim=num_faces,
inputs=[shape.id],
outputs=[vertex_normals],
)
wp.launch(
kernel=normalize_vector_array,
dim=len(vertex_normals),
inputs=[],
outputs=[vertex_normals],
)
def implicit_laplacian_smoothing(V, F, step_size, iters):
cot = igl.cotmatrix(V, F)
new_V_list = [V.copy()]
for i in range(iters):
area = igl.massmatrix(new_V_list[i], F, igl.MASSMATRIX_TYPE_BARYCENTRIC)
new_V_list.append(scipy.sparse.linalg.spsolve((area - step_size * cot), area @ new_V_list[i]))
return new_V_list
@wp.func
def mesh_query_inside_(id: wp.uint64, p: wp.vec3):
t = float(0.0)
u = float(0.0)
v = float(0.0)
sign = float(0.0)
n = wp.vec3()
face = int(0)
vote = int(0)
FLT_MAX = float(3.402823466e+38)
for i in range(3):
if i == 0: dir = wp.vec3(1.0, 0.0, 0.0)
if i == 1: dir = wp.vec3(0.0, 1.0, 0.0)
if i == 2: dir = wp.vec3(0.0, 0.0, 1.0)
if wp.mesh_query_ray(id, p, dir, FLT_MAX, t, u, v, sign, n, face) and sign < 0:
vote += 1
if (vote == 3):
return -1.0
else:
return 1.0