From 0268c633fae2de6399450e0afcf0fe1bf92be9f7 Mon Sep 17 00:00:00 2001
From: Cassie Jones <code@witchoflight.com>
Date: Sat, 1 Feb 2020 01:33:50 -0500
Subject: [PATCH] Use hand motion to influence particle motion

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               | 39 +++++++++++++++++++++++++++++++++------
 shaders/particles.comp | 15 +++++++++++++--
 2 files changed, 46 insertions(+), 8 deletions(-)

diff --git a/main.lua b/main.lua
index b931f2f..5c56689 100644
--- 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()
diff --git a/shaders/particles.comp b/shaders/particles.comp
index 6e136b4..c16ddd2 100644
--- a/shaders/particles.comp
+++ b/shaders/particles.comp
@@ -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;
 }
-- 
2.47.0