|
|
|
#version 450
|
|
|
|
|
|
|
|
layout (local_size_x = 32) in;
|
|
|
|
|
|
|
|
struct Vertex {
|
|
|
|
vec3 position;
|
|
|
|
vec3 color;
|
|
|
|
vec2 uv;
|
|
|
|
vec3 normal;
|
|
|
|
vec3 velocity;
|
|
|
|
vec3 prevPosition;
|
|
|
|
float inverseMass;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Face {
|
|
|
|
uint a;
|
|
|
|
uint b;
|
|
|
|
uint c;
|
|
|
|
};
|
|
|
|
|
|
|
|
layout (std430, set = 0, binding = 0) buffer VertexBuffer {
|
|
|
|
Vertex vertices[];
|
|
|
|
};
|
|
|
|
layout (std430, set = 0, binding = 1) buffer FaceBuffer {
|
|
|
|
Face faces[];
|
|
|
|
};
|
|
|
|
|
|
|
|
layout (std140, set = 0, binding = 5) uniform Sizes {
|
|
|
|
uint vertexCount;
|
|
|
|
uint faceCount;
|
|
|
|
};
|
|
|
|
|
|
|
|
layout (set = 1, binding = 0) uniform CameraUniform {
|
|
|
|
mat4 view;
|
|
|
|
mat4 projection;
|
|
|
|
vec2 viewport;
|
|
|
|
} camera;
|
|
|
|
|
|
|
|
|
|
|
|
layout (std430, set = 2, binding = 1) buffer GrabInformation {
|
|
|
|
float originalInverseMass;
|
|
|
|
uint vID;
|
|
|
|
uint distanceToFace; // bits are float format
|
|
|
|
bool foundHit;
|
|
|
|
};
|
|
|
|
|
|
|
|
layout (push_constant, std430) uniform PushConstants {
|
|
|
|
uint state;
|
|
|
|
vec2 screenPosition;
|
|
|
|
vec2 screenDelta;
|
|
|
|
};
|
|
|
|
|
|
|
|
vec3 toNDC(vec3 world){
|
|
|
|
mat4 realProjection = camera.projection;
|
|
|
|
realProjection[1][1] *= -1;
|
|
|
|
|
|
|
|
mat4 VP = realProjection * camera.view;
|
|
|
|
|
|
|
|
vec4 inhomo = VP * vec4(world, 1);
|
|
|
|
|
|
|
|
return vec3(inhomo.xyz / inhomo.w);
|
|
|
|
}
|
|
|
|
|
|
|
|
vec3 toWorld(vec3 ndcCube){
|
|
|
|
mat4 realProjection = camera.projection;
|
|
|
|
realProjection[1][1] *= -1;
|
|
|
|
mat4 VPinv = inverse(realProjection * camera.view);
|
|
|
|
|
|
|
|
vec4 inhomo = VPinv * vec4(ndcCube, 1);
|
|
|
|
|
|
|
|
return vec3(inhomo.xyz / inhomo.w);
|
|
|
|
}
|
|
|
|
|
|
|
|
void rayForward(out vec3 origin, out vec3 direction){
|
|
|
|
vec2 ndc = vec2(
|
|
|
|
(2 * screenPosition.x / camera.viewport.x) - 1,
|
|
|
|
1 - (2 * screenPosition.y / camera.viewport.y)
|
|
|
|
);
|
|
|
|
|
|
|
|
vec3 a = vec3(ndc, 0);
|
|
|
|
vec3 b = vec3(ndc, 1);
|
|
|
|
|
|
|
|
vec3 p1 = toWorld(a);
|
|
|
|
vec3 p2 = toWorld(b);
|
|
|
|
|
|
|
|
origin = p1;
|
|
|
|
direction = normalize(p2 - p1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFace(uint fID){
|
|
|
|
vec3 origin;
|
|
|
|
vec3 direction;
|
|
|
|
rayForward(origin, direction);
|
|
|
|
|
|
|
|
Face face = faces[fID];
|
|
|
|
vec3 a = vertices[face.a].position;
|
|
|
|
vec3 b = vertices[face.b].position;
|
|
|
|
vec3 c = vertices[face.c].position;
|
|
|
|
|
|
|
|
vec3 ab = b - a;
|
|
|
|
vec3 ac = c - a;
|
|
|
|
|
|
|
|
vec3 n = cross(ab, ac);
|
|
|
|
|
|
|
|
// only front faces
|
|
|
|
if (dot(direction, n) < 0){
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
float r = dot(a - origin, n) / dot(direction, n);
|
|
|
|
|
|
|
|
// no faces behind us
|
|
|
|
if (r < 0){
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
vec3 q = origin + r * direction;
|
|
|
|
|
|
|
|
// check if q is inside triangle
|
|
|
|
// https://math.stackexchange.com/questions/4322/check-whether-a-point-is-within-a-3d-triangle
|
|
|
|
|
|
|
|
vec3 qa = a - q;
|
|
|
|
vec3 qb = b - q;
|
|
|
|
vec3 qc = c - q;
|
|
|
|
|
|
|
|
float area = length(cross(ab, ac)) / 2;
|
|
|
|
float alpha = length(cross(qb, qc)) / (2 * area);
|
|
|
|
float beta = length(cross(qc, qa)) / (2 * area);
|
|
|
|
float gamma = length(cross(qa, qb)) / (2 * area);
|
|
|
|
|
|
|
|
bool isOne = abs(alpha + beta + gamma - 1) < 0.001;
|
|
|
|
|
|
|
|
if (alpha >= 0 && alpha <= 1 && beta >= 0 && beta <= 1 && gamma >= 0 && gamma <= 1 && isOne){
|
|
|
|
uint newDistance;
|
|
|
|
uint expectedOriginalDistance;
|
|
|
|
do {
|
|
|
|
expectedOriginalDistance = distanceToFace;
|
|
|
|
newDistance = floatBitsToUint(min(r, uintBitsToFloat(expectedOriginalDistance)));
|
|
|
|
} while (atomicCompSwap(distanceToFace, expectedOriginalDistance, newDistance) != expectedOriginalDistance);
|
|
|
|
if (newDistance == floatBitsToUint(r)){
|
|
|
|
foundHit = true;
|
|
|
|
|
|
|
|
// vertex nearest to q
|
|
|
|
if (length(qa) < length(qb)){
|
|
|
|
if (length(qa) < length(qc)){
|
|
|
|
vID = face.a;
|
|
|
|
} else {
|
|
|
|
vID = face.c;
|
|
|
|
}
|
|
|
|
} else if (length(qb) < length(qc)){
|
|
|
|
vID = face.b;
|
|
|
|
} else {
|
|
|
|
vID = face.c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void move(){
|
|
|
|
vec3 ndcDelta = vec3(
|
|
|
|
2 * screenDelta.x / camera.viewport.x,
|
|
|
|
-2 * screenDelta.y / camera.viewport.y,
|
|
|
|
0
|
|
|
|
);
|
|
|
|
|
|
|
|
vec3 ndc = toNDC(vertices[vID].position);
|
|
|
|
vec3 ndc2 = ndc + ndcDelta;
|
|
|
|
|
|
|
|
vec3 p2 = toWorld(ndc2);
|
|
|
|
|
|
|
|
vec3 worldDelta = p2 - vertices[vID].position;
|
|
|
|
|
|
|
|
vertices[vID].position += worldDelta;
|
|
|
|
}
|
|
|
|
|
|
|
|
void release(){
|
|
|
|
vertices[vID].inverseMass = originalInverseMass;
|
|
|
|
distanceToFace = floatBitsToUint(1.0e20);
|
|
|
|
foundHit = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void main(){
|
|
|
|
uint id = gl_GlobalInvocationID.x;
|
|
|
|
|
|
|
|
switch (state){
|
|
|
|
case 0:
|
|
|
|
if (id < faceCount){
|
|
|
|
testFace(id);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (id == 0 && foundHit){
|
|
|
|
originalInverseMass = vertices[vID].inverseMass;
|
|
|
|
vertices[vID].inverseMass = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (id == 0 && foundHit){
|
|
|
|
move();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
if (id == 0 && foundHit){
|
|
|
|
release();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|