Use hand motion to influence particle motion develop
authorCassie Jones <code@witchoflight.com>
Sat, 1 Feb 2020 06:33:50 +0000 (01:33 -0500)
committerCassie Jones <code@witchoflight.com>
Sat, 1 Feb 2020 06:33:50 +0000 (01:33 -0500)
We upload the hand motion (position and velocity) and use it to move
particles. The area of influence is based on the velocity of the hands:
faster moving hands affect a wider area, slower affect a smaller area.
This is intended to simulate the wake of the hand in a fluid, and has
the bonus effect that when your hands aren't moving they don't affect
many particles.

The particles are accelerated by mixing their velocity with the hand's
velocity, with the weight for that depending on the distance and the
velocity of the hand. Using mixing instead of direct accelerating
prevents you from accelerating particles faster than your hands move,
which simulates the feeling of them being pulled along in a current in
the fluid.

main.lua
shaders/particles.comp

index b931f2f434f2b67d966ff1a944f1ddbf34f42ad9..5c566898dd8ff661f99f80158545a037b19b5e4f 100644 (file)
--- a/main.lua
+++ b/main.lua
@@ -1,14 +1,18 @@
+local vec3 = lovr.math.vec3
 local mat4 = lovr.math.mat4
+local quat = lovr.math.quat
 
 local nParticles = 2 ^ 14
 local batchSize = 128
 local batches = nParticles / batchSize
+local dt = 0.01
 
 local features
 local shaders = {}
 local blocks = {}
 local meshes = {}
 local compute = {}
+local hands = {}
 
 function lovr.load()
     features = lovr.graphics.getFeatures()
@@ -30,27 +34,50 @@ function lovr.load()
 
     compute.particles = lovr.graphics.newComputeShader("shaders/particles.comp")
     compute.particles:sendBlock('particleData', blocks.particles)
-    compute.particles:send("dt", 0.01)
+    compute.particles:send("dt", dt)
 
-    local xr, yr, zr = range(-1, 1), range(1, 3), range(-2, 0)
+    local xr, yr, zr = range(-1, 1), range(1, 3), range(-1, -0.5)
     local sr, vr = range(0.005, 0.01), range(-0.05, 0.05)
     local initial = initialParticleData(nParticles, xr, yr, zr, sr, vr)
     blocks.particles:send("particles", initial)
+
+    for _, hand in ipairs(lovr.headset.getHands()) do
+        local x, y, z = lovr.headset.getPose(hand)
+        hands[hand] = {
+            pos = lovr.math.newVec3(x, y, z),
+            vel = lovr.math.newVec3(0, 0, 0),
+            rot = lovr.math.newQuat(),
+        }
+    end
 end
 
 function lovr.draw()
     lovr.graphics.clear()
     local x, y, z = lovr.headset.getPose("head")
+    shaders.particles:send("headPos", vec3(x, y, z))
+    local positions = {}
+    local velocities = {}
+    for _, hand in ipairs(lovr.headset.getHands()) do
+        local x, y, z, a, ax, ay, az = lovr.headset.getPose(hand)
+        local pos = vec3(x, y, z)
+        hands[hand].vel:set((pos - hands[hand].pos) / dt)
+        hands[hand].pos:set(pos)
+        hands[hand].rot:set(a, ax, ay, az)
+        table.insert(positions, pos)
+        table.insert(velocities, hands[hand].vel)
+    end
+    compute.particles:send("handPos", positions)
+    compute.particles:send("handVel", velocities)
+
     lovr.graphics.compute(compute.particles, batches)
     -- Read the buffer to force SSBO synch of some sort.
     blocks.particles:read("reloadFlag")
-    shaders.particles:send("headPos", {x, y, z})
 
     lovr.graphics.setColor(0.6, 0.1, 0)
     lovr.graphics.setShader(nil)
-    for _, hand in ipairs(lovr.headset.getHands()) do
-        local x, y, z, a, ax, ay, az = lovr.headset.getPose(hand)
-        lovr.graphics.cube("fill", x, y, z, 0.1, a, ax, ay, az)
+    for _, hand in pairs(hands) do
+        -- local x, y, z = hand.pos:unpack()
+        lovr.graphics.cube("fill", hand.pos, 0.1, hand.rot)
     end
 
     withDepthTest("lequal", false, function()
index 6e136b42e84034162d3b508aae708d5e125b4339..c16ddd2ec900f7bac090686cefc570daf7217154 100644 (file)
@@ -1,4 +1,6 @@
 uniform float dt;
+uniform vec3 handPos[2];
+uniform vec3 handVel[2];
 
 struct particle_t {
   vec4 positionAndSize;
@@ -11,10 +13,19 @@ layout(std430) buffer particleData {
   particle_t particles[];
 };
 
+vec3 wrap(vec3 v) { return mod(v, 2.0) - 1.0 + vec3(0.0, 1.0, 0.0); }
+
 layout(local_size_x = 128) in;
 void compute() {
   uint id = gl_GlobalInvocationID.x;
   vec3 vel = particles[id].velocity.xyz;
-  particles[id].positionAndSize.xyz += vel * dt;
-  particles[id].velocity.xyz -= vel * length(vel) * dt;
+  vec3 pos = particles[id].positionAndSize.xyz;
+  for (int i = 0; i < 2; i++) {
+    float speed = clamp(length(handVel[i]), 0.0, 3.0);
+    float dist = distance(handPos[i], pos);
+    float factor = 1.0 - smoothstep(0.0, speed * 0.1, dist);
+    vel = mix(vel, handVel[i], factor * 0.01);
+  }
+  particles[id].positionAndSize.xyz = wrap(pos + vel * dt);
+  particles[id].velocity.xyz = vel - vel * length(vel) * dt;
 }