| /* |
| The MIT License (MIT) |
| |
| 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. |
| */ |
| |
| #version 450 |
| |
| struct Particle { |
| vec4 pos; |
| vec4 vel; |
| vec4 uv; |
| vec4 normal; |
| float pinned; |
| }; |
| |
| layout(std430, binding = 0) buffer ParticleIn { |
| Particle particleIn[ ]; |
| }; |
| |
| layout(std430, binding = 1) buffer ParticleOut { |
| Particle particleOut[ ]; |
| }; |
| |
| // todo: use shared memory to speed up calculation |
| |
| layout (local_size_x = 10, local_size_y = 10) in; |
| |
| layout (binding = 2) uniform UBO |
| { |
| float deltaT; |
| float particleMass; |
| float springStiffness; |
| float damping; |
| float restDistH; |
| float restDistV; |
| float restDistD; |
| float sphereRadius; |
| vec4 spherePos; |
| vec4 gravity; |
| ivec2 particleCount; |
| } params; |
| |
| layout (push_constant) uniform PushConsts { |
| uint calculateNormals; |
| } pushConsts; |
| |
| vec3 springForce(vec3 p0, vec3 p1, float restDist) |
| { |
| vec3 dist = p0 - p1; |
| return normalize(dist) * params.springStiffness * (length(dist) - restDist); |
| } |
| |
| void main() |
| { |
| uvec3 id = gl_GlobalInvocationID; |
| |
| 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 = vec4(0.0); |
| return; |
| } |
| |
| // Initial force from gravity |
| vec3 force = params.gravity.xyz * params.particleMass; |
| |
| vec3 pos = particleIn[index].pos.xyz; |
| vec3 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 |
| vec3 f = force * (1.0 / params.particleMass); |
| particleOut[index].pos = vec4(pos + vel * params.deltaT + 0.5 * f * params.deltaT * params.deltaT, 1.0); |
| particleOut[index].vel = vec4(vel + f * params.deltaT, 0.0); |
| |
| // Sphere collision |
| vec3 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 = vec4(0.0); |
| } |
| |
| // Normals |
| if (pushConsts.calculateNormals == 1) { |
| vec3 normal = vec3(0.0); |
| vec3 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 = vec4(normalize(normal), 0.0f); |
| } |
| } |