1617a3babSopenharmony_ci/*
2617a3babSopenharmony_ciThe MIT License (MIT)
3617a3babSopenharmony_ci
4617a3babSopenharmony_ciCopyright (c) 2022 Sascha Willems
5617a3babSopenharmony_ci
6617a3babSopenharmony_ciPermission is hereby granted, free of charge, to any person obtaining a copy
7617a3babSopenharmony_ciof this software and associated documentation files (the "Software"), to deal
8617a3babSopenharmony_ciin the Software without restriction, including without limitation the rights
9617a3babSopenharmony_cito use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10617a3babSopenharmony_cicopies of the Software, and to permit persons to whom the Software is
11617a3babSopenharmony_cifurnished to do so, subject to the following conditions:
12617a3babSopenharmony_ci
13617a3babSopenharmony_ciThe above copyright notice and this permission notice shall be included in all
14617a3babSopenharmony_cicopies or substantial portions of the Software.
15617a3babSopenharmony_ci
16617a3babSopenharmony_ciTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17617a3babSopenharmony_ciIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18617a3babSopenharmony_ciFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19617a3babSopenharmony_ciAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20617a3babSopenharmony_ciLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21617a3babSopenharmony_ciOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22617a3babSopenharmony_ciSOFTWARE.
23617a3babSopenharmony_ci*/
24617a3babSopenharmony_ci
25617a3babSopenharmony_ci#version 450
26617a3babSopenharmony_ci
27617a3babSopenharmony_cistruct Particle {
28617a3babSopenharmony_ci	vec4 pos;
29617a3babSopenharmony_ci	vec4 vel;
30617a3babSopenharmony_ci	vec4 uv;
31617a3babSopenharmony_ci	vec4 normal;
32617a3babSopenharmony_ci	float pinned;
33617a3babSopenharmony_ci};
34617a3babSopenharmony_ci
35617a3babSopenharmony_cilayout(std430, binding = 0) buffer ParticleIn {
36617a3babSopenharmony_ci	Particle particleIn[ ];
37617a3babSopenharmony_ci};
38617a3babSopenharmony_ci
39617a3babSopenharmony_cilayout(std430, binding = 1) buffer ParticleOut {
40617a3babSopenharmony_ci	Particle particleOut[ ];
41617a3babSopenharmony_ci};
42617a3babSopenharmony_ci
43617a3babSopenharmony_ci// todo: use shared memory to speed up calculation
44617a3babSopenharmony_ci
45617a3babSopenharmony_cilayout (local_size_x = 10, local_size_y = 10) in;
46617a3babSopenharmony_ci
47617a3babSopenharmony_cilayout (binding = 2) uniform UBO
48617a3babSopenharmony_ci{
49617a3babSopenharmony_ci	float deltaT;
50617a3babSopenharmony_ci	float particleMass;
51617a3babSopenharmony_ci	float springStiffness;
52617a3babSopenharmony_ci	float damping;
53617a3babSopenharmony_ci	float restDistH;
54617a3babSopenharmony_ci	float restDistV;
55617a3babSopenharmony_ci	float restDistD;
56617a3babSopenharmony_ci	float sphereRadius;
57617a3babSopenharmony_ci	vec4 spherePos;
58617a3babSopenharmony_ci	vec4 gravity;
59617a3babSopenharmony_ci	ivec2 particleCount;
60617a3babSopenharmony_ci} params;
61617a3babSopenharmony_ci
62617a3babSopenharmony_cilayout (push_constant) uniform PushConsts {
63617a3babSopenharmony_ci	uint calculateNormals;
64617a3babSopenharmony_ci} pushConsts;
65617a3babSopenharmony_ci
66617a3babSopenharmony_civec3 springForce(vec3 p0, vec3 p1, float restDist)
67617a3babSopenharmony_ci{
68617a3babSopenharmony_ci	vec3 dist = p0 - p1;
69617a3babSopenharmony_ci	return normalize(dist) * params.springStiffness * (length(dist) - restDist);
70617a3babSopenharmony_ci}
71617a3babSopenharmony_ci
72617a3babSopenharmony_civoid main()
73617a3babSopenharmony_ci{
74617a3babSopenharmony_ci	uvec3 id = gl_GlobalInvocationID;
75617a3babSopenharmony_ci
76617a3babSopenharmony_ci	uint index = id.y * params.particleCount.x + id.x;
77617a3babSopenharmony_ci	if (index > params.particleCount.x * params.particleCount.y)
78617a3babSopenharmony_ci		return;
79617a3babSopenharmony_ci
80617a3babSopenharmony_ci	// Pinned?
81617a3babSopenharmony_ci	if (particleIn[index].pinned == 1.0) {
82617a3babSopenharmony_ci		particleOut[index].pos = particleOut[index].pos;
83617a3babSopenharmony_ci		particleOut[index].vel = vec4(0.0);
84617a3babSopenharmony_ci		return;
85617a3babSopenharmony_ci	}
86617a3babSopenharmony_ci
87617a3babSopenharmony_ci	// Initial force from gravity
88617a3babSopenharmony_ci	vec3 force = params.gravity.xyz * params.particleMass;
89617a3babSopenharmony_ci
90617a3babSopenharmony_ci	vec3 pos = particleIn[index].pos.xyz;
91617a3babSopenharmony_ci	vec3 vel = particleIn[index].vel.xyz;
92617a3babSopenharmony_ci
93617a3babSopenharmony_ci	// Spring forces from neighboring particles
94617a3babSopenharmony_ci	// left
95617a3babSopenharmony_ci	if (id.x > 0) {
96617a3babSopenharmony_ci		force += springForce(particleIn[index-1].pos.xyz, pos, params.restDistH);
97617a3babSopenharmony_ci	}
98617a3babSopenharmony_ci	// right
99617a3babSopenharmony_ci	if (id.x < params.particleCount.x - 1) {
100617a3babSopenharmony_ci		force += springForce(particleIn[index + 1].pos.xyz, pos, params.restDistH);
101617a3babSopenharmony_ci	}
102617a3babSopenharmony_ci	// upper
103617a3babSopenharmony_ci	if (id.y < params.particleCount.y - 1) {
104617a3babSopenharmony_ci		force += springForce(particleIn[index + params.particleCount.x].pos.xyz, pos, params.restDistV);
105617a3babSopenharmony_ci	}
106617a3babSopenharmony_ci	// lower
107617a3babSopenharmony_ci	if (id.y > 0) {
108617a3babSopenharmony_ci		force += springForce(particleIn[index - params.particleCount.x].pos.xyz, pos, params.restDistV);
109617a3babSopenharmony_ci	}
110617a3babSopenharmony_ci	// upper-left
111617a3babSopenharmony_ci	if ((id.x > 0) && (id.y < params.particleCount.y - 1)) {
112617a3babSopenharmony_ci		force += springForce(particleIn[index + params.particleCount.x - 1].pos.xyz, pos, params.restDistD);
113617a3babSopenharmony_ci	}
114617a3babSopenharmony_ci	// lower-left
115617a3babSopenharmony_ci	if ((id.x > 0) && (id.y > 0)) {
116617a3babSopenharmony_ci		force += springForce(particleIn[index - params.particleCount.x - 1].pos.xyz, pos, params.restDistD);
117617a3babSopenharmony_ci	}
118617a3babSopenharmony_ci	// upper-right
119617a3babSopenharmony_ci	if ((id.x < params.particleCount.x - 1) && (id.y < params.particleCount.y - 1)) {
120617a3babSopenharmony_ci		force += springForce(particleIn[index + params.particleCount.x + 1].pos.xyz, pos, params.restDistD);
121617a3babSopenharmony_ci	}
122617a3babSopenharmony_ci	// lower-right
123617a3babSopenharmony_ci	if ((id.x < params.particleCount.x - 1) && (id.y > 0)) {
124617a3babSopenharmony_ci		force += springForce(particleIn[index - params.particleCount.x + 1].pos.xyz, pos, params.restDistD);
125617a3babSopenharmony_ci	}
126617a3babSopenharmony_ci
127617a3babSopenharmony_ci	force += (-params.damping * vel);
128617a3babSopenharmony_ci
129617a3babSopenharmony_ci	// Integrate
130617a3babSopenharmony_ci	vec3 f = force * (1.0 / params.particleMass);
131617a3babSopenharmony_ci	particleOut[index].pos = vec4(pos + vel * params.deltaT + 0.5 * f * params.deltaT * params.deltaT, 1.0);
132617a3babSopenharmony_ci	particleOut[index].vel = vec4(vel + f * params.deltaT, 0.0);
133617a3babSopenharmony_ci
134617a3babSopenharmony_ci	// Sphere collision
135617a3babSopenharmony_ci	vec3 sphereDist = particleOut[index].pos.xyz - params.spherePos.xyz;
136617a3babSopenharmony_ci	if (length(sphereDist) < params.sphereRadius + 0.01) {
137617a3babSopenharmony_ci		// If the particle is inside the sphere, push it to the outer radius
138617a3babSopenharmony_ci		particleOut[index].pos.xyz = params.spherePos.xyz + normalize(sphereDist) * (params.sphereRadius + 0.01);
139617a3babSopenharmony_ci		// Cancel out velocity
140617a3babSopenharmony_ci		particleOut[index].vel = vec4(0.0);
141617a3babSopenharmony_ci	}
142617a3babSopenharmony_ci
143617a3babSopenharmony_ci	// Normals
144617a3babSopenharmony_ci	if (pushConsts.calculateNormals == 1) {
145617a3babSopenharmony_ci		vec3 normal = vec3(0.0);
146617a3babSopenharmony_ci		vec3 a, b, c;
147617a3babSopenharmony_ci		if (id.y > 0) {
148617a3babSopenharmony_ci			if (id.x > 0) {
149617a3babSopenharmony_ci				a = particleIn[index - 1].pos.xyz - pos;
150617a3babSopenharmony_ci				b = particleIn[index - params.particleCount.x - 1].pos.xyz - pos;
151617a3babSopenharmony_ci				c = particleIn[index - params.particleCount.x].pos.xyz - pos;
152617a3babSopenharmony_ci				normal += cross(a,b) + cross(b,c);
153617a3babSopenharmony_ci			}
154617a3babSopenharmony_ci			if (id.x < params.particleCount.x - 1) {
155617a3babSopenharmony_ci				a = particleIn[index - params.particleCount.x].pos.xyz - pos;
156617a3babSopenharmony_ci				b = particleIn[index - params.particleCount.x + 1].pos.xyz - pos;
157617a3babSopenharmony_ci				c = particleIn[index + 1].pos.xyz - pos;
158617a3babSopenharmony_ci				normal += cross(a,b) + cross(b,c);
159617a3babSopenharmony_ci			}
160617a3babSopenharmony_ci		}
161617a3babSopenharmony_ci		if (id.y < params.particleCount.y - 1) {
162617a3babSopenharmony_ci			if (id.x > 0) {
163617a3babSopenharmony_ci				a = particleIn[index + params.particleCount.x].pos.xyz - pos;
164617a3babSopenharmony_ci				b = particleIn[index + params.particleCount.x - 1].pos.xyz - pos;
165617a3babSopenharmony_ci				c = particleIn[index - 1].pos.xyz - pos;
166617a3babSopenharmony_ci				normal += cross(a,b) + cross(b,c);
167617a3babSopenharmony_ci			}
168617a3babSopenharmony_ci			if (id.x < params.particleCount.x - 1) {
169617a3babSopenharmony_ci				a = particleIn[index + 1].pos.xyz - pos;
170617a3babSopenharmony_ci				b = particleIn[index + params.particleCount.x + 1].pos.xyz - pos;
171617a3babSopenharmony_ci				c = particleIn[index + params.particleCount.x].pos.xyz - pos;
172617a3babSopenharmony_ci				normal += cross(a,b) + cross(b,c);
173617a3babSopenharmony_ci			}
174617a3babSopenharmony_ci		}
175617a3babSopenharmony_ci		particleOut[index].normal = vec4(normalize(normal), 0.0f);
176617a3babSopenharmony_ci	}
177617a3babSopenharmony_ci}
178