qbhf2's picture
added NvidiaWarp and GarmentCode repos
66c9c8a
# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved.
# NVIDIA CORPORATION and its licensors retain all intellectual property
# and proprietary rights in and to this software, related documentation
# and any modifications thereto. Any use, reproduction, disclosure or
# distribution of this software and related documentation without an express
# license agreement from NVIDIA CORPORATION is strictly prohibited.
###########################################################################
# Example Wave
#
# Shows how to implement a simple 2D wave-equation solver with collision
# against a moving sphere.
#
###########################################################################
import math
import os
import warp as wp
import warp.render
wp.init()
@wp.func
def sample(f: wp.array(dtype=float), x: int, y: int, width: int, height: int):
# clamp texture coords
x = wp.clamp(x, 0, width - 1)
y = wp.clamp(y, 0, height - 1)
s = f[y * width + x]
return s
@wp.func
def laplacian(f: wp.array(dtype=float), x: int, y: int, width: int, height: int):
ddx = sample(f, x + 1, y, width, height) - 2.0 * sample(f, x, y, width, height) + sample(f, x - 1, y, width, height)
ddy = sample(f, x, y + 1, width, height) - 2.0 * sample(f, x, y, width, height) + sample(f, x, y - 1, width, height)
return ddx + ddy
@wp.kernel
def wave_displace(
hcurrent: wp.array(dtype=float),
hprevious: wp.array(dtype=float),
width: int,
height: int,
center_x: float,
center_y: float,
r: float,
mag: float,
t: float,
):
tid = wp.tid()
x = tid % width
y = tid // width
dx = float(x) - center_x
dy = float(y) - center_y
dist_sq = float(dx * dx + dy * dy)
if dist_sq < r * r:
h = mag * wp.sin(t)
hcurrent[tid] = h
hprevious[tid] = h
@wp.kernel
def wave_solve(
hprevious: wp.array(dtype=float),
hcurrent: wp.array(dtype=float),
width: int,
height: int,
inv_cell: float,
k_speed: float,
k_damp: float,
dt: float,
):
tid = wp.tid()
x = tid % width
y = tid // width
l = laplacian(hcurrent, x, y, width, height) * inv_cell * inv_cell
# integrate
h1 = hcurrent[tid]
h0 = hprevious[tid]
h = 2.0 * h1 - h0 + dt * dt * (k_speed * l - k_damp * (h1 - h0))
# buffers get swapped each iteration
hprevious[tid] = h
# simple kernel to apply height deltas to a vertex array
@wp.kernel
def grid_update(heights: wp.array(dtype=float), vertices: wp.array(dtype=wp.vec3)):
tid = wp.tid()
h = heights[tid]
v = vertices[tid]
v_new = wp.vec3(v[0], h, v[2])
vertices[tid] = v_new
class Example:
def __init__(self, stage, resx, resy):
self.sim_width = resx
self.sim_height = resy
self.sim_fps = 60.0
self.sim_substeps = 16
self.sim_duration = 5.0
self.sim_frames = int(self.sim_duration * self.sim_fps)
self.sim_dt = (1.0 / self.sim_fps) / self.sim_substeps
self.sim_time = 0.0
# wave constants
self.k_speed = 1.0
self.k_damp = 0.0
# grid constants
self.grid_size = 0.1
self.grid_displace = 0.5
self.renderer = wp.render.UsdRenderer(stage)
vertices = []
self.indices = []
def grid_index(x, y, stride):
return y * stride + x
for z in range(self.sim_height):
for x in range(self.sim_width):
pos = (
float(x) * self.grid_size,
0.0,
float(z) * self.grid_size,
) # - Gf.Vec3f(float(self.sim_width)/2*self.grid_size, 0.0, float(self.sim_height)/2*self.grid_size)
# directly modifies verts_host memory since this is a numpy alias of the same buffer
vertices.append(pos)
if x > 0 and z > 0:
self.indices.append(grid_index(x - 1, z - 1, self.sim_width))
self.indices.append(grid_index(x, z, self.sim_width))
self.indices.append(grid_index(x, z - 1, self.sim_width))
self.indices.append(grid_index(x - 1, z - 1, self.sim_width))
self.indices.append(grid_index(x - 1, z, self.sim_width))
self.indices.append(grid_index(x, z, self.sim_width))
# simulation grids
self.sim_grid0 = wp.zeros(self.sim_width * self.sim_height, dtype=float)
self.sim_grid1 = wp.zeros(self.sim_width * self.sim_height, dtype=float)
self.sim_verts = wp.array(vertices, dtype=wp.vec3)
# create surface displacement around a point
self.cx = self.sim_width / 2 + math.sin(self.sim_time) * self.sim_width / 3
self.cy = self.sim_height / 2 + math.cos(self.sim_time) * self.sim_height / 3
def update(self):
with wp.ScopedTimer("simulate", active=True):
for s in range(self.sim_substeps):
# create surface displacement around a point
self.cx = self.sim_width / 2 + math.sin(self.sim_time) * self.sim_width / 3
self.cy = self.sim_height / 2 + math.cos(self.sim_time) * self.sim_height / 3
wp.launch(
kernel=wave_displace,
dim=self.sim_width * self.sim_height,
inputs=[
self.sim_grid0,
self.sim_grid1,
self.sim_width,
self.sim_height,
self.cx,
self.cy,
10.0,
self.grid_displace,
-math.pi * 0.5,
],
)
# integrate wave equation
wp.launch(
kernel=wave_solve,
dim=self.sim_width * self.sim_height,
inputs=[
self.sim_grid0,
self.sim_grid1,
self.sim_width,
self.sim_height,
1.0 / self.grid_size,
self.k_speed,
self.k_damp,
self.sim_dt,
],
)
# swap grids
(self.sim_grid0, self.sim_grid1) = (self.sim_grid1, self.sim_grid0)
self.sim_time += self.sim_dt
with wp.ScopedTimer("mesh", active=False):
# update grid vertices from heights
wp.launch(kernel=grid_update, dim=self.sim_width * self.sim_height, inputs=[self.sim_grid0, self.sim_verts])
def render(self, is_live=False):
with wp.ScopedTimer("render", active=True):
time = 0.0 if is_live else self.sim_time
vertices = self.sim_verts.numpy()
self.renderer.begin_frame(time)
self.renderer.render_mesh("surface", vertices, self.indices)
self.renderer.render_sphere(
"sphere",
(self.cx * self.grid_size, 0.0, self.cy * self.grid_size),
(0.0, 0.0, 0.0, 1.0),
10.0 * self.grid_size,
)
self.renderer.end_frame()
if __name__ == "__main__":
stage_path = os.path.join(os.path.dirname(__file__), "outputs/example_wave.usd")
example = Example(stage_path, resx=128, resy=128)
for i in range(example.sim_frames):
example.update()
example.render()
example.renderer.save()