| /* |
| The MIT License (MIT) |
| |
| Copyright (c) 2022 Google LLC |
| Copyright (c) 2022 Sascha Willems |
| |
| Permission is hereby granted, free of charge, to any person obtaining a copy |
| of this software and associated documentation files (the "Software"), to deal |
| in the Software without restriction, including without limitation the rights |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| copies of the Software, and to permit persons to whom the Software is |
| furnished to do so, subject to the following conditions: |
| |
| The above copyright notice and this permission notice shall be included in all |
| copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| SOFTWARE. |
| */ |
| |
| struct Particle { |
| float4 pos; |
| float4 vel; |
| float4 uv; |
| float4 normal; |
| float pinned; |
| }; |
| |
| [[vk::binding(0)]] |
| StructuredBuffer<Particle> particleIn; |
| [[vk::binding(1)]] |
| RWStructuredBuffer<Particle> particleOut; |
| |
| struct UBO |
| { |
| float deltaT; |
| float particleMass; |
| float springStiffness; |
| float damping; |
| float restDistH; |
| float restDistV; |
| float restDistD; |
| float sphereRadius; |
| float4 spherePos; |
| float4 gravity; |
| int2 particleCount; |
| }; |
| |
| cbuffer ubo : register(b2) |
| { |
| UBO params; |
| }; |
| |
| #ifdef GLSLANG |
| layout ( push_constant ) cbuffer PushConstants |
| { |
| uint calculateNormals; |
| } pushConstants; |
| #else |
| struct PushConstants |
| { |
| uint calculateNormals; |
| }; |
| |
| [[vk::push_constant]] |
| PushConstants pushConstants; |
| #endif |
| |
| float3 springForce(float3 p0, float3 p1, float restDist) |
| { |
| float3 dist = p0 - p1; |
| return normalize(dist) * params.springStiffness * (length(dist) - restDist); |
| } |
| |
| [numthreads(10, 10, 1)] |
| void main(uint3 id : SV_DispatchThreadID) |
| { |
| uint index = id.y * params.particleCount.x + id.x; |
| if (index > params.particleCount.x * params.particleCount.y) |
| return; |
| |
| // Pinned? |
| if (particleIn[index].pinned == 1.0) { |
| particleOut[index].pos = particleOut[index].pos; |
| particleOut[index].vel = float4(0, 0, 0, 0); |
| return; |
| } |
| |
| // Initial force from gravity |
| float3 force = params.gravity.xyz * params.particleMass; |
| |
| float3 pos = particleIn[index].pos.xyz; |
| float3 vel = particleIn[index].vel.xyz; |
| |
| // Spring forces from neighboring particles |
| // left |
| if (id.x > 0) { |
| force += springForce(particleIn[index-1].pos.xyz, pos, params.restDistH); |
| } |
| // right |
| if (id.x < params.particleCount.x - 1) { |
| force += springForce(particleIn[index + 1].pos.xyz, pos, params.restDistH); |
| } |
| // upper |
| if (id.y < params.particleCount.y - 1) { |
| force += springForce(particleIn[index + params.particleCount.x].pos.xyz, pos, params.restDistV); |
| } |
| // lower |
| if (id.y > 0) { |
| force += springForce(particleIn[index - params.particleCount.x].pos.xyz, pos, params.restDistV); |
| } |
| // upper-left |
| if ((id.x > 0) && (id.y < params.particleCount.y - 1)) { |
| force += springForce(particleIn[index + params.particleCount.x - 1].pos.xyz, pos, params.restDistD); |
| } |
| // lower-left |
| if ((id.x > 0) && (id.y > 0)) { |
| force += springForce(particleIn[index - params.particleCount.x - 1].pos.xyz, pos, params.restDistD); |
| } |
| // upper-right |
| if ((id.x < params.particleCount.x - 1) && (id.y < params.particleCount.y - 1)) { |
| force += springForce(particleIn[index + params.particleCount.x + 1].pos.xyz, pos, params.restDistD); |
| } |
| // lower-right |
| if ((id.x < params.particleCount.x - 1) && (id.y > 0)) { |
| force += springForce(particleIn[index - params.particleCount.x + 1].pos.xyz, pos, params.restDistD); |
| } |
| |
| force += (-params.damping * vel); |
| |
| // Integrate |
| float3 f = force * (1.0 / params.particleMass); |
| particleOut[index].pos = float4(pos + vel * params.deltaT + 0.5 * f * params.deltaT * params.deltaT, 1.0); |
| particleOut[index].vel = float4(vel + f * params.deltaT, 0.0); |
| |
| // Sphere collision |
| float3 sphereDist = particleOut[index].pos.xyz - params.spherePos.xyz; |
| if (length(sphereDist) < params.sphereRadius + 0.01) { |
| // If the particle is inside the sphere, push it to the outer radius |
| particleOut[index].pos.xyz = params.spherePos.xyz + normalize(sphereDist) * (params.sphereRadius + 0.01); |
| // Cancel out velocity |
| particleOut[index].vel = float4(0, 0, 0, 0); |
| } |
| |
| // Normals |
| if (pushConstants.calculateNormals == 1) { |
| float3 normal = float3(0, 0, 0); |
| float3 a, b, c; |
| if (id.y > 0) { |
| if (id.x > 0) { |
| a = particleIn[index - 1].pos.xyz - pos; |
| b = particleIn[index - params.particleCount.x - 1].pos.xyz - pos; |
| c = particleIn[index - params.particleCount.x].pos.xyz - pos; |
| normal += cross(a,b) + cross(b,c); |
| } |
| if (id.x < params.particleCount.x - 1) { |
| a = particleIn[index - params.particleCount.x].pos.xyz - pos; |
| b = particleIn[index - params.particleCount.x + 1].pos.xyz - pos; |
| c = particleIn[index + 1].pos.xyz - pos; |
| normal += cross(a,b) + cross(b,c); |
| } |
| } |
| if (id.y < params.particleCount.y - 1) { |
| if (id.x > 0) { |
| a = particleIn[index + params.particleCount.x].pos.xyz - pos; |
| b = particleIn[index + params.particleCount.x - 1].pos.xyz - pos; |
| c = particleIn[index - 1].pos.xyz - pos; |
| normal += cross(a,b) + cross(b,c); |
| } |
| if (id.x < params.particleCount.x - 1) { |
| a = particleIn[index + 1].pos.xyz - pos; |
| b = particleIn[index + params.particleCount.x + 1].pos.xyz - pos; |
| c = particleIn[index + params.particleCount.x].pos.xyz - pos; |
| normal += cross(a,b) + cross(b,c); |
| } |
| } |
| particleOut[index].normal = float4(normalize(normal), 0.0f); |
| } |
| } |