You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
3.9 KiB

#version 450
layout (local_size_x = 32) in;
#include "structs.comp"
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;
}
}