breakout/src/collision.cpp

263 lines
6.4 KiB
C++

#include <math.h>
#include <stdio.h>
#include "collision.hpp"
#include "model/cube.h"
// cube_vertices
// cube_Cube_triangles
vec3 _plane_normal(vec3 a,
vec3 b,
vec3 c)
{
return normalize(cross(b - a, c - a));
}
float _plane_d(vec3 normal,
vec3 origin)
{
return -dot(normal, origin);
}
float _signed_distance(vec3 plane_normal,
float plane_d,
vec3 point)
{
return dot(point, plane_normal) + plane_d;
}
float clamp(float v,
float min,
float max)
{
if (v > max)
return max;
if (v < min)
return min;
return v;
}
vec2 _t0_t1(float signed_distance,
float r_n_dot_v)
{
if (r_n_dot_v == 0.0)
return vec2(0.0, 1.0);
else {
float t0 = (-1.0 - signed_distance) * r_n_dot_v;
float t1 = ( 1.0 - signed_distance) * r_n_dot_v;
if (t0 > t1) {
float tmp = t0;
t0 = t1;
t1 = tmp;
}
t0 = clamp(t0, -1.0, 1.0);
t1 = clamp(t0, -1.0, 1.0);
return vec2(t0, t1);
}
}
bool point_in_triangle(vec3 point,
vec3 ap,
vec3 bp,
vec3 cp)
{
vec3 ba = bp - ap;
vec3 ca = cp - ap;
vec3 vp = point - ap;
float a = dot(ba, ba);
float b = dot(ba, ca);
float c = dot(ca, ca);
float d = dot(vp, ba);
float e = dot(vp, ca);
float x = (d * c) - (e * b);
float y = (e * a) - (d * b);
float z = x + y - ((a * c) - (b * b));
return (z < 0) && !((x < 0) || (y < 0));
}
struct ct {
bool collision;
float t;
};
#define NONE -99999999.0f
ct lowest_root(float a,
float b,
float c,
float max_r)
{
float determinant = b * b - 4.0f * a * c;
if (determinant < 0.0f)
return ct(false, max_r);
float sqrt_det = sqrtf(determinant);
float r_2a = 1.0f / (2.0f * a);
float r1 = (-b - sqrt_det) * r_2a;
float r2 = (-b + sqrt_det) * r_2a;
if (r1 > r2) {
float tmp = r1;
r1 = r2;
r2 = tmp;
}
if (r1 > 0 && (max_r == NONE || r1 < max_r))
return ct(true, r1);
if (r2 > 0 && (max_r == NONE || r2 < max_r))
return ct(true, r2);
return ct(false, max_r);
}
ct triangle_point_collide(vec3 tri_point,
vec3 point,
vec3 velocity,
float t)
{
float a = dot(velocity, velocity);
float b = 2.0f * dot(velocity, point - tri_point);
vec3 c0 = (tri_point - point);
float c = dot(c0, c0) - 1.0f;
return lowest_root(a, b, c, t);
}
struct ctf0 {
bool collision;
float t;
float f0;
};
ctf0 triangle_edge_collide(vec3 tri_point1,
vec3 tri_point2,
vec3 point,
vec3 velocity,
float t)
{
vec3 edge = tri_point2 - tri_point1;
vec3 base = tri_point1 - point;
float edge_dot_edge = dot(edge, edge);
float edge_dot_velocity = dot(edge, velocity);
float edge_dot_base = dot(edge, base);
float velocity_dot_velocity = dot(velocity, velocity);
float a = edge_dot_edge * -velocity_dot_velocity + (edge_dot_velocity * edge_dot_velocity);
float b = edge_dot_edge * 2.0f * (dot(velocity, base)) - 2.0f * edge_dot_velocity * edge_dot_base;
float c = edge_dot_edge * (1.0f - dot(base, base)) + (edge_dot_base * edge_dot_base);
ct res = lowest_root(a, b, c, t);
float f0 = NONE;
if (res.collision) {
f0 = (edge_dot_velocity * res.t - edge_dot_base) * (1.0f / edge_dot_edge);
}
return ctf0(res.collision, t, f0);
}
struct t0t1pip {
float t0;
float t1;
vec3 pip;
};
t0t1pip collision_inner(vec3 a, vec3 b, vec3 c,
vec3 point,
vec3 velocity)
{
vec3 plane_normal = _plane_normal(a, b, c);
vec3 plane_origin = a;
float plane_d = _plane_d(plane_normal, plane_origin);
float signed_distance = _signed_distance(plane_normal, plane_d, point);
float n_dot_v = dot(plane_normal, velocity);
float r_n_dot_v = 1.0f / n_dot_v;
bool embedded = n_dot_v == 0.0f;
if (n_dot_v == 0.0f) {
if (fabsf(signed_distance) >= 1.0)
return t0t1pip(NONE, NONE, vec3(0, 0, 0));
}
vec2 t0t1 = _t0_t1(signed_distance, r_n_dot_v);
float t0 = t0t1.x;
float t1 = t0t1.y;
vec3 plane_intersection_point = point - plane_normal + t0 * velocity;
bool inside = point_in_triangle(plane_intersection_point, a, b, c);
bool collision = false;
vec3 collision_point;
float t = NONE;
vec3 abc[] = {a, b, c};
for (int i = 0; i < 3; i++) {
vec3 tri_point = abc[i];
ct res = triangle_point_collide(tri_point, point, velocity, t);
t = res.t;
if (res.collision) {
collision_point = tri_point;
collision = true;
}
}
for (int i = 0; i < 3; i++) {
vec3 p1 = abc[i];
vec3 p2 = abc[(i + 1) % 3];
ctf0 res = triangle_edge_collide(p1, p2, point, velocity, t);
if (res.collision && res.f0 >= 0.0 && res.f0 <= 1.0) {
t = res.t;
collision_point = p1 + res.f0 * (p2 - p1);
collision = true;
}
}
if (inside && collision) {
printf("r_n_dot_v %f %f %f %f\n", n_dot_v, r_n_dot_v, t0, t1);
return t0t1pip(t0, t1, plane_intersection_point);
}
else
return t0t1pip(NONE, NONE, vec3(0, 0, 0));
}
vec4 collision(mat4x4 trans,
vec3 point, vec3 velocity)
{
bool collision = false;
float smallest = -9999.0f;
vec3 smallest_normal = vec3(0, 0, 0);
//printf("velocity %f %f %f\n", velocity.x, velocity.y, velocity.z);
for (int i = 0; i < cube_Cube_triangles_length / 3; i++) {
const float * a = &cube_vertices[cube_Cube_triangles[i * 3 + 0] * 8];
const float * b = &cube_vertices[cube_Cube_triangles[i * 3 + 1] * 8];
const float * c = &cube_vertices[cube_Cube_triangles[i * 3 + 2] * 8];
vec3 ap = trans * vec3(a[0], a[1], a[2]);
vec3 bp = trans * vec3(b[0], b[1], b[2]);
vec3 cp = trans * vec3(c[0], c[1], c[2]);
t0t1pip res = collision_inner(ap, bp, cp, point, velocity);
bool inside = res.t0 != NONE;
if (!inside)
continue;
if (collision == false || res.t0 < smallest) {
printf("new smallest %f %f\n", smallest, res.t0);
smallest = res.t0;
vec3 plane_normal = _plane_normal(ap, bp, cp);
smallest_normal = plane_normal;
}
collision = true;
}
//float collision_f = collision ? 1.0 : 0.0;
return vec4(smallest_normal.x,
smallest_normal.y,
smallest_normal.z,
collision ? smallest : -9999.0f);
}