Skip to content

Instantly share code, notes, and snippets.

@ramirez7
Forked from vurtun/col.md
Created November 15, 2023 20:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ramirez7/c196c67ccb1aecf4892dcd74c1c59130 to your computer and use it in GitHub Desktop.
Save ramirez7/c196c67ccb1aecf4892dcd74c1c59130 to your computer and use it in GitHub Desktop.
#include <math.h> /* sqrt, sin, cos, tan */
#include <assert.h> /* assert macro */
#include <string.h> /* memcpy */
#include <stdio.h> /* memcpy */
#include <stdarg.h> /* memcpy */
#include <stdlib.h>
#include <float.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define clamp(a,v,b) (max(min(b,v),a))
#define sign(a) (((a) < 0) ? -1: 1)
#define cntof(a) ((int)(sizeof(a)/sizeof((a)[0])))
#define PI_CONSTANT (3.141592654f)
#define TO_RAD ((PI_CONSTANT/180.0f))
/* ============================================================================
*
* FLOAT MATH
*
* =========================================================================== */
typedef float m3[9];
static const float f3z[3];
static const m3 m3z;
static const m3 m3id = {1,0,0, 0,1,0, 0,0,1};
#define fop(r,e,a,p,b,i,s) (r) e ((a) p (b)) i (s)
#define f3op(r,e,a,p,b,i,s)\
{fop((r)[0],e,(a)[0],p,(b)[0],i,s),\
fop((r)[1],e,(a)[1],p,(b)[1],i,s),\
fop((r)[2],e,(a)[2],p,(b)[2],i,s);}
#define f3set(v,x,y,z) (v)[0]=(x),(v)[1]=(y),(v)[2]=(z)
#define f3zero(v) f3set(v,0,0,0)
#define f3unpack(v) (v)[0],(v)[1],(v)[2]
#define f3cpy(d,s) (d)[0]=(s)[0],(d)[1]=(s)[1],(d)[2]=(s)[2]
#define f3add(d,a,b) f3op(d,=,a,+,b,+,0)
#define f3sub(d,a,b) f3op(d,=,a,-,b,+,0)
#define f3mul(d,a,s) f3op(d,=,a,+,f3z,*,s)
#define f3dot(a,b) ((a)[0]*(b)[0]+(a)[1]*(b)[1]+(a)[2]*(b)[2])
#define f3lerp(r,a,t,b) f3op(r,=,a,+,f3z,*,(1.0f - (t))); f3op(r,+=,b,+,f3z,*,t)
#define m3cpy(d,s) memcpy(d,s,sizeof(m3))
static inline void
f3cross(float *r, const float *a, const float *b)
{
r[0] = (a[1]*b[2]) - (a[2]*b[1]);
r[1] = (a[2]*b[0]) - (a[0]*b[2]);
r[2] = (a[0]*b[1]) - (a[1]*b[0]);
}
static inline float
f3box(const float *a, const float *b, const float *c)
{
float n[3];
f3cross(n, a, b);
return f3dot(n, c);
}
static inline void
f3norm(float *v)
{
float il, l = f3dot(v, v);
if (l == 0.0f) return;
il = 1 / sqrtf(l);
f3mul(v,v,il);
}
static void
m3mul(float *r, const float *a, const float *b)
{
int i = 0;
#define A(col, row) (a)[(col*3)+row]
#define B(col, row) (b)[(col*3)+row]
#define P(col, row) (r)[(col*3)+row]
for (i = 0; i < 3; ++i) {
const float ai0 = A(i,0), ai1 = A(i,1), ai2 = A(i,2);
P(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0);
P(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1);
P(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2);
}
#undef A
#undef B
#undef P
}
static void
transform(float *r, const float *v, const float *r33, const float *t3)
{
for (int i = 0; i < 3; ++i) {
r[i] = v[i] * r33[i*3+0];
r[i] += v[i] * r33[i*3+1];
r[i] += v[i] * r33[i*3+2];
r[i] += t3[i];
}
}
static void
transformS(float *v, const float *r33, const float *t3)
{
float tmp[3]; f3cpy(tmp, v);
transform(v, tmp, r33, t3);
}
static void
transformT(float *r, const float *v, const float *r33, const float *t3)
{
for (int i = 0; i < 3; ++i) {
float p = v[i] - t3[i];
r[i] = p * r33[0*3+i];
r[i] += p * r33[1*3+i];
r[i] += p * r33[2*3+i];
}
}
static void
transformST(float *v, const float *r33, const float *t3)
{
float tmp[3]; f3cpy(tmp, v);
transformT(v, tmp, r33, t3);
}
static void
m3rot(float *r, float angle, float X, float Y, float Z)
{
#define M(col, row) m[(col*3)+row]
float s = (float)sinf(angle);
float c = (float)cosf(angle);
float oc = 1.0f - c;
float m[16];
M(0,0) = oc * X * X + c;
M(0,1) = oc * X * Y - Z * s;
M(0,2) = oc * Z * X + Y * s;
M(1,0) = oc * X * Y + Z * s;
M(1,1) = oc * Y * Y + c;
M(1,2) = oc * Y * Z - X * s;
M(2,0) = oc * Z * X - Y * s;
M(2,1) = oc * Y * Z + X * s;
M(2,2) = oc * Z * Z + c;
m3mul(r, r, m);
}
static void
m3mulv(float *r, const float *m, const float *v)
{
#define X(a) (r)[0]
#define Y(a) (r)[1]
#define Z(a) (r)[2]
#define M(col, row) m[(col*3)+row]
float o[3]; f3cpy(o,v);
X(r) = M(0,0)*X(o) + M(0,1)*Y(o) + M(0,2)*Z(o);
Y(r) = M(1,0)*X(o) + M(1,1)*Y(o) + M(1,2)*Z(o);
Z(r) = M(2,0)*X(o) + M(2,1)*Y(o) + M(2,2)*Z(o);
#undef X
#undef Y
#undef Z
#undef M
}
/* ============================================================================
*
* GJK
*
* =========================================================================== */
#define GJK_MAX_ITERATIONS 20
struct gjk_support {
int aid, bid;
float a[3];
float b[3];
};
struct gjk_vertex {
float a[3];
float b[3];
float p[3];
int aid, bid;
};
struct gjk_simplex {
int hit;
int iter, max_iter;
int vcnt, scnt;
int saveA[5], saveB[5];
struct gjk_vertex v[5];
float bc[4], D;
};
struct gjk_result {
int hit;
float p0[3];
float p1[3];
float distance_squared;
int iterations;
};
static int
gjk(struct gjk_simplex *s, const struct gjk_support *sup, float *dv)
{
assert(s);
assert(dv);
assert(sup);
if (!s || !sup || !dv) return 0;
if (s->max_iter > 0 && s->iter >= s->max_iter)
return 0;
/* I.) Initialize */
if (!s->vcnt) {
s->scnt = 0;
s->D = FLT_MAX;
s->max_iter = !s->max_iter ? GJK_MAX_ITERATIONS: s->max_iter;
}
/* II.) Check for duplications */
for (int i = 0; i < s->scnt; ++i) {
if (sup->aid != s->saveA[i]) continue;
if (sup->bid != s->saveB[i]) continue;
return 0;
}
/* III.) Add vertex into simplex */
struct gjk_vertex *vert = &s->v[s->vcnt];
f3cpy(vert->a, sup->a);
f3cpy(vert->b, sup->b);
f3cpy(vert->p, dv);
vert->aid = sup->aid;
vert->bid = sup->bid;
s->bc[s->vcnt++] = 1.0f;
/* IV.) Find closest simplex point */
switch (s->vcnt) {
case 1: break;
case 2: {
/* -------------------- Line ----------------------- */
float a[3], b[3];
f3cpy(a, s->v[0].p);
f3cpy(b, s->v[1].p);
/* compute barycentric coordinates */
float ab[3], ba[3];
f3sub(ab, a, b);
f3sub(ba, b, a);
float u = f3dot(b, ba);
float v = f3dot(a, ab);
if (v <= 0.0f) {
/* region A */
s->bc[0] = 1.0f;
s->vcnt = 1;
break;
}
if (u <= 0.0f) {
/* region B */
s->v[0] = s->v[1];
s->bc[0] = 1.0f;
s->vcnt = 1;
break;
}
/* region AB */
s->bc[0] = u;
s->bc[1] = v;
s->vcnt = 2;
} break;
case 3: {
/* -------------------- Triangle ----------------------- */
float a[3], b[3], c[3];
f3cpy(a, s->v[0].p);
f3cpy(b, s->v[1].p);
f3cpy(c, s->v[2].p);
float ab[3], ba[3], bc[3], cb[3], ca[3], ac[3];
f3sub(ab, a, b);
f3sub(ba, b, a);
f3sub(bc, b, c);
f3sub(cb, c, b);
f3sub(ca, c, a);
f3sub(ac, a, c);
/* compute barycentric coordinates */
float u_ab = f3dot(b, ba);
float v_ab = f3dot(a, ab);
float u_bc = f3dot(c, cb);
float v_bc = f3dot(b, bc);
float u_ca = f3dot(a, ac);
float v_ca = f3dot(c, ca);
if (v_ab <= 0.0f && u_ca <= 0.0f) {
/* region A */
s->bc[0] = 1.0f;
s->vcnt = 1;
break;
}
if (u_ab <= 0.0f && v_bc <= 0.0f) {
/* region B */
s->v[0] = s->v[1];
s->bc[0] = 1.0f;
s->vcnt = 1;
break;
}
if (u_bc <= 0.0f && v_ca <= 0.0f) {
/* region C */
s->v[0] = s->v[2];
s->bc[0] = 1.0f;
s->vcnt = 1;
break;
}
/* calculate fractional area */
float n[3], n1[3], n2[3], n3[3];
f3cross(n, ba, ca);
f3cross(n1, b, c);
f3cross(n2, c, a);
f3cross(n3, a, b);
float u_abc = f3dot(n1, n);
float v_abc = f3dot(n2, n);
float w_abc = f3dot(n3, n);
if (u_ab > 0.0f && v_ab > 0.0f && w_abc <= 0.0f) {
/* region AB */
s->bc[0] = u_ab;
s->bc[1] = v_ab;
s->vcnt = 2;
break;
}
if (u_bc > 0.0f && v_bc > 0.0f && u_abc <= 0.0f) {
/* region BC */
s->v[0] = s->v[1];
s->v[1] = s->v[2];
s->bc[0] = u_bc;
s->bc[1] = v_bc;
s->vcnt = 2;
break;
}
if (u_ca > 0.0f && v_ca > 0.0f && v_abc <= 0.0f) {
/* region CA */
s->v[1] = s->v[0];
s->v[0] = s->v[2];
s->bc[0] = u_ca;
s->bc[1] = v_ca;
s->vcnt = 2;
break;
}
/* region ABC */
assert(u_abc > 0.0f && v_abc > 0.0f && w_abc > 0.0f);
s->bc[0] = u_abc;
s->bc[1] = v_abc;
s->bc[2] = w_abc;
s->vcnt = 3;
} break;
case 4: {
/* -------------------- Tetrahedron ----------------------- */
float a[3], b[3], c[3], d[3];
f3cpy(a, s->v[0].p);
f3cpy(b, s->v[1].p);
f3cpy(c, s->v[2].p);
f3cpy(d, s->v[3].p);
float ab[3], ba[3], bc[3], cb[3], ca[3], ac[3];
float db[3], bd[3], dc[3], cd[3], ad[3], da[3];
f3sub(ab, a, b);
f3sub(ba, b, a);
f3sub(bc, b, c);
f3sub(cb, c, b);
f3sub(ca, c, a);
f3sub(ac, a, c);
f3sub(db, d, b);
f3sub(bd, b, d);
f3sub(dc, d, c);
f3sub(cd, c, d);
f3sub(da, d, a);
f3sub(ad, a, d);
/* compute barycentric coordinates */
float u_ab = f3dot(b, ba);
float v_ab = f3dot(a, ab);
float u_bc = f3dot(c, cb);
float v_bc = f3dot(b, bc);
float u_ca = f3dot(a, ac);
float v_ca = f3dot(c, ca);
float u_bd = f3dot(d, db);
float v_bd = f3dot(b, bd);
float u_dc = f3dot(c, cd);
float v_dc = f3dot(d, dc);
float u_ad = f3dot(d, da);
float v_ad = f3dot(a, ad);
/* check verticies for closest point */
if (v_ab <= 0.0f && u_ca <= 0.0f && v_ad <= 0.0f) {
/* region A */
s->bc[0] = 1.0f;
s->vcnt = 1;
break;
}
if (u_ab <= 0.0f && v_bc <= 0.0f && v_bd <= 0.0f) {
/* region B */
s->v[0] = s->v[1];
s->bc[0] = 1.0f;
s->vcnt = 1;
break;
}
if (u_bc <= 0.0f && v_ca <= 0.0f && u_dc <= 0.0f) {
/* region C */
s->v[0] = s->v[2];
s->bc[0] = 1.0f;
s->vcnt = 1;
break;
}
if (u_bd <= 0.0f && v_dc <= 0.0f && u_ad <= 0.0f) {
/* region D */
s->v[0] = s->v[3];
s->bc[0] = 1.0f;
s->vcnt = 1;
break;
}
/* calculate fractional area */
float n[3], n1[3], n2[3], n3[3];
f3cross(n, da, ba);
f3cross(n1, d, b);
f3cross(n2, b, a);
f3cross(n3, a, d);
float u_adb = f3dot(n1, n);
float v_adb = f3dot(n2, n);
float w_adb = f3dot(n3, n);
f3cross(n, ca, da);
f3cross(n1, c, d);
f3cross(n2, d, a);
f3cross(n3, a, c);
float u_acd = f3dot(n1, n);
float v_acd = f3dot(n2, n);
float w_acd = f3dot(n3, n);
f3cross(n, bc, dc);
f3cross(n1, b, d);
f3cross(n2, d, c);
f3cross(n3, c, b);
float u_cbd = f3dot(n1, n);
float v_cbd = f3dot(n2, n);
float w_cbd = f3dot(n3, n);
f3cross(n, ba, ca);
f3cross(n1, b, c);
f3cross(n2, c, a);
f3cross(n3, a, b);
float u_abc = f3dot(n1, n);
float v_abc = f3dot(n2, n);
float w_abc = f3dot(n3, n);
/* check edges for closest point */
if (w_abc <= 0.0f && v_adb <= 0.0f && u_ab > 0.0f && v_ab > 0.0f) {
/* region AB */
s->bc[0] = u_ab;
s->bc[1] = v_ab;
s->vcnt = 2;
break;
}
if (u_abc <= 0.0f && w_cbd <= 0.0f && u_bc > 0.0f && v_bc > 0.0f) {
/* region BC */
s->v[0] = s->v[1];
s->v[1] = s->v[2];
s->bc[0] = u_bc;
s->bc[1] = v_bc;
s->vcnt = 2;
break;
}
if (v_abc <= 0.0f && w_acd <= 0.0f && u_ca > 0.0f && v_ca > 0.0f) {
/* region CA */
s->v[1] = s->v[0];
s->v[0] = s->v[2];
s->bc[0] = u_ca;
s->bc[1] = v_ca;
s->vcnt = 2;
break;
}
if (v_cbd <= 0.0f && u_acd <= 0.0f && u_dc > 0.0f && v_dc > 0.0f) {
/* region DC */
s->v[0] = s->v[3];
s->v[1] = s->v[2];
s->bc[0] = u_dc;
s->bc[1] = v_dc;
s->vcnt = 2;
break;
}
if (v_acd <= 0.0f && w_adb <= 0.0f && u_ad > 0.0f && v_ad > 0.0f) {
/* region AD */
s->v[1] = s->v[3];
s->bc[0] = u_ad;
s->bc[1] = v_ad;
s->vcnt = 2;
break;
}
if (u_cbd <= 0.0f && u_adb <= 0.0f && u_bd > 0.0f && v_bd > 0.0f) {
/* region BD */
s->v[0] = s->v[1];
s->v[1] = s->v[3];
s->bc[0] = u_bd;
s->bc[1] = v_bd;
s->vcnt = 2;
break;
}
/* calculate fractional volume (volume can be negative!) */
float denom = f3box(cb, ab, db);
float volume = (denom == 0) ? 1.0f: 1.0f/denom;
float u_abcd = f3box(c, d, b) * volume;
float v_abcd = f3box(c, a, d) * volume;
float w_abcd = f3box(d, a, b) * volume;
float x_abcd = f3box(b, a, c) * volume;
/* check faces for closest point */
if (x_abcd <= 0.0f && u_abc > 0.0f && v_abc > 0.0f && w_abc > 0.0f) {
/* region ABC */
s->bc[0] = u_abc;
s->bc[1] = v_abc;
s->bc[2] = w_abc;
s->vcnt = 3;
break;
}
if (u_abcd <= 0.0f && u_cbd > 0.0f && v_cbd > 0.0f && w_cbd > 0.0f) {
/* region CBD */
s->v[0] = s->v[2];
s->v[2] = s->v[3];
s->bc[0] = u_cbd;
s->bc[1] = v_cbd;
s->bc[2] = w_cbd;
s->vcnt = 3;
break;
}
if (v_abcd <= 0.0f && u_acd > 0.0f && v_acd > 0.0f && w_acd > 0.0f) {
/* region ACD */
s->v[1] = s->v[2];
s->v[2] = s->v[3];
s->bc[0] = u_acd;
s->bc[1] = v_acd;
s->bc[2] = w_acd;
s->vcnt = 3;
break;
}
if (w_abcd <= 0.0f && u_adb > 0.0f && v_adb > 0.0f && w_adb > 0.0f) {
/* region ADB */
s->v[2] = s->v[1];
s->v[1] = s->v[3];
s->bc[0] = u_adb;
s->bc[1] = v_adb;
s->bc[2] = w_adb;
s->vcnt = 3;
break;
}
/* region ABCD */
assert(u_abcd > 0.0f && v_abcd > 0.0f && w_abcd > 0.0f && x_abcd > 0.0f);
s->bc[0] = u_abcd;
s->bc[1] = v_abcd;
s->bc[2] = w_abcd;
s->bc[3] = x_abcd;
s->vcnt = 4;
} break;}
/* V.) Check if origin is enclosed by tetrahedron */
if (s->vcnt == 4) {
s->hit = 1;
return 0;
}
/* VI.) Ensure closing in on origin to prevent multi-step cycling */
{float pnt[3], denom = 0;
for (int i = 0; i < s->vcnt; ++i)
denom += s->bc[i];
denom = 1.0f / denom;
switch (s->vcnt) {
case 1: f3cpy(pnt, s->v[0].p); break;
case 2: {
/* --------- Line -------- */
float a[3], b[3];
f3mul(a, s->v[0].p, denom * s->bc[0]);
f3mul(b, s->v[1].p, denom * s->bc[1]);
f3add(pnt, a, b);
} break;
case 3: {
/* ------- Triangle ------ */
float a[3], b[3], c[3];
f3mul(a, s->v[0].p, denom * s->bc[0]);
f3mul(b, s->v[1].p, denom * s->bc[1]);
f3mul(c, s->v[2].p, denom * s->bc[2]);
f3add(pnt, a, b);
f3add(pnt, pnt, c);
} break;
case 4: {
/* ----- Tetrahedron ----- */
float a[3], b[3], c[3], d[3];
f3mul(a, s->v[0].p, denom * s->bc[0]);
f3mul(b, s->v[1].p, denom * s->bc[1]);
f3mul(c, s->v[2].p, denom * s->bc[2]);
f3mul(d, s->v[3].p, denom * s->bc[3]);
f3add(pnt, a, b);
f3add(pnt, pnt, c);
f3add(pnt, pnt, d);
} break;}
float d2 = f3dot(pnt, pnt);
if (d2 >= s->D) return 0;
s->D = d2;}
/* VII.) New search direction */
switch (s->vcnt) {
default: assert(0); break;
case 1: {
/* --------- Point -------- */
f3mul(dv, s->v[0].p, -1);
} break;
case 2: {
/* ------ Line segment ---- */
float b0[3], ba[3], t[3];
f3sub(ba, s->v[1].p, s->v[0].p);
f3mul(b0, s->v[1].p, -1);
f3cross(t, ba, b0);
f3cross(dv, t, ba);
} break;
case 3: {
/* ------- Triangle ------- */
float n[3], ab[3], ac[3];
f3sub(ab, s->v[1].p, s->v[0].p);
f3sub(ac, s->v[2].p, s->v[0].p);
f3cross(n, ab, ac);
if (f3dot(n, s->v[0].p) <= 0.0f)
f3cpy(dv, n);
else f3mul(dv, n, -1);
}}
if (f3dot(dv,dv) < FLT_EPSILON * FLT_EPSILON)
return 0;
/* VIII.) Save ids for next duplicate check */
s->scnt = s->vcnt; s->iter++;
for (int i = 0; i < s->scnt; ++i) {
s->saveA[i] = s->v[i].aid;
s->saveB[i] = s->v[i].bid;
} return 1;
}
static struct gjk_result
gjk_analyze(const struct gjk_simplex *s)
{
struct gjk_result r = {0};
r.iterations = s->iter;
r.hit = s->hit;
/* calculate normalization denominator */
float denom = 0;
for (int i = 0; i < s->vcnt; ++i)
denom += s->bc[i];
denom = 1.0f / denom;
/* compute closest points */
switch (s->vcnt) {
default: assert(0); break;
case 1: {
/* Point */
f3cpy(r.p0, s->v[0].a);
f3cpy(r.p1, s->v[0].b);
} break;
case 2: {
/* Line */
float as = denom * s->bc[0];
float bs = denom * s->bc[1];
float a[3], b[3], c[3], d[3];
f3mul(a, s->v[0].a, as);
f3mul(b, s->v[1].a, bs);
f3mul(c, s->v[0].b, as);
f3mul(d, s->v[1].b, bs);
f3add(r.p0, a, b);
f3add(r.p1, c, d);
} break;
case 3: {
/* Triangle */
float as = denom * s->bc[0];
float bs = denom * s->bc[1];
float cs = denom * s->bc[2];
float a[3], b[3], c[3];
f3mul(a, s->v[0].a, as);
f3mul(b, s->v[1].a, bs);
f3mul(c, s->v[2].a, cs);
float d[3], e[3], f[3];
f3mul(d, s->v[0].b, as);
f3mul(e, s->v[1].b, bs);
f3mul(f, s->v[2].b, cs);
f3add(r.p0, a, b);
f3add(r.p0, r.p0, c);
f3add(r.p1, d, e);
f3add(r.p1, r.p1, f);
} break;
case 4: {
/* Tetrahedron */
float as = denom * s->bc[0];
float bs = denom * s->bc[1];
float cs = denom * s->bc[2];
float ds = denom * s->bc[3];
float a[3], b[3], c[3], d[3];
f3mul(a, s->v[0].a, as);
f3mul(b, s->v[1].a, bs);
f3mul(c, s->v[2].a, cs);
f3mul(d, s->v[3].a, ds);
f3add(r.p0, a, b);
f3add(r.p0, r.p0, c);
f3add(r.p0, r.p0, d);
f3cpy(r.p1, r.p0);
} break;}
if (!r.hit) {
/* compute distance */
float d[3]; f3sub(d, r.p1, r.p0);
r.distance_squared = f3dot(d, d);
} else r.distance_squared = 0;
return r;
}
/* ============================================================================
*
* COLLISION
*
* =========================================================================== */
struct manifold {
float depth;
float contact_point[3];
float normal[3];
};
/* segment */
static float segment_closest_point_to_point_sqdist(const float *a, const float *b, const float *p);
static void segment_closest_point_to_point(float *res, const float *a, const float *b, const float *p);
static float segment_closest_point_to_segment(float *t1, float *t2, float *c1, float *c2, const float *p1, const float *q1, const float *p2, const float *q2);
/* plane */
static void planeq(float *plane4, const float *n3, const float *pnt3);
static void planeqf(float *plane4, float nx, float ny, float nz, float px, float py, float pz);
/* ray */
static float ray_intersects_plane(const float *ro, const float *rd, const float *p4);
static float ray_intersects_triangle(const float *ro, const float *rd, const float *p1, const float *p2, const float *p3);
static int ray_intersects_sphere(float *t0, float *t1, const float *ro, const float *rd, const float *c, float r);
static int ray_intersects_aabb(float *t0, float *t1, const float *ro, const float *rd, const float *min, const float *max);
/* sphere */
static void sphere_closest_point_to_point(float *res, const float *c, const float r, const float *p);
static int sphere_intersects_sphere(const float *ca, float ra, const float *cb, float rb);
static int sphere_intersects_sphere_manifold(struct manifold *m, const float *ca, float ra, const float *cb, float rb);
static int sphere_intersect_aabb(const float *c, float r, const float *min, const float *max);
static int sphere_intersect_aabb_manifold(struct manifold *m, const float *c, float r, const float *min, const float *max);
static int sphere_intersect_capsule(const float *sc, float sr, const float *ca, const float *cb, float cr);
static int sphere_intersect_capsule_manifold(struct manifold *m, const float *sc, float sr, const float *ca, const float *cb, float cr);
static int sphere_intersecting_polyhedron(const float *c, float r, const float *verts, int cnt);
/* aabb */
static void aabb_rebalance_transform(float *bmin, float *bmax, const float *amin, const float *amax, const float *m, const float t[3]);
static void aabb_closest_point_to_point(float *res, const float *min, const float *max, const float *p);
static float aabb_sqdist_to_point(const float *min, const float *max, const float *p);
static int aabb_contains_point(const float *amin, const float *amax, const float *p);
static int aabb_intersect_aabb(const float *amin, const float *amax, const float *bmin, const float *bmax);
static int aabb_intersect_aabb_manifold(struct manifold *m, const float *amin, const float *amax, const float *bmin, const float *bmax);
static int aabb_intersect_sphere(const float *min, const float *max, const float *c, float r);
static int aabb_intersect_sphere_manifold(struct manifold *m, const float *min, const float *max, const float *c, float r);
static int aabb_intersect_capsule(const float *min, const float *max, const float *ca, const float *cb, float cr);
static int aabb_intersect_capsule_manifold(struct manifold *m, const float *min, const float *max, const float *ca, const float *cb, float cr);
static int aabb_intersect_polyhedron(const float *min, const float *max, const float *verts, int cnt);
/* capsule */
static float capsule_point_sqdist(const float *ca, const float *cb, float cr, const float *p);
static void capsule_closest_point_to_point(float *res, const float *ca, const float *cb, float cr, const float *p);
static int capsule_intersect_capsule(const float *aa, const float *ab, float ar, const float *ba, const float *bb, float br);
static int capsule_intersect_capsule_manifold(struct manifold *m, const float *aa, const float *ab, float ar, const float *ba, const float *bb, float br);
static int capsule_intersect_sphere(const float *ca, const float *cb, float cr, const float *sc, float sr);
static int capsule_intersect_sphere_manifold(struct manifold *m, const float *ca, const float *cb, float cr, const float *sc, float sr);
static int capsule_intersect_aabb(const float *ca, const float *cb, float cr, const float *min, const float *max);
static int capsule_intersect_aabb_manifold(struct manifold *m, const float *ca, const float *cb, float cr, const float *min, const float *max);
static int capsule_intersect_polyhedron(const float *ca, const float *cb, float cr, const float *verts, int cnt);
/* polyhedron: query */
static int polyhedron_is_intersecting_sphere(const float *verts, int cnt, const float *c, float r);
static int polyhedron_is_intersecting_aabb(const float *verts, int cnt, const float *min, const float *max);
static int polyhedron_is_intersecting_capsule(const float *verts, int cnt, const float *ca, const float *cb, float cr);
static int polyhedron_is_intersecting_polyhedron(const float *averts, int acnt, const float *bverts, int bcnt);
/* polyhedron: query transformed */
static int polyhedron_is_intersecting_sphere_transform(const float *verts, int cnt, const float *pos3, const float *rot33, const float *sc, float sr);
static int polyhedron_is_intersecting_aabb_transform(const float *verts, int cnt, const float *apos3, const float *arot33, const float *min, const float *max);
static int polyhedron_is_intersecting_capsule_transform(const float *verts, int cnt, const float *pos3, const float *rot33, const float *ca, const float *cb, float cr);
static int polyhedron_is_intersecting_polyhedron_transform(const float *averts, int acnt, const float *apos3, const float *arot33, const float *bverts, int bcnt, const float *bpos3, const float *brot33);
/* polyhedron: gjk result */
static int polyhedron_intersect_sphere(struct gjk_result *res, const float *verts, int cnt, const float *sc, float sr);
static int polyhedron_intersect_aabb(struct gjk_result *res, const float *verts, int cnt, const float *min, const float *max);
static int polyhedron_intersect_capsule(struct gjk_result *res, const float *verts, int cnt, const float *ca, const float *cb, float cr);
static int polyhedron_intersect_polyhedron(struct gjk_result *res, const float *averts, int acnt, const float *bverts, int bcnt);
/* polyhedron: gjk result transformed */
static int polyhedron_intersect_sphere_transform(struct gjk_result *res, const float *verts, int cnt, const float *pos3, const float *rot33, const float *sc, float sr);
static int polyhedron_intersect_aabb_transform(struct gjk_result *res, const float *verts, int cnt, const float *pos3, const float *rot33, const float *min, const float *max);
static int polyhedron_intersect_capsule_transform(struct gjk_result *res, const float *verts, int cnt, const float *pos3, const float *rot33, const float *ca, const float *cb, float cr);
static int polyhedron_intersect_polyhedron_transform(struct gjk_result *res, const float *averts, int acnt, const float *at3, const float *ar33, const float *bverts, int bcnt, const float *bt3, const float *br33);
static void
planeq(float *r, const float *n, const float *p)
{
f3cpy(r,n);
r[3] = -f3dot(n,p);
}
static void
planeqf(float *r, float nx, float ny, float nz, float px, float py, float pz)
{
/* Plane: ax + by + cz + d
* Equation:
* n * (p - p0) = 0
* n * p - n * p0 = 0
* |a b c| * p - |a b c| * p0
*
* |a b c| * p + d = 0
* d = -1 * |a b c| * p0
*
* Plane: |a b c d| d = -|a b c| * p0
*/
float n[3], p[3];
f3set(n,nx,ny,nz);
f3set(p,px,py,pz);
planeq(r, n, p);
}
static void
segment_closest_point_to_point(float *res,
const float *a, const float *b, const float *p)
{
float ab[3], pa[3];
f3sub(ab, b,a);
f3sub(pa, p,a);
float t = f3dot(pa,ab) / f3dot(ab,ab);
if (t < 0.0f) t = 0.0f;
if (t > 1.0f) t = 1.0f;
f3mul(res, ab, t);
f3add(res, a, res);
}
static float
segment_closest_point_to_point_sqdist(const float *a, const float *b, const float *p)
{
float ab[3], ap[3], bp[3];
f3sub(ab,a,b);
f3sub(ap,a,p);
f3sub(bp,a,p);
float e = f3dot(ap,ab);
/* handle cases p proj outside ab */
if (e <= 0.0f) return f3dot(ap,ap);
float f = f3dot(ab,ab);
if (e >= f) return f3dot(bp,bp);
return f3dot(ap,ap) - (e*e)/f;
}
static float
segment_closest_point_to_segment(float *t1, float *t2, float *c1, float *c2,
const float *p1, const float *q1, const float *p2, const float *q2)
{
#define EPSILON (1e-6)
float r[3], d1[3], d2[3];
f3sub(d1, q1, p1); /* direction vector segment s1 */
f3sub(d2, q2, p2); /* direction vector segment s2 */
f3sub(r, p1, p2);
float a = f3dot(d1, d1);
float e = f3dot(d2, d2);
float f = f3dot(d2, r);
if (a <= EPSILON && e <= EPSILON) {
/* both segments degenerate into points */
float d12[3];
*t1 = *t2 = 0.0f;
f3cpy(c1, p1);
f3cpy(c2, p2);
f3sub(d12, c1, c2);
return f3dot(d12,d12);
}
if (a > EPSILON) {
float c = f3dot(d1,r);
if (e > EPSILON) {
/* non-degenerate case */
float b = f3dot(d1,d2);
float denom = a*e - b*b;
/* compute closest point on L1/L2 if not parallel else pick any t2 */
if (denom != 0.0f)
*t1 = clamp(0.0f, (b*f - c*e) / denom, 1.0f);
else *t1 = 0.0f;
/* cmpute point on L2 closest to S1(s) */
*t2 = (b*(*t1) + f) / e;
if (*t2 < 0.0f) {
*t2 = 0.0f;
*t1 = clamp(0.0f, -c/a, 1.0f);
} else if (*t2 > 1.0f) {
*t2 = 1.0f;
*t1 = clamp(0.0f, (b-c)/a, 1.0f);
}
} else {
/* second segment degenerates into a point */
*t1 = clamp(0.0f, -c/a, 1.0f);
*t2 = 0.0f;
}
} else {
/* first segment degenerates into a point */
*t2 = clamp(0.0f, f / e, 1.0f);
*t1 = 0.0f;
}
/* calculate closest points */
float n[3], d12[3];
f3mul(n, d1, *t1);
f3add(c1, p1, n);
f3mul(n, d2, *t2);
f3add(c2, p2, n);
/* calculate squared distance */
f3sub(d12, c1, c2);
return f3dot(d12,d12);
}
static float
ray_intersects_plane(const float *ro, const float *rd,
const float *plane)
{
/* Ray: P = origin + rd * t
* Plane: plane_normal * P + d = 0
*
* Substitute:
* normal * (origin + rd*t) + d = 0
*
* Solve for t:
* plane_normal * origin + plane_normal * rd*t + d = 0
* -(plane_normal*rd*t) = plane_normal * origin + d
*
* plane_normal * origin + d
* t = -1 * -------------------------
* plane_normal * rd
*
* Result:
* Behind: t < 0
* Infront: t >= 0
* Parallel: t = 0
* Intersection point: ro + rd * t
*/
float n = -(f3dot(plane,ro) + plane[3]);
if (fabs(n) < 0.0001f) return 0.0f;
return n/(f3dot(plane,rd));
}
static float
ray_intersects_triangle(const float *ro, const float *rd,
const float *p0, const float *p1, const float *p2)
{
float p[4];
float t = 0;
float di0[3], di1[3], di2[3];
float d21[3], d02[3], in[3];
float n[3], d10[3], d20[3];
float in0[3], in1[3], in2[3];
/* calculate triangle normal */
f3sub(d10, p1,p0);
f3sub(d20, p2,p0);
f3sub(d21, p2,p1);
f3sub(d02, p0,p2);
f3cross(n, d10,d20);
/* check for plane intersection */
planeq(p, n, p0);
t = ray_intersects_plane(ro, rd, p);
if (t <= 0.0f) return t;
/* intersection point */
f3mul(in,rd,t);
f3add(in,in,ro);
/* check if point inside triangle in plane */
f3sub(di0, in, p0);
f3sub(di1, in, p1);
f3sub(di2, in, p2);
f3cross(in0, d10, di0);
f3cross(in1, d21, di1);
f3cross(in2, d02, di2);
if (f3dot(in0,n) < 0.0f)
return -1;
if (f3dot(in1,n) < 0.0f)
return -1;
if (f3dot(in2,n) < 0.0f)
return -1;
return t;
}
static int
ray_intersects_sphere(float *t0, float *t1,
const float *ro, const float *rd,
const float *c, float r)
{
float a[3];
float tc,td,d2,r2;
f3sub(a,c,ro);
tc = f3dot(rd,a);
if (tc < 0) return 0;
r2 = r*r;
d2 = f3dot(a,a) - tc*tc;
if (d2 > r2) return 0;
td = sqrtf(r2 - d2);
*t0 = tc - td;
*t1 = tc + td;
return 1;
}
static int
ray_intersects_aabb(float *t0, float *t1,
const float *ro, const float *rd,
const float *min, const float *max)
{
float t0x = (min[0] - ro[0]) / rd[0];
float t0y = (min[1] - ro[1]) / rd[1];
float t0z = (min[2] - ro[2]) / rd[2];
float t1x = (max[0] - ro[0]) / rd[0];
float t1y = (max[1] - ro[1]) / rd[1];
float t1z = (max[2] - ro[2]) / rd[2];
float tminx = min(t0x, t1x);
float tminy = min(t0y, t1y);
float tminz = min(t0z, t1z);
float tmaxx = max(t0x, t1x);
float tmaxy = max(t0y, t1y);
float tmaxz = max(t0z, t1z);
if (tminx > tmaxy || tminy > tmaxx)
return 0;
*t0 = max(tminx, tminy);
*t1 = min(tmaxy, tmaxx);
if (*t0 > tmaxz || tminz> *t1)
return 0;
*t0 = max(*t0, tminz);
*t1 = min(*t1, tmaxz);
return 1;
}
static void
sphere_closest_point_to_point(float *res,
const float *c, const float r,
const float *p)
{
float d[3], n[3];
f3sub(d, p, c);
f3norm(d);
f3mul(res,n,r);
f3add(res,c,res);
}
static int
sphere_intersects_sphere(const float *ca, float ra,
const float *cb, float rb)
{
float d[3];
f3sub(d, cb, ca);
float r = ra + rb;
if (f3dot(d,d) > r*r)
return 0;
return 1;
}
static int
sphere_intersects_sphere_manifold(struct manifold *m,
const float *ca, float ra, const float *cb, float rb)
{
float d[3];
f3sub(d, cb, ca);
float r = ra + rb;
float d2 = f3dot(d,d);
if (d2 <= r*r) {
float l = sqrtf(d2);
float linv = 1.0f / ((l != 0) ? l: 1.0f);
f3mul(m->normal, d, linv);
m->depth = r - l;
f3mul(d, m->normal, rb);
f3sub(m->contact_point, cb, d);
return 1;
} return 0;
}
static int
sphere_intersect_aabb(const float *c, float r,
const float *min, const float *max)
{
return aabb_intersect_sphere(min, max, c, r);
}
static int
sphere_intersect_aabb_manifold(struct manifold *m,
const float *c, float r, const float *min, const float *max)
{
/* find closest aabb point to sphere center point */
float ap[3], d[3];
aabb_closest_point_to_point(ap, min, max, c);
f3sub(d, c, ap);
float d2 = f3dot(d, d);
if (d2 > r*r) return 0;
/* calculate distance vector between sphere and aabb center points */
float ac[3];
f3sub(ac, max, min);
f3mul(ac, ac, 0.5f);
f3add(ac, min, ac);
f3sub(d, ac, c);
/* normalize distance vector */
float l2 = f3dot(d,d);
float l = l2 != 0.0f ? sqrtf(l2): 1.0f;
float linv = 1.0f/l;
f3mul(d, d, linv);
f3cpy(m->normal, d);
f3mul(m->contact_point, m->normal, r);
f3add(m->contact_point, c, m->contact_point);
/* calculate penetration depth */
float sp[3];
sphere_closest_point_to_point(sp, c, r, ap);
f3sub(d, sp, ap);
m->depth = sqrtf(f3dot(d,d)) - l;
return 1;
}
static int
sphere_intersect_capsule(const float *sc, float sr,
const float *ca, const float *cb, float cr)
{
return capsule_intersect_sphere(ca, cb, cr, sc, sr);
}
static int
sphere_intersect_capsule_manifold(struct manifold *m,
const float *sc, float sr, const float *ca, const float *cb, float cr)
{
/* find closest capsule point to sphere center point */
float cp[3];
capsule_closest_point_to_point(cp, ca, cb, cr, sc);
f3sub(m->normal, cp, sc);
float d2 = f3dot(m->normal, m->normal);
if (d2 > sr*sr) return 0;
/* normalize manifold normal vector */
float l = d2 != 0.0f ? sqrtf(d2): 1;
float linv = 1.0f/l;
f3mul(m->normal, m->normal, linv);
/* calculate penetration depth */
f3mul(m->contact_point, m->normal, sr);
f3add(m->contact_point, sc, m->contact_point);
m->depth = d2 - sr*sr;
m->depth = m->depth != 0.0f ? sqrtf(m->depth): 0.0f;
return 1;
}
static int
sphere_intersecting_polyhedron(const float *c, float r,
const float *verts, int cnt)
{
return polyhedron_is_intersecting_sphere(verts, cnt, c, r);
}
static void
aabb_rebalance_transform(float *bmin, float *bmax,
const float *amin, const float *amax,
const float *m, const float *t)
{
for (int i = 0; i < 3; ++i) {
bmin[i] = bmax[i] = t[i];
for (int j = 0; j < 3; ++j) {
float e = m[i*3+j] * amin[j];
float f = m[i*3+j] * amax[j];
if (e < f) {
bmin[i] += e;
bmax[i] += f;
} else {
bmin[i] += f;
bmax[i] += e;
}
}
}
}
static void
aabb_closest_point_to_point(float *res,
const float *min, const float *max,
const float *p)
{
for (int i = 0; i < 3; ++i) {
float v = p[i];
if (v < min[i]) v = min[i];
if (v > max[i]) v = max[i];
res[i] = v;
}
}
static float
aabb_sqdist_to_point(const float *min, const float *max, const float *p)
{
float r = 0;
for (int i = 0; i < 3; ++i) {
float v = p[i];
if (v < min[i]) r += (min[i]-v) * (min[i]-v);
if (v > max[i]) r += (v-max[i]) * (v-max[i]);
} return r;
}
static int
aabb_contains_point(const float *amin, const float *amax, const float *p)
{
if (p[0] < amin[0] || p[0] > amax[0]) return 0;
if (p[1] < amin[1] || p[1] > amax[1]) return 0;
if (p[2] < amin[2] || p[2] > amax[2]) return 0;
return 1;
}
static int
aabb_intersect_aabb(const float *amin, const float *amax,
const float *bmin, const float *bmax)
{
if (amax[0] < bmin[0] || amin[0] > bmax[0]) return 0;
if (amax[1] < bmin[1] || amin[1] > bmax[1]) return 0;
if (amax[2] < bmin[2] || amin[2] > bmax[2]) return 0;
return 1;
}
static int
aabb_intersect_aabb_manifold(struct manifold *m,
const float *amin, const float *amax,
const float *bmin, const float *bmax)
{
if (!aabb_intersect_aabb(amin, amax, bmin, bmax))
return 0;
/* calculate distance vector between both aabb center points */
float ac[3], bc[3], d[3];
f3sub(ac, amax, amin);
f3sub(bc, bmax, bmin);
f3mul(ac, ac, 0.5f);
f3mul(bc, bc, 0.5f);
f3add(ac, amin, ac);
f3add(bc, bmin, bc);
f3sub(d, bc, ac);
/* normalize distance vector */
float l2 = f3dot(d,d);
float l = l2 != 0.0f ? sqrtf(l2): 1.0f;
float linv = 1.0f/l;
f3mul(d, d, linv);
/* calculate contact point */
f3cpy(m->normal, d);
aabb_closest_point_to_point(m->contact_point, amin, amax, bc);
f3sub(d, m->contact_point, ac);
/* calculate penetration depth */
float r2 = f3dot(d,d);
float r = sqrtf(r2);
m->depth = r - l;
return 1;
}
static int
aabb_intersect_sphere(const float *min, const float *max,
const float *c, float r)
{
/* compute squared distance between sphere center and aabb */
float d2 = aabb_sqdist_to_point(min, max, c);
/* intersection if distance is smaller/equal sphere radius*/
return d2 <= r*r;
}
static int
aabb_intersect_sphere_manifold(struct manifold *m,
const float *min, const float *max,
const float *c, float r)
{
/* find closest aabb point to sphere center point */
float d[3];
aabb_closest_point_to_point(m->contact_point, min, max, c);
f3sub(d, c, m->contact_point);
float d2 = f3dot(d, d);
if (d2 > r*r) return 0;
/* calculate distance vector between aabb and sphere center points */
float ac[3];
f3sub(ac, max, min);
f3mul(ac, ac, 0.5f);
f3add(ac, min, ac);
f3sub(d, c, ac);
/* normalize distance vector */
float l2 = f3dot(d,d);
float l = l2 != 0.0f ? sqrtf(l2): 1.0f;
float linv = 1.0f/l;
f3mul(d, d, linv);
/* calculate penetration depth */
f3cpy(m->normal, d);
f3sub(d, m->contact_point, ac);
m->depth = sqrtf(f3dot(d,d));
return 1;
}
static int
aabb_intersect_capsule(const float *min, const float *max,
const float *ca, const float *cb, float cr)
{
return capsule_intersect_aabb(ca, cb, cr, min, max);
}
static int
aabb_intersect_capsule_manifold(struct manifold *m,
const float *min, const float *max,
const float *ca, const float *cb, float cr)
{
/* calculate aabb center point */
float ac[3];
f3sub(ac, max, min);
f3mul(ac, ac, 0.5f);
f3add(ac, min, ac);
/* calculate closest point from aabb to point on capsule and check if inside aabb */
float cp[3];
capsule_closest_point_to_point(cp, ca, cb, cr, ac);
if (!aabb_contains_point(min, max, cp))
return 0;
/* vector and distance between both capsule closests point and aabb center*/
float d[3], d2;
f3sub(d, cp, ac);
d2 = f3dot(d,d);
/* calculate penetration depth from closest aabb point to capsule */
float ap[3], dt[3];
aabb_closest_point_to_point(ap, min, max, cp);
f3sub(dt, ap, cp);
m->depth = sqrtf(f3dot(dt,dt));
/* calculate normal */
float l = sqrtf(d2);
float linv = 1.0f / ((l != 0.0f) ? l: 1.0f);
f3mul(m->normal, d, linv);
f3cpy(m->contact_point, ap);
return 1;
}
static int
aabb_intersect_polyhedron(const float *min, const float *max,
const float *verts, int cnt)
{
return polyhedron_is_intersecting_aabb(verts, cnt, min, max);
}
static float
capsule_point_sqdist(const float *ca, const float *cb, float cr, const float *p)
{
float d2 = segment_closest_point_to_point_sqdist(ca, cb, p);
return d2 - (cr*cr);
}
static void
capsule_closest_point_to_point(float *res,
const float *ca, const float *cb, float cr, const float *p)
{
/* calculate closest point to internal capsule segment */
float pp[3], d[3];
segment_closest_point_to_point(pp, ca, cb, p);
/* extend point out by radius in normal direction */
f3sub(d,p,pp);
f3norm(d);
f3mul(res, d, cr);
f3add(res, pp, res);
}
static int
capsule_intersect_capsule(const float *aa, const float *ab, float ar,
const float *ba, const float *bb, float br)
{
float t1, t2;
float c1[3], c2[3];
float d2 = segment_closest_point_to_segment(&t1, &t2, c1, c2, aa, ab, ba, bb);
float r = ar + br;
return d2 <= r*r;
}
static int
capsule_intersect_capsule_manifold(struct manifold *m,
const float *aa, const float *ab, float ar,
const float *ba, const float *bb, float br)
{
float t1, t2;
float c1[3], c2[3];
float d2 = segment_closest_point_to_segment(&t1, &t2, c1, c2, aa, ab, ba, bb);
float r = ar + br;
if (d2 > r*r) return 0;
/* calculate normal from both closest points for each segement */
float cp[3], d[3];
f3sub(m->normal, c2, c1);
f3norm(m->normal);
/* calculate contact point from closest point and depth */
capsule_closest_point_to_point(m->contact_point, aa, ab, ar, c2);
capsule_closest_point_to_point(cp, ba, bb, br, c1);
f3sub(d, c1, cp);
m->depth = sqrtf(f3dot(d,d));
return 1;
}
static int
capsule_intersect_sphere(const float *ca, const float *cb, float cr,
const float *sc, float sr)
{
/* squared distance bwetween sphere center and capsule line segment */
float d2 = segment_closest_point_to_point_sqdist(ca,cb,sc);
float r = sr + cr;
return d2 <= r * r;
}
static int
capsule_intersect_sphere_manifold(struct manifold *m,
const float *ca, const float *cb, float cr,
const float *sc, float sr)
{
/* find closest capsule point to sphere center point */
capsule_closest_point_to_point(m->contact_point, ca, cb, cr, sc);
f3sub(m->normal, sc, m->contact_point);
float d2 = f3dot(m->normal, m->normal);
if (d2 > sr*sr) return 0;
/* normalize manifold normal vector */
float l = d2 != 0.0f ? sqrtf(d2): 1;
float linv = 1.0f/l;
f3mul(m->normal, m->normal, linv);
/* calculate penetration depth */
m->depth = d2 - sr*sr;
m->depth = m->depth != 0.0f ? sqrtf(m->depth): 0.0f;
return 1;
}
static int
capsule_intersect_aabb(const float *ca, const float *cb, float cr,
const float *min, const float *max)
{
/* calculate aabb center point */
float ac[3];
f3sub(ac, max, min);
f3mul(ac, ac, 0.5f);
/* calculate closest point from aabb to point on capsule and check if inside aabb */
float p[3];
capsule_closest_point_to_point(p, ca, cb, cr, ac);
return aabb_contains_point(min, max, p);
}
static int
capsule_intersect_aabb_manifold(struct manifold *m,
const float *ca, const float *cb, float cr,
const float *min, const float *max)
{
/* calculate aabb center point */
float ac[3];
f3sub(ac, max, min);
f3mul(ac, ac, 0.5f);
f3add(ac, min, ac);
/* calculate closest point from aabb to point on capsule and check if inside aabb */
float cp[3];
capsule_closest_point_to_point(cp, ca, cb, cr, ac);
if (!aabb_contains_point(min, max, cp))
return 0;
/* vector and distance between both capsule closests point and aabb center*/
float d[3], d2;
f3sub(d, ac, cp);
d2 = f3dot(d,d);
/* calculate penetration depth from closest aabb point to capsule */
float ap[3], dt[3];
aabb_closest_point_to_point(ap, min, max, cp);
f3sub(dt, ap, cp);
m->depth = sqrtf(f3dot(dt,dt));
/* calculate normal */
float l = sqrtf(d2);
float linv = 1.0f / ((l != 0.0f) ? l: 1.0f);
f3mul(m->normal, d, linv);
f3cpy(m->contact_point, cp);
return 1;
}
static int
capsule_intersect_polyhedron(const float *ca, const float *cb, float cr,
const float *verts, int cnt)
{
return polyhedron_is_intersecting_capsule(verts, cnt, ca, cb, cr);
}
static int
line_support(float *support, const float *d,
const float *a, const float *b)
{
int i = 0;
float adot = f3dot(a, d);
float bdot = f3dot(b, d);
if (adot < bdot) {
f3cpy(support, b);
i = 1;
} else f3cpy(support, a);
return i;
}
static int
polyhedron_support(float *support, const float *d,
const float *verts, int cnt)
{
int imax = 0;
float dmax = f3dot(verts, d);
for (int i = 1; i < cnt; ++i) {
/* find vertex with max dot product in direction d */
float dot = f3dot(&verts[i*3], d);
if (dot < dmax) continue;
imax = i, dmax = dot;
} f3cpy(support, &verts[imax*3]);
return imax;
}
static int
polyhedron_intersect_sphere(struct gjk_result *res,
const float *verts, int cnt,
const float *sc, float sr)
{
/* initial guess */
float d[3] = {0};
struct gjk_support s = {0};
f3cpy(s.a, verts);
f3cpy(s.b, sc);
f3sub(d, s.b, s.a);
/* run gjk algorithm */
struct gjk_simplex gsx = {0};
while (gjk(&gsx, &s, d)) {
float n[3]; f3mul(n, d, -1);
s.aid = polyhedron_support(s.a, n, verts, cnt);
f3sub(d, s.b, s.a);
}
/* check distance between closest points */
*res = gjk_analyze(&gsx);
return res->distance_squared <= sr*sr;
}
static int
polyhedron_intersect_sphere_transform(struct gjk_result *res,
const float *verts, int cnt, const float *pos3, const float *rot33,
const float *sc, float sr)
{
/* initial guess */
float d[3] = {0};
struct gjk_support s = {0};
f3cpy(s.a, verts);
f3cpy(s.b, sc);
transformS(s.a, rot33, pos3);
f3sub(d, s.b, s.a);
/* run gjk algorithm */
struct gjk_simplex gsx = {0};
while (gjk(&gsx, &s, d)) {
float n[3]; f3mul(n, d, -1);
float da[3]; transformT(da, n, rot33, pos3);
s.aid = polyhedron_support(s.a, da, verts, cnt);
transformS(s.a, rot33, pos3);
f3sub(d, s.b, s.a);
}
/* check distance between closest points */
*res = gjk_analyze(&gsx);
return res->distance_squared <= sr*sr;
}
static int
polyhedron_is_intersecting_sphere(const float *verts, int cnt,
const float *sc, float sr)
{
struct gjk_result res;
return polyhedron_intersect_sphere(&res, verts, cnt, sc, sr);
}
static int
polyhedron_is_intersecting_sphere_transform(const float *verts, int cnt, const float *pos3,
const float *rot33, const float *sc, float sr)
{
struct gjk_result res;
return polyhedron_intersect_sphere_transform(&res, verts, cnt, pos3, rot33, sc, sr);
}
static int
polyhedron_intersect_capsule(struct gjk_result *res,
const float *verts, int cnt,
const float *ca, const float *cb, float cr)
{
/* initial guess */
float d[3] = {0};
struct gjk_support s = {0};
f3cpy(s.a, verts);
f3cpy(s.b, ca);
f3sub(d, s.b, s.a);
/* run gjk algorithm */
struct gjk_simplex gsx = {0};
while (gjk(&gsx, &s, d)) {
float n[3]; f3mul(n, d, -1);
s.aid = polyhedron_support(s.a, n, verts, cnt);
s.bid = line_support(s.b, d, ca, cb);
f3sub(d, s.b, s.a);
}
/* check distance between closest points */
assert(gsx.iter < gsx.max_iter);
*res = gjk_analyze(&gsx);
return res->distance_squared <= cr*cr;
}
static int
polyhedron_is_intersecting_capsule(const float *verts, int cnt,
const float *ca, const float *cb, float cr)
{
struct gjk_result res;
return polyhedron_intersect_capsule(&res, verts, cnt, ca, cb, cr);
}
static int
polyhedron_intersect_capsule_transform(struct gjk_result *res,
const float *verts, int cnt, const float *pos3, const float *rot33,
const float *ca, const float *cb, float cr)
{
/* initial guess */
float d[3] = {0};
struct gjk_support s = {0};
f3cpy(s.a, verts);
f3cpy(s.b, ca);
transformS(s.a, rot33, pos3);
f3sub(d, s.b, s.a);
/* run gjk algorithm */
struct gjk_simplex gsx = {0};
while (gjk(&gsx, &s, d)) {
float n[3]; f3mul(n, d, -1);
float da[3]; transformT(da, n, rot33, pos3);
s.aid = polyhedron_support(s.a, da, verts, cnt);
s.bid = line_support(s.b, d, ca, cb);
transformS(s.a, rot33, pos3);
f3sub(d, s.b, s.a);
}
/* check distance between closest points */
*res = gjk_analyze(&gsx);
return res->distance_squared <= cr*cr;
}
static int
polyhedron_is_intersecting_capsule_transform(const float *verts, int cnt,
const float *pos3, const float *rot33,
const float *ca, const float *cb, float cr)
{
struct gjk_result res;
return polyhedron_intersect_capsule_transform(&res, verts, cnt, pos3, rot33, ca, cb, cr);
}
static int
polyhedron_intersect_polyhedron_transform(struct gjk_result *res,
const float *averts, int acnt, const float *at3, const float *ar33,
const float *bverts, int bcnt, const float *bt3, const float *br33)
{
/* initial guess */
float d[3] = {0};
struct gjk_support s = {0};
f3cpy(s.a, averts);
f3cpy(s.b, bverts);
transformS(s.a, ar33, at3);
transformS(s.b, br33, bt3);
f3sub(d, s.b, s.a);
/* run gjk algorithm */
struct gjk_simplex gsx = {0};
while (gjk(&gsx, &s, d)) {
/* transform direction */
float n[3]; f3mul(n, d, -1);
float da[3]; transformT(da, n, ar33, at3);
float db[3]; transformT(db, d, br33, bt3);
/* run support function on tranformed directions */
s.aid = polyhedron_support(s.a, da, averts, acnt);
s.bid = polyhedron_support(s.b, db, bverts, bcnt);
/* calculate distance vector on transformed points */
transformS(s.a, ar33, at3);
transformS(s.b, br33, bt3);
f3sub(d, s.b, s.a);
}
*res = gjk_analyze(&gsx);
return gsx.hit;
}
static int
polyhedron_intersect_polyhedron(struct gjk_result *res,
const float *averts, int acnt,
const float *bverts, int bcnt)
{
/* initial guess */
float d[3] = {0};
struct gjk_support s = {0};
f3cpy(s.a, averts);
f3cpy(s.b, bverts);
f3sub(d, s.b, s.a);
/* run gjk algorithm */
struct gjk_simplex gsx = {0};
while (gjk(&gsx, &s, d)) {
float n[3]; f3mul(n, d, -1);
s.aid = polyhedron_support(s.a, n, averts, acnt);
s.bid = polyhedron_support(s.b, d, bverts, bcnt);
f3sub(d, s.b, s.a);
}
*res = gjk_analyze(&gsx);
return gsx.hit;
}
static int
polyhedron_is_intersecting_polyhedron(const float *averts, int acnt,
const float *bverts, int bcnt)
{
struct gjk_result res;
return polyhedron_intersect_polyhedron(&res, averts, acnt, bverts, bcnt);
}
static int
polyhedron_is_intersecting_polyhedron_transform(const float *averts, int acnt,
const float *apos3, const float *arot33,
const float *bverts, int bcnt,
const float *bpos3, const float *brot33)
{
struct gjk_result res;
return polyhedron_intersect_polyhedron_transform(&res, averts, acnt,
apos3, arot33, bverts, bcnt, bpos3, brot33);
}
static int
polyhedron_intersect_aabb(struct gjk_result *res,
const float *verts, int cnt,
const float *min, const float *max)
{
float box[24];
f3set(box+0, min[0], min[1], min[2]),
f3set(box+3, min[0], min[1], max[2]);
f3set(box+6, min[0], max[1], min[2]);
f3set(box+9, min[0], max[1], max[2]);
f3set(box+12,max[0], min[1], min[2]);
f3set(box+15,max[0], min[1], max[2]);
f3set(box+18,max[0], max[1], min[2]);
f3set(box+21,max[0], max[1], max[2]);
return polyhedron_intersect_polyhedron(res, verts, cnt, box, 8);
}
static int
polyhedron_intersect_aabb_transform(struct gjk_result *res,
const float *verts, int cnt, const float *pos3, const float *rot33,
const float *min, const float *max)
{
float box[24];
static const float zero[3];
static const float id[3][3] = {{1,0,0},{0,1,0},{0,0,1}};
f3set(box+0, min[0], min[1], min[2]),
f3set(box+3, min[0], min[1], max[2]);
f3set(box+6, min[0], max[1], min[2]);
f3set(box+9, min[0], max[1], max[2]);
f3set(box+12,max[0], min[1], min[2]);
f3set(box+15,max[0], min[1], max[2]);
f3set(box+18,max[0], max[1], min[2]);
f3set(box+21,max[0], max[1], max[2]);
return polyhedron_intersect_polyhedron_transform(res, verts, cnt, pos3, rot33,
box, 8, zero, &id[0][0]);
}
static int
polyhedron_is_intersecting_aabb(const float *verts, int cnt,
const float *min, const float *max)
{
struct gjk_result res;
return polyhedron_intersect_aabb(&res, verts, cnt, min, max);
}
static int
polyhedron_is_intersecting_aabb_transform(const float *verts, int cnt,
const float *apos3, const float *arot33, const float *min, const float *max)
{
struct gjk_result res;
return polyhedron_intersect_aabb_transform(&res, verts, cnt, apos3, arot33, min, max);
}
/* ============================================================================
*
* MATH OBJECTS
*
* =========================================================================== */
#define vf(v) (&((v).x))
typedef struct v2 {float x,y;} v2;
typedef struct v3 {float x,y,z;} v3;
typedef struct v4 {float x,y,z,w;} v4;
typedef struct quat {float x,y,z,w;} quat;
typedef struct mat4 {v4 x,y,z;} mat3;
#define v3unpack(v) (v).x,(v).y,(v).z
static inline v3 v3mk(float x, float y, float z){v3 r; f3set(&r.x, x,y,z); return r;}
static inline v3 v3mkv(const float *v){v3 r; f3set(&r.x, v[0],v[1],v[2]); return r;}
static inline v3 v3add(v3 a, v3 b){f3add(vf(a),vf(a),vf(b));return a;}
static inline v3 v3sub(v3 a, v3 b){f3sub(vf(a),vf(a),vf(b)); return a;}
static inline v3 v3scale(v3 v, float s){f3mul(vf(v),vf(v),s); return v;}
static inline float v3dot(v3 a, v3 b){return f3dot(vf(a),vf(b));}
static inline v3 v3norm(v3 v) {f3norm(vf(v)); return v;}
static inline v3 v3cross(v3 a, v3 b) {v3 r; f3cross(vf(r),vf(a),vf(b)); return r;}
static inline v3 v3lerp(v3 a, float t, v3 b) {v3 r; f3lerp(vf(r),vf(a),t,vf(b)); return r;}
/* ============================================================================
*
* COLLISION VOLUME
*
* =========================================================================== */
struct sphere {v3 p; float r;};
struct aabb {v3 min, max;};
struct plane {v3 p, n;};
struct capsule {v3 a,b; float r;};
struct ray {v3 p, d;};
struct raycast {v3 p, n; float t0, t1;};
/* plane */
static struct plane plane(v3 p, v3 n);
static struct plane planef(float px, float py, float pz, float nx, float ny, float nz);
static struct plane planefv(float px, float py, float pz, float nx, float ny, float nz);
/* ray */
static struct ray ray(v3 p, v3 d);
static struct ray rayf(float px, float py, float pz, float dx, float dy, float dz);
static struct ray rayfv(const float *p, const float *d);
static int ray_test_plane(struct raycast *o, struct ray r, struct plane p);
static int ray_test_triangle(struct raycast *o, struct ray r, const struct v3 *tri);
static int ray_test_sphere(struct raycast *o, struct ray r, struct sphere s);
static int ray_test_aabb(struct raycast *o, struct ray r, struct aabb a);
/* sphere */
static struct sphere sphere(v3 p, float r);
static struct sphere spheref(float cx, float cy, float cz, float r);
static struct sphere spherefv(const float *p, float r);
static int sphere_test_sphere(struct sphere a, struct sphere b);
static int sphere_test_sphere_manifold(struct manifold *m, struct sphere a, struct sphere b);
static int sphere_test_aabb(struct sphere s, struct aabb a);
static int sphere_test_aabb_manifold(struct manifold *m, struct sphere s, struct aabb a);
static int sphere_test_capsule(struct sphere s, struct capsule c);
static int sphere_test_capsule_manifold(struct manifold *m, struct sphere s, struct capsule c);
/* aabb */
static struct aabb aabb(v3 min, v3 max);
static struct aabb aabbf(float minx, float miny, float minz, float maxx, float maxy, float maxz);
static struct aabb aabbfv(const float *min, const float *max);
static struct aabb aabb_transform(struct aabb a, const float *rot, const v3 t);
static v3 aabb_closest_point(struct aabb, v3 p);
static int aabb_test_aabb(struct aabb, struct aabb);
static int aabb_test_aabb_manifold(struct manifold *m, struct aabb, struct aabb);
static int aabb_test_sphere(struct aabb a, struct sphere s);
static int aabb_test_sphere_manifold(struct manifold *m,struct aabb a, struct sphere s);
static int aabb_test_capsule(struct aabb a, struct capsule c);
static int aabb_test_capsule_manifold(struct manifold *m, struct aabb a, struct capsule c);
/* capsule */
static struct capsule capsule(v3 from, v3 to, float r);
static struct capsule capsulef(float fx, float fy, float fz, float tx, float ty, float tz, float r);
static struct capsule capsulefv(const float *f, const float *t, float r);
static float capsule_sqdist_point(struct capsule, v3 p);
static v3 capsule_closest_point(struct capsule, v3 pnt);
static int capsule_test_sphere(struct capsule, struct sphere s);
static int capsule_test_sphere_manifold(struct manifold *m,struct capsule, struct sphere s);
static int capsule_test_aabb(struct capsule c, struct aabb a);
static int capsule_test_aabb_manifold(struct manifold *m, struct capsule c, struct aabb a);
static int capsule_test_capsule(struct capsule a, struct capsule b);
static int capsule_test_capsule_manifold(struct manifold *m, struct capsule a, struct capsule b);
static struct plane
plane(v3 p, v3 n)
{
struct plane r;
r.p = p;
r.n = n;
return r;
}
static struct plane
planef(float px, float py, float pz, float nx, float ny, float nz)
{
struct plane r;
r.p = v3mk(px,py,pz);
r.n = v3mk(nx,ny,nz);
return r;
}
static struct plane
planefv(float px, float py, float pz, float nx, float ny, float nz)
{
struct plane r;
r.p = v3mk(px,py,pz);
r.n = v3mk(nx,ny,nz);
return r;
}
static struct ray
ray(v3 p, v3 d)
{
struct ray r;
r.p = p;
r.d = v3norm(d);
return r;
}
static struct ray
rayf(float px, float py, float pz, float dx, float dy, float dz)
{
struct ray r;
r.p = v3mk(px,py,pz);
r.d = v3norm(v3mk(dx,dy,dz));
return r;
}
static struct ray
rayfv(const float *p, const float *d)
{
struct ray r;
r.p = v3mk(p[0],p[1],p[2]);
r.d = v3norm(v3mk(d[0],d[1],d[2]));
return r;
}
static int
ray_test_plane(struct raycast *o, struct ray r, struct plane p)
{
float pf[4];
planeq(pf, &p.n.x, &p.p.x);
float t = ray_intersects_plane(&r.p.x, &r.d.x, pf);
if (t <= 0.0f) return 0;
o->p = v3add(r.p, v3scale(r.d, t));
o->t0 = o->t1 = t;
o->n = v3scale(p.n, -1.0f);
return 1;
}
static int
ray_test_triangle(struct raycast *o, struct ray r, const struct v3 *tri)
{
float t = ray_intersects_triangle(&r.p.x, &r.d.x, &tri[0].x, &tri[1].x, &tri[2].x);
if (t <= 0) return 0;
o->t0 = o->t1 = t;
o->p = v3add(r.p, v3scale(r.d, t));
o->n = v3norm(v3cross(v3sub(tri[1],tri[0]),v3sub(tri[2],tri[0])));
return 1;
}
static int
ray_test_sphere(struct raycast *o, struct ray r, struct sphere s)
{
if (!ray_intersects_sphere(&o->t0, &o->t1, &r.p.x, &r.d.x, &s.p.x, s.r))
return 0;
o->p = v3add(r.p, v3scale(r.d, min(o->t0,o->t1)));
o->n = v3norm(v3sub(o->p, s.p));
return 1;
}
static int
ray_test_aabb(struct raycast *o, struct ray r, struct aabb a)
{
v3 pnt, ext, c;
float d, min;
if (!ray_intersects_aabb(&o->t0, &o->t1, &r.p.x, &r.d.x, &a.min.x, &a.max.x))
return 0;
o->p = v3add(r.p, v3scale(r.d, min(o->t0,o->t1)));
ext = v3sub(a.max, a.min);
c = v3add(a.min, v3scale(ext,0.5f));
pnt = v3sub(o->p, c);
min = fabs(ext.x - fabs(pnt.x));
o->n = v3scale(v3mk(1,0,0), sign(pnt.x));
d = fabs(ext.y - fabs(pnt.y));
if (d < min) {
min = d;
o->n = v3scale(v3mk(0,1,0), sign(pnt.y));
}
d = fabs(ext.z - fabs(pnt.z));
if (d < min)
o->n = v3scale(v3mk(0,0,1), sign(pnt.z));
return 1;
}
static struct sphere
sphere(v3 p, float r)
{
struct sphere s;
s.p = p;
s.r = r;
return s;
}
static struct sphere
spheref(float cx, float cy, float cz, float r)
{
struct sphere s;
s.p = v3mk(cx,cy,cz);
s.r = r;
return s;
}
static struct sphere
spherefv(const float *p, float r)
{
return spheref(p[0], p[1], p[2], r);
}
static int
sphere_test_sphere(struct sphere a, struct sphere b)
{
return sphere_intersects_sphere(&a.p.x, a.r, &b.p.x, b.r);
}
static int
sphere_test_sphere_manifold(struct manifold *m, struct sphere a, struct sphere b)
{
return sphere_intersects_sphere_manifold(m, &a.p.x, a.r, &b.p.x, b.r);
}
static int
sphere_test_aabb(struct sphere s, struct aabb a)
{
return sphere_intersect_aabb(&s.p.x, s.r, &a.min.x, &a.max.x);
}
static int
sphere_test_aabb_manifold(struct manifold *m, struct sphere s, struct aabb a)
{
return sphere_intersect_aabb_manifold(m, &s.p.x, s.r, &a.min.x, &a.max.x);
}
static int
sphere_test_capsule(struct sphere s, struct capsule c)
{
return sphere_intersect_capsule(&s.p.x, s.r, &c.a.x, &c.b.x, c.r);
}
static int
sphere_test_capsule_manifold(struct manifold *m,
struct sphere s, struct capsule c)
{
return sphere_intersect_capsule_manifold(m, &s.p.x, s.r, &c.a.x, &c.b.x, c.r);
}
static struct aabb
aabb(v3 min, v3 max)
{
struct aabb a;
a.min = min;
a.max = max;
return a;
}
static struct aabb
aabbf(float minx, float miny, float minz, float maxx, float maxy, float maxz)
{
struct aabb a;
a.min = v3mk(minx,miny,minz);
a.max = v3mk(maxx,maxy,maxz);
return a;
}
static struct aabb
aabbfv(const float *min, const float *max)
{
struct aabb a;
a.min = v3mk(min[0],min[1],min[2]);
a.max = v3mk(max[0],max[1],max[3]);
return a;
}
static struct aabb
aabb_transform(struct aabb a, const float *rot, const v3 t)
{
struct aabb res;
aabb_rebalance_transform(&res.min.x, &res.max.x, &a.min.x, &a.max.x, rot, &t.x);
return res;
}
static v3
aabb_closest_point(struct aabb a, v3 p)
{
v3 res = {0};
aabb_closest_point_to_point(&res.x, &a.min.x, &a.max.x, &p.x);
return res;
}
static int
aabb_test_aabb(struct aabb a, struct aabb b)
{
return aabb_intersect_aabb(&a.min.x, &a.max.x, &b.min.x, &b.max.x);
}
static int
aabb_test_aabb_manifold(struct manifold *m, struct aabb a, struct aabb b)
{
return aabb_intersect_aabb_manifold(m, &a.min.x, &a.max.x, &b.min.x, &b.max.x);
}
static int
aabb_test_sphere(struct aabb a, struct sphere s)
{
return aabb_intersect_sphere(&a.min.x, &a.max.x, &s.p.x, s.r);
}
static int
aabb_test_sphere_manifold(struct manifold *m,
struct aabb a, struct sphere s)
{
return aabb_intersect_sphere_manifold(m, &a.min.x, &a.max.x, &s.p.x, s.r);
}
static int
aabb_test_capsule(struct aabb a, struct capsule c)
{
return aabb_intersect_capsule(&a.min.x, &a.max.x, &c.a.x, &c.b.x, c.r);
}
static int
aabb_test_capsule_manifold(struct manifold *m, struct aabb a, struct capsule c)
{
return aabb_intersect_capsule_manifold(m, &a.min.x, &a.max.x, &c.a.x, &c.b.x, c.r);
}
static struct capsule
capsule(v3 a, v3 b, float r)
{
struct capsule c;
c.a = a; c.b = b; c.r = r;
return c;
}
static struct capsule
capsulef(float ax, float ay, float az, float bx, float by, float bz, float r)
{
struct capsule c;
c.a = v3mk(ax,ay,az);
c.b = v3mk(bx,by,bz);
c.r = r;
return c;
}
static struct capsule
capsulefv(const float *a, const float *b, float r)
{
struct capsule c;
c.a = v3mkv(a);
c.b = v3mkv(b);
c.r = r;
return c;
}
static float
capsule_sqdist_point(struct capsule c, v3 p)
{
return capsule_point_sqdist(&c.a.x, &c.b.x, c.r, &p.x);
}
static v3
capsule_closest_point(struct capsule c, v3 pnt)
{
float p[3];
capsule_closest_point_to_point(p, &c.a.x, &c.b.x, c.r, &pnt.x);
return v3mkv(p);
}
static int
capsule_test_sphere(struct capsule c, struct sphere s)
{
return capsule_intersect_sphere(&c.a.x, &c.b.x, c.r, &s.p.x, s.r);
}
static int
capsule_test_sphere_manifold(struct manifold *m, struct capsule c, struct sphere s)
{
return capsule_intersect_sphere_manifold(m, &c.a.x, &c.b.x, c.r, &s.p.x, s.r);
}
static int
capsule_test_aabb(struct capsule c, struct aabb a)
{
return capsule_intersect_aabb(&c.a.x, &c.b.x, c.r, &a.min.x, &a.max.x);
}
static int
capsule_test_aabb_manifold(struct manifold *m, struct capsule c, struct aabb a)
{
return capsule_intersect_aabb_manifold(m, &c.a.x, &c.b.x, c.r, &a.min.x, &a.max.x);
}
static int
capsule_test_capsule(struct capsule a, struct capsule b)
{
return capsule_intersect_capsule(&a.a.x, &a.b.x, a.r, &b.a.x, &b.b.x, b.r);
}
static int
capsule_test_capsule_manifold(struct manifold *m, struct capsule a, struct capsule b)
{
return capsule_intersect_capsule_manifold(m, &a.a.x, &a.b.x, a.r, &b.a.x, &b.b.x, b.r);
}
/* ============================================================================
*
* CAMERA
*
* ============================================================================*/
#define CAM_INF (-1.0f)
enum cam_orient {CAM_ORIENT_QUAT, CAM_ORIENT_MAT};
enum cam_output_z_range {
CAM_OUT_Z_NEGATIVE_ONE_TO_ONE,
CAM_OUT_Z_NEGATIVE_ONE_TO_ZERO,
CAM_OUT_Z_ZERO_TO_ONE
};
struct cam {
/*------- [input] --------*/
int zout;
enum cam_orient orient_struct;
/* projection */
float fov;
float near, far;
float aspect_ratio;
float z_range_epsilon;
/* view */
float pos[3];
float off[3];
float ear[3];
float q[4];
float m[3][3];
/*------- [output] --------*/
float view[4][4];
float view_inv[4][4];
float proj[4][4];
float proj_inv[4][4];
float loc[3];
float forward[3];
float backward[3];
float right[3];
float down[3];
float left[3];
float up[3];
/*-------------------------*/
};
static void
cam_init(struct cam *c)
{
static const float qid[] = {0,0,0,1};
static const float m3id[] = {1,0,0,0,1,0,0,0,1};
static const float m4id[] = {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};
memset(c, 0, sizeof(*c));
c->aspect_ratio = 3.0f/2.0f;
c->fov = PI_CONSTANT / 4.0f;
c->near = 0.01f;
c->far = 10000;
memcpy(c->q, qid, sizeof(qid));
memcpy(c->m, m3id, sizeof(m3id));
memcpy(c->view, m4id, sizeof(m4id));
memcpy(c->view_inv, m4id, sizeof(m4id));
memcpy(c->proj, m4id, sizeof(m4id));
memcpy(c->proj_inv, m4id, sizeof(m4id));
}
static void
cam_build(struct cam *c)
{
assert(c);
if (!c) return;
/* convert orientation matrix into quaternion */
if (c->orient_struct == CAM_ORIENT_MAT) {
float s,t,
trace = c->m[0][0];
trace += c->m[1][1];
trace += c->m[2][2];
if (trace > 0.0f) {
t = trace + 1.0f;
s = (float)sqrt((double)(1.0f/t)) * 0.5f;
c->q[3] = s * t;
c->q[0] = (c->m[2][1] - c->m[1][2]) * s;
c->q[1] = (c->m[0][2] - c->m[2][0]) * s;
c->q[2] = (c->m[1][0] - c->m[0][1]) * s;
} else {
int i = 0, j, k;
static const int next[] = {1,2,0};
if (c->m[1][1] > c->m[0][0] ) i = 1;
if (c->m[2][2] > c->m[i][i] ) i = 2;
j = next[i]; k = next[j];
t = (c->m[i][i] - (c->m[j][j] - c->m[k][k])) + 1.0f;
s = (float)sqrt((double)(1.0f/t)) * 0.5f;
c->q[i] = s*t;
c->q[3] = (c->m[k][j] - c->m[j][k]) * s;
c->q[j] = (c->m[j][i] + c->m[i][j]) * s;
c->q[k] = (c->m[k][i] + c->m[i][k]) * s;
}
/* normalize quaternion */
{float len2 = c->q[0]*c->q[0] + c->q[1]*c->q[1];
len2 += c->q[2]*c->q[2] + c->q[3]*c->q[3];
if (len2 != 0.0f) {
float len = (float)sqrt((double)len2);
float inv_len = 1.0f/len;
c->q[0] *= inv_len; c->q[1] *= inv_len;
c->q[2] *= inv_len; c->q[3] *= inv_len;
}}
}
/* Camera euler orientation
It is not feasible to multiply euler angles directly together to represent the camera
orientation because of gimbal lock (Even quaternions do not save you against
gimbal lock under all circumstances). While it is true that it is not a problem for
FPS style cameras, it is a problem for free cameras. To fix that issue this camera only
takes in the relative angle rotation and does not store the absolute angle values [7].*/
{float sx =(float)sin((double)(c->ear[0]*0.5f));
float sy = (float)sin((double)(c->ear[1]*0.5f));
float sz = (float)sin((double)(c->ear[2]*0.5f));
float cx = (float)cos((double)(c->ear[0]*0.5f));
float cy = (float)cos((double)(c->ear[1]*0.5f));
float cz = (float)cos((double)(c->ear[2]*0.5f));
float a[4], b[4];
a[0] = cz*sx; a[1] = sz*sx; a[2] = sz*cx; a[3] = cz*cx;
b[0] = c->q[0]*cy - c->q[2]*sy;
b[1] = c->q[3]*sy + c->q[1]*cy;
b[2] = c->q[2]*cy + c->q[0]*sy;
b[3] = c->q[3]*cy - c->q[1]*sy;
c->q[0] = a[3]*b[0] + a[0]*b[3] + a[1]*b[2] - a[2]*b[1];
c->q[1] = a[3]*b[1] + a[1]*b[3] + a[2]*b[0] - a[0]*b[2];
c->q[2] = a[3]*b[2] + a[2]*b[3] + a[0]*b[1] - a[1]*b[0];
c->q[3] = a[3]*b[3] - a[0]*b[0] - a[1]*b[1] - a[2]*b[2];
memset(c->ear, 0, sizeof(c->ear));}
/* Convert quaternion to matrix
Next up we want to convert our camera quaternion orientation into a 3x3 matrix
to generate our view matrix. So to convert from quaternion to rotation
matrix we first look at how to transform a vector quaternion by and how by matrix.
To transform a vector by a unit quaternion you turn the vector into a zero w
quaternion and left multiply by quaternion and right multiply by quaternion inverse:
p2 = q * q(P1) * pi
= q(qx,qy,qz,qw) * q(x,y,z,0) * q(-qx,-qy,-qz,qw)
To get the same result with a rotation matrix you just multiply the vector by matrix:
p2 = M * p1
So to get the matrix M you first multiply out the quaternion transformation and
group each x,y and z term into a column. The end result is: */
{float x2 = c->q[0] + c->q[0];
float y2 = c->q[1] + c->q[1];
float z2 = c->q[2] + c->q[2];
float xx = c->q[0]*x2;
float xy = c->q[0]*y2;
float xz = c->q[0]*z2;
float yy = c->q[1]*y2;
float yz = c->q[1]*z2;
float zz = c->q[2]*z2;
float wx = c->q[3]*x2;
float wy = c->q[3]*y2;
float wz = c->q[3]*z2;
c->m[0][0] = 1.0f - (yy + zz);
c->m[0][1] = xy - wz;
c->m[0][2] = xz + wy;
c->m[1][0] = xy + wz;
c->m[1][1] = 1.0f - (xx + zz);
c->m[1][2] = yz - wx;
c->m[2][0] = xz - wy;
c->m[2][1] = yz + wx;
c->m[2][2] = 1.0f - (xx + yy);}
/* View matrix
The general transform pipeline is object space to world space to local camera
space to screenspace to clipping space. While this particular matrix, the view matrix,
transforms from world space to local camera space.
So we start by trying to find the camera world transform a 4x3 matrix which
can and will be in this particular implementation extended to a 4x4 matrix
composed of camera rotation, camera translation and camera offset.
While pure camera position and orientation is usefull for free FPS style cameras,
allows the camera offset 3rd person cameras or tracking ball behavior.
T.......camera position
F.......camera offset
R.......camera orientation 3x3 matrix
C.......camera world transform 4x4 matrix
|1 T| |R 0| |1 F|
C = |0 1| * |0 1| * |0 1|
|R Rf+T|
C = |0 1|
*/
/* 1.) copy orientation matrix */
c->view_inv[0][0] = c->m[0][0]; c->view_inv[0][1] = c->m[0][1];
c->view_inv[0][2] = c->m[0][2]; c->view_inv[1][0] = c->m[1][0];
c->view_inv[1][1] = c->m[1][1]; c->view_inv[1][2] = c->m[1][2];
c->view_inv[2][0] = c->m[2][0]; c->view_inv[2][1] = c->m[2][1];
c->view_inv[2][2] = c->m[2][2];
/* 2.) transform offset by camera orientation and add translation */
c->view_inv[3][0] = c->view_inv[0][0]*c->off[0];
c->view_inv[3][1] = c->view_inv[0][1]*c->off[0];
c->view_inv[3][2] = c->view_inv[0][2]*c->off[0];
c->view_inv[3][0] += c->view_inv[1][0]*c->off[1];
c->view_inv[3][1] += c->view_inv[1][1]*c->off[1];
c->view_inv[3][2] += c->view_inv[1][2]*c->off[1];
c->view_inv[3][0] += c->view_inv[2][0]*c->off[2];
c->view_inv[3][1] += c->view_inv[2][1]*c->off[2];
c->view_inv[3][2] += c->view_inv[2][2]*c->off[2];
c->view_inv[3][0] += c->pos[0];
c->view_inv[3][1] += c->pos[1];
c->view_inv[3][2] += c->pos[2];
/* 3.) fill last empty 4x4 matrix row */
c->view_inv[0][3] = 0;
c->view_inv[1][3] = 0;
c->view_inv[2][3] = 0;
c->view_inv[3][3] = 1.0f;
/* Now we have a matrix to transform from local camera space into world
camera space. But remember we are looking for the opposite transform since we
want to transform the world around the camera. So to get the other way
around we have to invert our world camera transformation to transform to
local camera space.
Usually inverting matricies is quite a complex endeavour both in needed complexity
as well as number of calculations required. Luckily we can use a nice property
of orthonormal matrices (matrices with each column being unit length)
on the more complicated matrix the rotation matrix.
The inverse of orthonormal matrices is the same as the transpose of the same
matrix, which is just a matrix column to row swap.
So with inverse rotation matrix covered the only thing left is the inverted
camera translation which just needs to be negated plus and this is the more
important part tranformed by the inverted rotation matrix.
Why? simply because the inverse of a matrix multiplication M = A * B
is NOT Mi = Ai * Bi (i for inverse) but rather Mi = Bi * Ai.
So if we put everything together we get the following view matrix:
R.......camera orientation matrix3x3
T.......camera translation
F.......camera offset
Ti......camera inverse translation
Fi......camera inverse offset
Ri......camera inverse orientation matrix3x3
V.......view matrix
(|R Rf+T|)
V = inv(|0 1|)
|Ri -Ri*Ti-Fi|
V = |0 1|
Now we finally have our matrix composition and can fill out the view matrix:
1.) Inverse camera orientation by transpose */
c->view[0][0] = c->m[0][0];
c->view[0][1] = c->m[1][0];
c->view[0][2] = c->m[2][0];
c->view[1][0] = c->m[0][1];
c->view[1][1] = c->m[1][1];
c->view[1][2] = c->m[2][1];
c->view[2][0] = c->m[0][2];
c->view[2][1] = c->m[1][2];
c->view[2][2] = c->m[2][2];
/* 2.) Transform inverted position vector by transposed orientation and subtract offset */
{float pos_inv[3];
pos_inv[0] = -c->pos[0];
pos_inv[1] = -c->pos[1];
pos_inv[2] = -c->pos[2];
c->view[3][0] = c->view[0][0] * pos_inv[0];
c->view[3][1] = c->view[0][1] * pos_inv[0];
c->view[3][2] = c->view[0][2] * pos_inv[0];
c->view[3][0] += c->view[1][0] * pos_inv[1];
c->view[3][1] += c->view[1][1] * pos_inv[1];
c->view[3][2] += c->view[1][2] * pos_inv[1];
c->view[3][0] += c->view[2][0] * pos_inv[2];
c->view[3][1] += c->view[2][1] * pos_inv[2];
c->view[3][2] += c->view[2][2] * pos_inv[2];
c->view[3][0] -= c->off[0];
c->view[3][1] -= c->off[1];
c->view[3][2] -= c->off[2];}
/* 3.) fill last empty 4x4 matrix row */
c->view[0][3] = 0; c->view[1][3] = 0;
c->view[2][3] = 0; c->view[3][3] = 1.0f;
/* Projection matrix
While the view matrix transforms from world space to local camera space,
tranforms the perspective projection matrix from camera space to screen space.
The actual work for the transformation is from eye coordinates camera
frustum far plane to a cube with coordinates (-1,1), (0,1), (-1,0) depending on
argument `out_z_range` in this particual implementation.
To actually build the projection matrix we need:
- Vertical field of view angle
- Screen aspect ratio which controls the horizontal view angle in
contrast to the field of view.
- Z coordinate of the frustum near clipping plane
- Z coordinate of the frustum far clipping plane
While I will explain how to incooperate the near,far z clipping
plane I would recommend reading [7] for the other values since it is quite
hard to do understand without visual aid and he is probably better in
explaining than I would be.*/
{float hfov = (float)tan((double)(c->fov*0.5f));
c->proj[0][0] = 1.0f/(c->aspect_ratio * hfov);
c->proj[0][1] = 0;
c->proj[0][2] = 0;
c->proj[0][3] = 0;
c->proj[1][0] = 0;
c->proj[1][1] = 1.0f/hfov;
c->proj[1][2] = 0;
c->proj[1][3] = 0;
c->proj[2][0] = 0;
c->proj[2][1] = 0;
c->proj[2][3] = -1.0f;
c->proj[3][0] = 0;
c->proj[3][1] = 0;
c->proj[3][3] = 0;
if (c->far <= CAM_INF) {
/* Up to this point we got perspective matrix:
|1/aspect*hfov 0 0 0|
|0 1/hfov 0 0|
|0 0 A B|
|0 0 -1 0|
but we are still missing A and B to map between the frustum near/far
and the clipping cube near/far value. So we take the lower right part
of the matrix and multiply by a vector containing the missing
z and w value, which gives us the resulting clipping cube z;
|A B| |z| |Az + B | B
|-1 0| * |1| = | -z | = -A + ------
-z
So far so good but now we need to map from the frustum near,
far clipping plane (n,f) to the clipping cube near and far plane (cn,cf).
So we plugin the frustum near/far values into z and the resulting
cube near/far plane we want to end up with.
B B
-A + ------ = cn and -A + ------ = cf
n f
We now have two equations with two unkown A and B since n,f as well
as cn,cf are provided, so we can solved them by subtitution either by
hand or, if you are like me prone to easily make small mistakes,
with WolframAlpha which solved for:*/
switch (c->zout) {
default:
case CAM_OUT_Z_NEGATIVE_ONE_TO_ONE: {
/* cn = -1 and cf = 1: */
c->proj[2][2] = -(c->far + c->near) / (c->far - c->near);
c->proj[3][2] = -(2.0f * c->far * c->near) / (c->far - c->near);
} break;
case CAM_OUT_Z_NEGATIVE_ONE_TO_ZERO: {
/* cn = -1 and cf = 0: */
c->proj[2][2] = (c->near) / (c->near - c->far);
c->proj[3][2] = (c->far * c->near) / (c->near - c->far);
} break;
case CAM_OUT_Z_ZERO_TO_ONE: {
/* cn = 0 and cf = 1: */
c->proj[2][2] = -(c->far) / (c->far - c->near);
c->proj[3][2] = -(c->far * c->near) / (c->far - c->near);
} break;
}
} else {
/* Infinite projection [1]:
In general infinite projection matrices map direction to points on the
infinite distant far plane, which is mainly useful for rendering:
- skyboxes, sun, moon, stars
- stencil shadow volume caps
To actually calculate the infinite perspective matrix you let the
far clip plane go to infinity. Once again I would recommend using and
checking WolframAlpha to make sure all values are correct, while still
doing the calculation at least once by hand.
While it is mathematically correct to go to infinity, floating point errors
result in a depth smaller or bigger. This in term results in
fragment culling since the hardware thinks fragments are beyond the
far clipping plane. The general solution is to introduce an epsilon
to fix the calculation error and map it to the infinite far plane.
Important:
For 32-bit floating point epsilon should be greater than 2.4*10^-7,
to account for floating point precision problems. */
switch (c->zout) {
default:
case CAM_OUT_Z_NEGATIVE_ONE_TO_ONE: {
/*lim f->inf -((f+n)/(f-n)) => -((inf+n)/(inf-n)) => -(inf)/(inf) => -1.0*/
c->proj[2][2] = c->z_range_epsilon - 1.0f;
/* lim f->inf -(2*f*n)/(f-n) => -(2*inf*n)/(inf-n) => -(2*inf*n)/inf => -2n*/
c->proj[3][2] = (c->z_range_epsilon - 2.0f) * c->near;
} break;
case CAM_OUT_Z_NEGATIVE_ONE_TO_ZERO: {
/* lim f->inf n/(n-f) => n/(n-inf) => n/(n-inf) => -0 */
c->proj[2][2] = c->z_range_epsilon;
/* lim f->inf (f*n)/(n-f) => (inf*n)/(n-inf) => (inf*n)/-inf = -n */
c->proj[3][2] = (c->z_range_epsilon - 1.0f) * c->near;
} break;
case CAM_OUT_Z_ZERO_TO_ONE: {
/* lim f->inf (-f)/(f-n) => (-inf)/(inf-n) => -inf/inf = -1 */
c->proj[2][2] = c->z_range_epsilon - 1.0f;
/* lim f->inf (-f*n)/(f-n) => (-inf*n)/(inf-n) => (-inf*n)/(inf) => -n */
c->proj[3][2] = (c->z_range_epsilon - 1.0f) * c->near;
} break;
}
}}
/* Invert projection [2][3]:
Since perspective matrices have a fixed layout, it makes sense
to calculate the specific perspective inverse instead of relying on a default
matrix inverse function. Actually calculating the matrix for any perspective
matrix is quite straight forward:
I.......identity matrix
p.......perspective matrix
I(p)....inverse perspective matrix
1.) Fill a variable inversion matrix and perspective layout matrix into the
inversion formula: I(p) * p = I
|x0 x1 x2 x3 | |a 0 0 0| |1 0 0 0|
|x4 x5 x6 x7 | * |0 b 0 0| = |0 1 0 0|
|x8 x9 x10 x11| |0 0 c d| |0 0 1 0|
|x12 x13 x14 x15| |0 0 e 0| |0 0 0 1|
2.) Multiply inversion matrix times our perspective matrix
|x0*a x1*b x2*c+x3*e x2*d| |1 0 0 0|
|x4*a x5*b x6*c+x7*e x6*d| = |0 1 0 0|
|x8*a x9*b x10*c+x11*e x10*d| |0 0 1 0|
|x12*a x13*b x14*c+x15*e x14*d| |0 0 0 1|
3.) Finally substitute each x value: e.g: x0*a = 1 => x0 = 1/a so I(p) at column 0, row 0 is 1/a.
|1/a 0 0 0|
I(p) = |0 1/b 0 0|
|0 0 0 1/e|
|0 0 1/d -c/de|
These steps basically work for any invertable matrices, but I would recommend
using WolframAlpha for these specific kinds of matrices, since it can
automatically generate inversion matrices without any fuss or possible
human calculation errors. */
memset(c->proj_inv, 0, sizeof(c->proj_inv));
c->proj_inv[0][0] = 1.0f/c->proj[0][0];
c->proj_inv[1][1] = 1.0f/c->proj[1][1];
c->proj_inv[2][3] = 1.0f/c->proj[3][2];
c->proj_inv[3][2] = 1.0f/c->proj[2][3];
c->proj_inv[3][3] = -c->proj[2][2]/(c->proj[3][2] * c->proj[2][3]);
/* fill vectors with data */
c->loc[0] = c->view_inv[3][0];
c->loc[1] = c->view_inv[3][1];
c->loc[2] = c->view_inv[3][2];
c->right[0] = c->view_inv[0][0];
c->right[1] = c->view_inv[0][1];
c->right[2] = c->view_inv[0][2];
c->left[0] = -c->view_inv[0][0];
c->left[1] = -c->view_inv[0][1];
c->left[2] = -c->view_inv[0][2];
c->up[0] = c->view_inv[1][0];
c->up[1] = c->view_inv[1][1];
c->up[2] = c->view_inv[1][2];
c->down[0] = -c->view_inv[1][0];
c->down[1] = -c->view_inv[1][1];
c->down[2] = -c->view_inv[1][2];
c->forward[0] = c->view_inv[2][0];
c->forward[1] = c->view_inv[2][1];
c->forward[2] = c->view_inv[2][2];
c->backward[0] = -c->view_inv[2][0];
c->backward[1] = -c->view_inv[2][1];
c->backward[2] = -c->view_inv[2][2];
}
static void
cam_lookat(struct cam *c,
float eye_x, float eye_y, float eye_z,
float ctr_x, float ctr_y, float ctr_z,
float up_x, float up_y, float up_z)
{
float f[3], u[3], r[3];
f[0] = ctr_x - eye_x, f[1] = ctr_y - eye_y, f[2] = ctr_z - eye_z;
/* calculate right vector */
r[0] = (f[1]*up_z) - (f[2]*up_y);
r[1] = (f[2]*up_x) - (f[0]*up_z);
r[2] = (f[0]*up_y) - (f[1]*up_x);
/* calculate up vector */
u[0] = (r[1]*f[2]) - (r[2]*f[1]);
u[1] = (r[2]*f[0]) - (r[0]*f[2]);
u[2] = (r[0]*f[1]) - (r[1]*f[0]);
/* normlize vectors */
{float fl = f[0]*f[0]+f[1]*f[1]+f[2]*f[2];
float rl = r[0]*r[0]+r[1]*r[1]+r[2]*r[2];
float ul = u[0]*u[0]+u[1]*u[1]+u[2]*u[2];
fl = (fl == 0.0f) ? 1.0f: 1/sqrtf(fl);
rl = (rl == 0.0f) ? 1.0f: 1/sqrtf(rl);
ul = (ul == 0.0f) ? 1.0f: 1/sqrtf(ul);
f[0] *= fl, f[1] *= fl, f[2] *= fl;
r[0] *= rl, r[1] *= rl, r[2] *= rl;
u[0] *= ul, u[1] *= ul, u[2] *= ul;}
/* setup camera */
c->pos[0] = eye_x, c->pos[1] = eye_y, c->pos[2] = eye_z;
c->m[0][0] = r[0], c->m[0][1] = r[1], c->m[0][2] = r[2];
c->m[1][0] = u[0], c->m[1][1] = u[1], c->m[1][2] = u[2];
c->m[2][0] = f[0], c->m[2][1] = f[1], c->m[2][2] = f[2];
/* build camera */
{enum cam_orient orient = c->orient_struct;
c->orient_struct = CAM_ORIENT_MAT;
memset(c->ear, 0, sizeof(c->ear));
cam_build(c);
c->orient_struct = orient;}
}
static void
cam_move(struct cam *c, float x, float y, float z)
{
c->pos[0] += c->view_inv[0][0]*x;
c->pos[1] += c->view_inv[0][1]*x;
c->pos[2] += c->view_inv[0][2]*x;
c->pos[0] += c->view_inv[1][0]*y;
c->pos[1] += c->view_inv[1][1]*y;
c->pos[2] += c->view_inv[1][2]*y;
c->pos[0] += c->view_inv[2][0]*z;
c->pos[1] += c->view_inv[2][1]*z;
c->pos[2] += c->view_inv[2][2]*z;
}
static void
cam_screen_to_world(const struct cam *c, float width, float height,
float *res, float screen_x, float screen_y, float camera_z)
{
/* Screen space to world space coordinates
To convert from screen space coordinates to world coordinates we
basically have to revert all transformations typically done to
convert from world space to screen space:
Viewport => NDC => Clip => View => World
Viewport => NDC => Clip
-----------------------
First up is the transform from viewport to clipping space.
To get from clipping space to viewport we calculate:
|((x+1)/2)*w|
Vn = v = |((1-y)/2)*h|
|((z+1)/2) |
Now we need to the the inverse process by solvinging for n:
|(2*x)/w - 1|
n = |(2*y)/h |
|(2*z)-1) |
| 1 |
*/
float x = (screen_x / width * 2.0f) - 1.0f;
float y = (screen_y / height) * 2.0f - 1.0f;
float z = 2.0f * camera_z - 1.0f;
/* Clip => View
-----------------------
A vector v or position p in view space is tranform to clip
coordinates c by being transformed by a projection matrix P:
c = P * v
To convert from clipping coordinates c to view coordinates we
just have to transfrom c by the inverse projection matrix Pi:
v = Pi * c
The inverse projection matrix for all common projection matrices
can be calculated by (see Camera_Build for more information):
|1/a 0 0 0|
Pi = |0 1/b 0 0|
|0 0 0 1/e|
|0 0 1/d -c/de|
View => World
-----------------------
Finally we just need to convert from view coordinates to world
coordinates w by transforming our view coordinates by the inverse view
matrix Vi which in this context is just the camera translation and
rotation.
w = Vi * v
Now we reached our goal and have our world coordinates. This implementation
combines both the inverse projection as well as inverse view transformation
into one since the projection layout is known we can do some optimization:*/
float ax = c->proj_inv[0][0]*x;
float by = c->proj_inv[1][1]*y;
float dz = c->proj_inv[2][3]*z;
float w = c->proj_inv[3][3] + dz;
res[0] = c->proj_inv[3][2] * c->view_inv[2][0];
res[0] += c->proj_inv[3][3] * c->view_inv[3][0];
res[0] += ax * c->view_inv[0][0];
res[0] += by * c->view_inv[1][0];
res[0] += dz * c->view_inv[3][0];
res[1] = c->proj_inv[3][2] * c->view_inv[2][1];
res[1] += c->proj_inv[3][3] * c->view_inv[3][1];
res[1] += ax * c->view_inv[0][1];
res[1] += by * c->view_inv[1][1];
res[1] += dz * c->view_inv[3][1];
res[2] = c->proj_inv[3][2] * c->view_inv[2][2];
res[2] += c->proj_inv[3][3] * c->view_inv[3][2];
res[2] += ax * c->view_inv[0][2];
res[2] += by * c->view_inv[1][2];
res[2] += dz * c->view_inv[3][2];
res[0] /= w; res[1] /= w; res[2] /= w;
}
static void
cam_picking_ray(const struct cam *c, float w, float h,
float mx, float my, float *ro, float *rd)
{
float world[3];
cam_screen_to_world(c, w, h, world, mx, my, 0);
/* calculate direction
We generate the ray normal vector by first transforming the mouse cursor position
from screen coordinates into world coordinates. After that we only have to
subtract our camera position from our calculated mouse world position and
normalize the result to make sure we have a unit vector as direction. */
ro[0] = c->loc[0];
ro[1] = c->loc[1];
ro[2] = c->loc[2];
rd[0] = world[0] - ro[0];
rd[1] = world[1] - ro[1];
rd[2] = world[2] - ro[2];
/* normalize */
{float dot = rd[0]*rd[0] + rd[1]*rd[1] + rd[2]*rd[2];
if (dot != 0.0f) {
float len = (float)sqrt((double)dot);
rd[0] /= len; rd[1] /= len; rd[2] /= len;
}}
}
/* ============================================================================
*
* 3D DRAW
*
* =========================================================================== */
#include <GL/gl.h>
#include <GL/glu.h>
#define glLine(a,b) glLinef((a)[0],(a)[1],(a)[2],(b)[0],(b)[1],(b)[2])
#define glBox(c,w,h,d) glBoxf((c)[0],(c)[1],(c)[2],w,h,d)
#define glTriangle(a,b,c) glTrianglef((a)[0],(a)[1],(a)[2],(b)[0],(b)[1],(b)[2],(c)[0],(c)[1],(c)[2])
#define glSphere(c,r) glSpheref((c)[0],(c)[1],(c)[2],r)
#define glPlane(p,n,scale) glPlanef(p[0],p[1],p[2],n[0],n[1],n[2],scale)
#define glArrow(f,t,size) glArrowf((f)[0],(f)[1],(f)[2],(t)[0],(t)[1],(t)[2],size)
#define glBox(c,w,h,d) glBoxf((c)[0],(c)[1],(c)[2],w,h,d)
#define glCapsule(a,b,r) glCapsulef((a)[0],(a)[1],(a)[2], (b)[0],(b)[1],(b)[2], (r))
#define glPyramid(a,b,s) glPyramidf((a)[0],(a)[1],(a)[2], (b)[0],(b)[1],(b)[2], (s))
#define glError() glerror_(__FILE__, __LINE__)
static void
glerror_(const char *file, int line)
{
const GLenum code = glGetError();
if (code == GL_INVALID_ENUM)
fprintf(stdout, "[GL] Error: (%s:%d) invalid value!\n", file, line);
else if (code == GL_INVALID_OPERATION)
fprintf(stdout, "[GL] Error: (%s:%d) invalid operation!\n", file, line);
else if (code == GL_INVALID_FRAMEBUFFER_OPERATION)
fprintf(stdout, "[GL] Error: (%s:%d) invalid frame op!\n", file, line);
else if (code == GL_OUT_OF_MEMORY)
fprintf(stdout, "[GL] Error: (%s:%d) out of memory!\n", file, line);
else if (code == GL_STACK_UNDERFLOW)
fprintf(stdout, "[GL] Error: (%s:%d) stack underflow!\n", file, line);
else if (code == GL_STACK_OVERFLOW)
fprintf(stdout, "[GL] Error: (%s:%d) stack overflow!\n", file, line);
}
static void
f3ortho(float *left, float *up, const float *v)
{
#define inv_sqrt(x) (1.0f/sqrtf(x));
float lenSqr, invLen;
if (fabs(v[2]) > 0.7f) {
lenSqr = v[1]*v[1]+v[2]*v[2];
invLen = inv_sqrt(lenSqr);
up[0] = 0.0f;
up[1] = v[2]*invLen;
up[2] = -v[1]*invLen;
left[0] = lenSqr*invLen;
left[1] = -v[0]*up[2];
left[2] = v[0]*up[1];
} else {
lenSqr = v[0]*v[0] + v[1]*v[1];
invLen = inv_sqrt(lenSqr);
left[0] = -v[1] * invLen;
left[1] = v[0] * invLen;
left[2] = 0.0f;
up[0] = -v[2] * left[1];
up[1] = v[2] * left[0];
up[2] = lenSqr * invLen;
}
}
static void
glLinef(float ax, float ay, float az, float bx, float by, float bz)
{
glBegin(GL_LINES);
glVertex3f(ax, ay, az);
glVertex3f(bx, by, bz);
glEnd();
}
static void
glPlanef(float px, float py, float pz,
float nx, float ny, float nz, float scale)
{
float n[3], p[3];
float v1[3], v2[3], v3[3], v4[3];
float tangent[3], bitangent[3];
f3set(n,nx,ny,nz);
f3set(p,px,py,pz);
f3ortho(tangent, bitangent, n);
#define DD_PLANE_V(v, op1, op2) \
v[0] = (p[0] op1 (tangent[0]*scale) op2 (bitangent[0]*scale)); \
v[1] = (p[1] op1 (tangent[1]*scale) op2 (bitangent[1]*scale)); \
v[2] = (p[2] op1 (tangent[2]*scale) op2 (bitangent[2]*scale))
DD_PLANE_V(v1,-,-);
DD_PLANE_V(v2,+,-);
DD_PLANE_V(v3,+,+);
DD_PLANE_V(v4,-,+);
#undef DD_PLANE_V
glLine(v1,v2);
glLine(v2,v3);
glLine(v3,v4);
glLine(v4,v1);
}
static void
glTrianglef(float ax, float ay, float az,
float bx, float by, float bz,
float cx, float cy, float cz)
{
glLinef(ax,ay,az, bx,by,bz);
glLinef(bx,by,bz, cx,cy,cz);
glLinef(cx,cy,cz, ax,ay,az);
}
static void
glArrowf(float fx, float fy, float fz, float tx, float ty, float tz, const float size)
{
int i = 0;
float degrees = 0.0f;
static const float arrow_step = 30.0f;
static const float arrow_sin[45] = {
0.0f, 0.5f, 0.866025f, 1.0f, 0.866025f, 0.5f, -0.0f, -0.5f, -0.866025f,
-1.0f, -0.866025f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
};
static const float arrow_cos[45] = {
1.0f, 0.866025f, 0.5f, -0.0f, -0.5f, -0.866026f, -1.0f, -0.866025f, -0.5f, 0.0f,
0.5f, 0.866026f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
}; float from[3], to[3];
float up[3], right[3], forward[3];
f3set(from,fx,fy,fz);
f3set(to,tx,ty,tz);
f3sub(forward, to, from);
f3norm(forward);
f3ortho(right, up, forward);
f3mul(forward, forward, size);
glLine(from, to);
for (i = 0; degrees < 360.0f; degrees += arrow_step, ++i) {
float scale;
float v1[3], v2[3], temp[3];
scale = 0.5f * size * arrow_cos[i];
f3mul(temp, right, scale);
f3sub(v1, to, forward);
f3add(v1, v1, temp);
scale = 0.5f * size * arrow_sin[i];
f3mul(temp, up, scale);
f3add(v1, v1, temp);
scale = 0.5f * size * arrow_cos[i + 1];
f3mul(temp, right, scale);
f3sub(v2, to, forward);
f3add(v2, v2, temp);
scale = 0.5f * size * arrow_sin[i + 1];
f3mul(temp, up, scale);
f3add(v2, v2, temp);
glLine(v1, to);
glLine(v1, v2);
}
}
static void
glSpheref(float cx, float cy, float cz, const float radius)
{
#define STEP_SIZE 15
float last[3], tmp[3], radius_vec[3];
float cache[(360 / STEP_SIZE)*3];
int j = 0, i = 0, n = 0;
f3set(radius_vec,0,0,radius);
f3set(&cache[0], cx,cy,cz+radius_vec[2]);
for (n = 1; n < (cntof(cache)/3); ++n)
f3cpy(&cache[n*3], &cache[0]);
/* first circle iteration */
for (i = STEP_SIZE; i <= 360; i += STEP_SIZE) {
const float s = sinf(i*TO_RAD);
const float c = cosf(i*TO_RAD);
last[0] = cx;
last[1] = cy + radius * s;
last[2] = cz + radius * c;
/* second circle iteration */
for (n = 0, j = STEP_SIZE; j <= 360; j += STEP_SIZE, ++n) {
tmp[0] = cx + sinf(TO_RAD*j)*radius*s;
tmp[1] = cy + cosf(TO_RAD*j)*radius*s;
tmp[2] = last[2];
glLine(last, tmp);
glLine(last, &cache[n*3]);
f3cpy(&cache[n*3], last);
f3cpy(last, tmp);
}
}
#undef STEP_SIZE
}
static void
glCapsulef(float fx, float fy, float fz, float tx, float ty, float tz, float r)
{
int i = 0;
static const int step_size = 20;
float up[3], right[3], forward[3];
float from[3], to[3];
float lastf[3], lastt[3];
/* calculate axis */
f3set(from,fx,fy,fz);
f3set(to,tx,ty,tz);
f3sub(forward, to, from);
f3norm(forward);
f3ortho(right, up, forward);
/* calculate first two cone verts (buttom + top) */
f3mul(lastf, up,r);
f3add(lastt, to,lastf);
f3add(lastf, from,lastf);
/* step along circle outline and draw lines */
for (i = step_size; i <= 360; i += step_size) {
/* calculate current rotation */
float ax[3], ay[3], pf[3], pt[3], tmp[3];
f3mul(ax, right, sinf(i*TO_RAD));
f3mul(ay, up, cosf(i*TO_RAD));
/* calculate current vertices on cone */
f3add(tmp, ax, ay);
f3mul(pf, tmp, r);
f3mul(pt, tmp, r);
f3add(pf, pf, from);
f3add(pt, pt, to);
/* draw cone vertices */
glLine(lastf, pf);
glLine(lastt, pt);
glLine(pf, pt);
f3cpy(lastf, pf);
f3cpy(lastt, pt);
/* calculate first top sphere vert */
float prevt[3], prevf[3];
f3mul(prevt, tmp, r);
f3add(prevf, prevt, from);
f3add(prevt, prevt, to);
/* sphere (two half spheres )*/
for (int j = 1; j < 180/step_size; j++) {
/* angles */
float ta = j*step_size;
float fa = 360-(j*step_size);
float t[3];
/* top half-sphere */
f3mul(ax, forward, sinf(ta*TO_RAD));
f3mul(ay, tmp, cosf(ta*TO_RAD));
f3add(t, ax, ay);
f3mul(pf, t, r);
f3add(pf, pf, to);
glLine(pf, prevt);
f3cpy(prevt, pf);
/* bottom half-sphere */
f3mul(ax, forward, sinf(fa*TO_RAD));
f3mul(ay, tmp, cosf(fa*TO_RAD));
f3add(t, ax, ay);
f3mul(pf, t, r);
f3add(pf, pf, from);
glLine(pf, prevf);
f3cpy(prevf, pf);
}
}
}
static void
pyramid(float *dst, float fx, float fy, float fz, float tx, float ty, float tz, float size)
{
float from[3];
float *a = dst + 0;
float *b = dst + 3;
float *c = dst + 6;
float *d = dst + 9;
float *r = dst + 12;
/* calculate axis */
float up[3], right[3], forward[3];
f3set(from,fx,fy,fz);
f3set(r,tx,ty,tz);
f3sub(forward, r, from);
f3norm(forward);
f3ortho(right, up, forward);
/* calculate extend */
float xext[3], yext[3];
float nxext[3], nyext[3];
f3mul(xext, right, size);
f3mul(yext, up, size);
f3mul(nxext, right, -size);
f3mul(nyext, up, -size);
/* calculate base vertexes */
f3add(a, from, xext);
f3add(a, a, yext);
f3add(b, from, xext);
f3add(b, b, nyext);
f3add(c, from, nxext);
f3add(c, c, nyext);
f3add(d, from, nxext);
f3add(d, d, yext);
}
static void
glPyramidf(float fx, float fy, float fz,
float tx, float ty, float tz, float size)
{
float pyr[16];
pyramid(pyr, fx, fy, fz, tx, ty, tz, size);
float *a = pyr + 0;
float *b = pyr + 3;
float *c = pyr + 6;
float *d = pyr + 9;
float *r = pyr + 12;
/* draw vertexes */
glLine(a, b);
glLine(b, c);
glLine(c, d);
glLine(d, a);
/* draw root */
glLine(a, r);
glLine(b, r);
glLine(c, r);
glLine(d, r);
}
static void
diamond(float *dst, float fx, float fy, float fz,
float tx, float ty, float tz, float size)
{
float d[3], f[3], t[3], from[3];
f3set(t, tx,ty,tz);
f3set(f, fx,fy,fz);
f3sub(d, t, f);
f3mul(d, d, 0.5f);
f3add(from, f, d);
pyramid(dst, from[0], from[1], from[2], tx, ty, tz, size);
f3cpy(dst + 15, f);
}
static void
glDiamondf(float fx, float fy, float fz,
float tx, float ty, float tz, float size)
{
float dmd[18];
diamond(dmd, fx, fy, fz, tx, ty, tz, size);
float *a = dmd + 0;
float *b = dmd + 3;
float *c = dmd + 6;
float *d = dmd + 9;
float *t = dmd + 12;
float *f = dmd + 15;
/* draw vertexes */
glLine(a, b);
glLine(b, c);
glLine(c, d);
glLine(d, a);
/* draw roof */
glLine(a, t);
glLine(b, t);
glLine(c, t);
glLine(d, t);
/* draw floor */
glLine(a, f);
glLine(b, f);
glLine(c, f);
glLine(d, f);
}
static void
glGrid(const float mins, const float maxs, const float y, const float step)
{
float i;
float from[3], to[3];
for (i = mins; i <= maxs; i += step) {
/* Horizontal line (along the X) */
f3set(from, mins,y,i);
f3set(to, maxs,y,i);
glLine(from,to);
/* Vertical line (along the Z) */
f3set(from, i,y,mins);
f3set(to, i,y,maxs);
glLine(from,to);
}
}
static void
glBounds(const float *points)
{
int i = 0;
for (i = 0; i < 4; ++i) {
glLine(&points[i*3], &points[((i + 1) & 3)*3]);
glLine(&points[(4 + i)*3], &points[(4 + ((i + 1) & 3))*3]);
glLine(&points[i*3], &points[(4 + i)*3]);
}
}
static void
glBoxf(float cx, float cy, float cz, float w, float h, float d)
{
float pnts[8*3];
w = w*0.5f;
h = h*0.5f;
d = d*0.5f;
#define DD_BOX_V(v, op1, op2, op3)\
(v)[0] = cx op1 w; (v)[1] = cy op2 h; (v)[2] = cz op3 d
DD_BOX_V(&pnts[0*3], -, +, +);
DD_BOX_V(&pnts[1*3], -, +, -);
DD_BOX_V(&pnts[2*3], +, +, -);
DD_BOX_V(&pnts[3*3], +, +, +);
DD_BOX_V(&pnts[4*3], -, -, +);
DD_BOX_V(&pnts[5*3], -, -, -);
DD_BOX_V(&pnts[6*3], +, -, -);
DD_BOX_V(&pnts[7*3], +, -, +);
#undef DD_BOX_V
glBounds(pnts);
}
static void
glAabb(const float *bounds)
{
int i = 0;
float bb[2*3], pnts[8*3];
f3cpy(&bb[0], bounds);
f3cpy(&bb[3], bounds+3);
for (i = 0; i < cntof(pnts)/3; ++i) {
pnts[i*3+0] = bb[(((i ^ (i >> 1)) & 1)*3+0)];
pnts[i*3+1] = bb[(((i >> 1) & 1)*3)+1];
pnts[i*3+2] = bb[(((i >> 2) & 1)*3)+2];
} glBounds(pnts);
}
/* ============================================================================
*
* SYSTEM
*
* =========================================================================== */
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
typedef struct sys_int2 {int x,y;} sys_int2;
enum {
SYS_FALSE = 0,
SYS_TRUE = 1,
SYS_MAX_KEYS = 256
};
struct sys_button {
unsigned int down:1;
unsigned int pressed:1;
unsigned int released:1;
};
enum sys_mouse_mode {
SYSTEM_MOUSE_ABSOLUTE,
SYSTEM_MOUSE_RELATIVE
};
struct sys_mouse {
enum sys_mouse_mode mode;
unsigned visible:1;
sys_int2 pos;
sys_int2 pos_last;
sys_int2 pos_delta;
float scroll_delta;
struct sys_button left_button;
struct sys_button right_button;
};
struct sys_time {
float refresh_rate;
float last;
float delta;
unsigned int start;
};
enum sys_window_mode {
SYSTEM_WM_WINDOWED,
SYSTEM_WM_FULLSCREEN,
SYSTEM_WM_MAX,
};
struct sys_window {
const char *title;
SDL_Window *handle;
SDL_GLContext glContext;
enum sys_window_mode mode;
sys_int2 pos;
sys_int2 size;
unsigned resized:1;
unsigned moved:1;
};
struct sys {
unsigned quit:1;
unsigned initialized:1;
struct sys_time time;
struct sys_window win;
struct sys_button key[SYS_MAX_KEYS];
struct sys_mouse mouse;
};
static void
sys_init(struct sys *s)
{
int monitor_refresh_rate;
int win_x, win_y, win_w, win_h;
const char *title;
SDL_Init(SDL_INIT_VIDEO);
title = s->win.title ? s->win.title: "SDL";
win_x = s->win.pos.x ? s->win.pos.x: SDL_WINDOWPOS_CENTERED;
win_y = s->win.pos.y ? s->win.pos.y: SDL_WINDOWPOS_CENTERED;
win_w = s->win.size.x ? s->win.size.x: 800;
win_h = s->win.size.y ? s->win.size.y: 600;
s->win.handle = SDL_CreateWindow(title, win_x, win_y, win_w, win_h, SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
s->win.glContext = SDL_GL_CreateContext(s->win.handle);
s->mouse.visible = SYS_TRUE;
monitor_refresh_rate = 60;
s->time.refresh_rate = 1.0f/(float)monitor_refresh_rate;
s->initialized = SYS_TRUE;
}
static void
sys_shutdown(struct sys *s)
{
SDL_GL_DeleteContext(s->win.glContext);
SDL_DestroyWindow(s->win.handle);
SDL_Quit();
}
static void
sys_update_button(struct sys_button *b, unsigned down)
{
int was_down = b->down;
b->down = down;
b->pressed = !was_down && down;
b->released = was_down && !down;
}
static void
sys_pull(struct sys *s)
{
int i = 0;
SDL_PumpEvents();
s->time.start = SDL_GetTicks();
s->mouse.left_button.pressed = 0;
s->mouse.left_button.released = 0;
s->mouse.right_button.pressed = 0;
s->mouse.right_button.released = 0;
s->mouse.scroll_delta = 0;
/* window-mode */
if (s->win.mode == SYSTEM_WM_WINDOWED)
SDL_SetWindowFullscreen(s->win.handle, 0);
else SDL_SetWindowFullscreen(s->win.handle, SDL_WINDOW_FULLSCREEN_DESKTOP);
/* poll window */
s->win.moved = 0;
s->win.resized = 0;
SDL_GetWindowSize(s->win.handle, &s->win.size.x, &s->win.size.y);
SDL_GetWindowPosition(s->win.handle, &s->win.pos.x, &s->win.pos.y);
/* poll keyboard */
{const unsigned char* keys = SDL_GetKeyboardState(0);
for (i = 0; i < SYS_MAX_KEYS; ++i) {
SDL_Keycode sym = SDL_GetKeyFromScancode((SDL_Scancode)i);
if (sym < SYS_MAX_KEYS)
sys_update_button(s->key + sym, keys[i]);
}}
/* poll mouse */
s->mouse.pos_delta.x = 0;
s->mouse.pos_delta.y = 0;
SDL_SetRelativeMouseMode(s->mouse.mode == SYSTEM_MOUSE_RELATIVE);
if (s->mouse.mode == SYSTEM_MOUSE_ABSOLUTE) {
s->mouse.pos_last.x = s->mouse.pos.x;
s->mouse.pos_last.y = s->mouse.pos.y;
SDL_GetMouseState(&s->mouse.pos.x, &s->mouse.pos.y);
s->mouse.pos_delta.x = s->mouse.pos.x - s->mouse.pos_last.x;
s->mouse.pos_delta.y = s->mouse.pos.y - s->mouse.pos_last.y;
} else {
s->mouse.pos_last.x = s->win.size.x/2;
s->mouse.pos_last.y = s->win.size.y/2;
s->mouse.pos.x = s->win.size.x/2;
s->mouse.pos.y = s->win.size.y/2;
}
/* handle events */
{SDL_Event evt;
while (SDL_PollEvent(&evt)) {
if (evt.type == SDL_QUIT)
s->quit = SYS_TRUE;
else if (evt.type == SDL_MOUSEBUTTONDOWN || evt.type == SDL_MOUSEBUTTONUP) {
unsigned down = evt.type == SDL_MOUSEBUTTONDOWN;
if (evt.button.button == SDL_BUTTON_LEFT)
sys_update_button(&s->mouse.left_button, down);
else if (evt.button.button == SDL_BUTTON_RIGHT)
sys_update_button(&s->mouse.right_button, down);
} else if (evt.type == SDL_WINDOWEVENT) {
if (evt.window.event == SDL_WINDOWEVENT_MOVED)
s->win.moved = SYS_TRUE;
else if (evt.window.event == SDL_WINDOWEVENT_RESIZED)
s->win.resized = SYS_TRUE;
} else if (evt.type == SDL_MOUSEMOTION) {
s->mouse.pos_delta.x += evt.motion.xrel;
s->mouse.pos_delta.y += evt.motion.yrel;
} else if (evt.type == SDL_MOUSEWHEEL) {
s->mouse.scroll_delta = -evt.wheel.y;
}
}}
}
static void
sys_push(struct sys *s)
{
SDL_GL_SwapWindow(s->win.handle);
s->time.delta = (SDL_GetTicks() - s->time.start) / 1000.0f;
if (s->time.delta < s->time.refresh_rate)
SDL_Delay((Uint32)((s->time.refresh_rate - s->time.delta) * 1000.0f));
}
/* ============================================================================
*
* APP
*
* =========================================================================== */
int main(void)
{
/* Platform */
struct sys sys;
memset(&sys, 0, sizeof(sys));
sys.win.title = "Demo";
sys.win.size.x = 1200;
sys.win.size.y = 800;
sys_init(&sys);
/* Camera */
struct cam cam;
cam_init(&cam);
cam.pos[2] = 5;
cam_build(&cam);
/* Main */
while (!sys.quit)
{
sys_pull(&sys);
/* Camera control */
#define CAMERA_SPEED (10.0f)
{const float frame_movement = CAMERA_SPEED * sys.time.delta;
const float forward = (sys.key['w'].down ? -1 : 0.0f) + (sys.key['s'].down ? 1 : 0.0f);
const float right = (sys.key['a'].down ? -1 : 0.0f) + (sys.key['d'].down ? 1 : 0.0f);
if (sys.mouse.right_button.down) {
sys.mouse.mode = SYSTEM_MOUSE_RELATIVE;
cam.ear[0] = (float)sys.mouse.pos_delta.y * TO_RAD;
cam.ear[1] = (float)sys.mouse.pos_delta.x * TO_RAD;
} else sys.mouse.mode = SYSTEM_MOUSE_ABSOLUTE;
if (sys.key['e'].down)
cam.ear[2] = 1.0f * TO_RAD;
if (sys.key['r'].down)
cam.ear[2] = -1.0f * TO_RAD;
cam.off[2] += sys.mouse.scroll_delta;
cam.aspect_ratio = (float)sys.win.size.x/(float)sys.win.size.y;
cam_move(&cam, right * frame_movement, 0, forward * frame_movement);
cam_build(&cam);}
/* Keybindings */
if (sys.key['f'].pressed)
sys.win.mode = !sys.win.mode;
if (sys.key['b'].pressed)
printf("break!\n");
/* Rendering */
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glClearColor(0.15f, 0.15f, 0.15f, 1);
glViewport(0, 0, sys.win.size.x, sys.win.size.y);
{
/* 3D */
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glLoadMatrixf((float*)cam.proj);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glLoadMatrixf((float*)cam.view);
/* grid */
glColor3f(0.2f,0.2f,0.2f);
glGrid(-50.0f, 50.0f, -1.0f, 1.7f);
/* basis-axis */
glColor3f(1,0,0);
glArrowf(0,0,0, 1,0,0, 0.1f);
glColor3f(0,1,0);
glArrowf(0,0,0, 0,1,0, 0.1f);
glColor3f(0,0,1);
glArrowf(0,0,0, 0,0,1, 0.1f);
{
/* Triangle-Ray Intersection*/
static float dx = 0, dy = 0;
float ro[3], rd[3];
int suc;
v3 tri[3];
tri[0] = v3mk(-9,1,28);
tri[1] = v3mk(-10,0,28);
tri[2] = v3mk(-11,1,28);
/* ray */
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
f3set(ro, -10,-1,20);
f3set(rd, -10+0.4f*sinf(dx), 2.0f*cosf(dy), 29.81023f);
f3sub(rd, rd, ro);
f3norm(rd);
struct raycast hit;
struct ray r = rayfv(ro, rd);
if ((suc = ray_test_triangle(&hit, r, tri))) {
/* point of intersection */
glColor3f(1,0,0);
glBox(&hit.p.x, 0.10f, 0.10f, 0.10f);
/* intersection normal */
glColor3f(0,0,1);
v3 v = v3add(hit.p, hit.n);
glArrow(&hit.p.x, &v.x, 0.05f);
}
/* line */
glColor3f(1,0,0);
f3mul(rd,rd,10);
f3add(rd,ro,rd);
glLine(ro, rd);
/* triangle */
if (suc) glColor3f(1,0,0);
else glColor3f(1,1,1);
glTriangle(&tri[0].x,&tri[1].x,&tri[2].x);
}
{
/* Plane-Ray Intersection*/
static float d = 0;
struct ray ray;
struct plane plane;
struct raycast hit;
float ro[3], rd[3];
int suc = 0;
m3 rot;
/* ray */
d += sys.time.delta * 2.0f;
f3set(ro, 0,-1,20);
f3set(rd, 0.1f, 0.5f, 9.81023f);
f3sub(rd, rd, ro);
f3norm(rd);
/* rotation */
m3cpy(rot, m3id);
m3rot((float*)rot, d, 0,1,0);
m3mulv(rd,(float*)rot,rd);
/* intersection */
ray = rayfv(ro, rd);
plane = planefv(0,0,28, 0,0,1);
if ((suc = ray_test_plane(&hit, ray, plane))) {
/* point of intersection */
glColor3f(1,0,0);
glBox(&hit.p.x, 0.10f, 0.10f, 0.10f);
/* intersection normal */
glColor3f(0,0,1);
v3 v = v3add(hit.p, hit.n);
glArrow(&hit.p.x, &v.x, 0.05f);
glColor3f(1,0,0);
}
/* line */
glColor3f(1,0,0);
f3mul(rd,rd,9);
f3add(rd,ro,rd);
glLine(ro, rd);
/* plane */
if (suc) glColor3f(1,0,0);
else glColor3f(1,1,1);
glPlanef(0,0,28, 0,0,1, 3.0f);
}
{
/* Sphere-Ray Intersection*/
static float dx = 0, dy = 0;
float ro[3], rd[3];
struct sphere s;
struct raycast hit;
struct ray r;
int suc;
/* ray */
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
f3set(ro, 0,-1,0);
f3set(rd, 0.4f*sinf(dx), 2.0f*cosf(dy), 9.81023f);
f3sub(rd, rd, ro);
f3norm(rd);
r = rayfv(ro, rd);
s = sphere(v3mk(0,0,8), 1);
if ((suc = ray_test_sphere(&hit, r, s))) {
/* points of intersection */
float in[3];
f3mul(in,rd,hit.t0);
f3add(in,ro,in);
glColor3f(1,0,0);
glBox(in, 0.05f, 0.05f, 0.05f);
f3mul(in,rd,hit.t1);
f3add(in,ro,in);
glColor3f(1,0,0);
glBox(in, 0.05f, 0.05f, 0.05f);
/* intersection normal */
glColor3f(0,0,1);
v3 v = v3add(hit.p, hit.n);
glArrow(&hit.p.x, &v.x, 0.05f);
glColor3f(1,0,0);
}
/* line */
glColor3f(1,0,0);
f3mul(rd,rd,10);
f3add(rd,ro,rd);
glLine(ro, rd);
/* sphere */
if (suc) glColor3f(1,0,0);
else glColor3f(1,1,1);
glSpheref(0,0,8, 1);
}
{
/* AABB-Ray Intersection*/
static float dx = 0, dy = 0;
struct ray ray;
struct aabb bounds;
struct raycast hit;
int suc = 0;
float ro[3], rd[3];
/* ray */
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
f3set(ro, 10,-1,0);
f3set(rd, 10+0.4f*sinf(dx), 2.0f*cosf(dy), 9.81023f);
f3sub(rd, rd, ro);
f3norm(rd);
ray = rayfv(ro, rd);
bounds = aabb(v3mk(10-0.5f,-0.5f,7.5f), v3mk(10.5f,0.5f,8.5f));
if ((suc = ray_test_aabb(&hit, ray, bounds))) {
/* points of intersection */
float in[3];
f3mul(in,rd,hit.t0);
f3add(in,ro,in);
glColor3f(1,0,0);
glBox(in, 0.05f, 0.05f, 0.05f);
f3mul(in,rd,hit.t1);
f3add(in,ro,in);
glColor3f(1,0,0);
glBox(in, 0.05f, 0.05f, 0.05f);
/* intersection normal */
glColor3f(0,0,1);
v3 v = v3add(hit.p, hit.n);
glArrow(&hit.p.x, &v.x, 0.05f);
glColor3f(1,0,0);
} else glColor3f(1,1,1);
glBoxf(10,0,8, 1,1,1);
/* line */
glColor3f(1,0,0);
f3mul(rd,rd,10);
f3add(rd,ro,rd);
glLine(ro, rd);
}
{
/* Sphere-Sphere intersection*/
int suc = 0;
struct manifold m;
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
struct sphere a = sphere(v3mk(-10,0,8), 1);
struct sphere b = sphere(v3mk(-10+0.6f*sinf(dx), 3.0f*cosf(dy),8), 1);
if ((suc = sphere_test_sphere_manifold(&m, a, b))) {
float v[3];
glColor3f(0,0,1);
glBox(m.contact_point, 0.05f, 0.05f, 0.05f);
f3add(v, m.contact_point, m.normal);
glArrow(m.contact_point, v, 0.05f);
glColor3f(1,0,0);
} else glColor3f(1,1,1);
glSphere(&a.p.x, 1);
glSphere(&b.p.x, 1);
}
{
/* AABB-AABB intersection*/
int suc = 0;
struct manifold m;
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
const float x = 10+0.6f*sinf(dx);
const float y = 3.0f*cosf(dy);
const float z = 20.0f;
struct aabb a = aabb(v3mk(10-0.5f,-0.5f,20-0.5f), v3mk(10+0.5f,0.5f,20.5f));
struct aabb b = aabbf(x-0.5f,y-0.5f,z-0.5f, x+0.5f,y+0.5f,z+0.5f);
if ((suc = aabb_test_aabb_manifold(&m, a, b))) {
float v[3];
glColor3f(0,0,1);
glBox(m.contact_point, 0.05f, 0.05f, 0.05f);
f3add(v, m.contact_point, m.normal);
glArrow(m.contact_point, v, 0.05f);
glColor3f(1,0,0);
} else glColor3f(1,1,1);
glBoxf(10,0,20, 1,1,1);
glBoxf(x,y,z, 1,1,1);
}
{
/* Capsule-Capsule intersection*/
int suc = 0;
struct manifold m;
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
const float x = 20+0.4f*sinf(dx);
const float y = 3.0f*cosf(dy);
const float z = 28.5f;
struct capsule a = capsulef(20.0f,-1.0f,28.0f, 20.0f,1.0f,28.0f, 0.2f);
struct capsule b = capsulef(x,y-1.0f,z, x,y+1.0f,z-1.0f, 0.2f);
if ((suc = capsule_test_capsule_manifold(&m, a, b))) {
float v[3];
glColor3f(0,0,1);
glBox(m.contact_point, 0.05f, 0.05f, 0.05f);
f3add(v, m.contact_point, m.normal);
glArrow(m.contact_point, v, 0.05f);
glColor3f(1,0,0);
} else glColor3f(1,1,1);
glCapsulef(x,y-1.0f,z, x,y+1.0f,z-1.0f, 0.2f);
glCapsulef(20.0f,-1.0f,28.0f, 20.0f,1.0f,28.0f, 0.2f);
}
{
/* AABB-Sphere intersection*/
int suc = 0;
struct manifold m;
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
struct aabb a = aabb(v3mk(20-0.5f,-0.5f,7.5f), v3mk(20.5f,0.5f,8.5f));
struct sphere s = sphere(v3mk(20+0.6f*sinf(dx), 3.0f*cosf(dy),8), 1);
if ((suc = aabb_test_sphere_manifold(&m, a, s))) {
float v[3];
glColor3f(0,0,1);
glBox(m.contact_point, 0.05f, 0.05f, 0.05f);
f3add(v, m.contact_point, m.normal);
glArrow(m.contact_point, v, 0.05f);
glColor3f(1,0,0);
} else glColor3f(1,1,1);
glBoxf(20,0,8, 1,1,1);
glSphere(&s.p.x, 1);
}
{
/* Sphere-AABB intersection*/
int suc = 0;
struct manifold m;
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
const float x = 10+0.6f*sinf(dx);
const float y = 3.0f*cosf(dy);
const float z = -8.0f;
struct sphere s = sphere(v3mk(10,0,-8), 1);
struct aabb a = aabbf(x-0.5f,y-0.5f,z-0.5f, x+0.5f,y+0.5f,z+0.5f);
if ((suc = sphere_test_aabb_manifold(&m, s, a))) {
float v[3];
glColor3f(0,0,1);
glBox(m.contact_point, 0.05f, 0.05f, 0.05f);
f3add(v, m.contact_point, m.normal);
glArrow(m.contact_point, v, 0.05f);
glColor3f(1,0,0);
} else glColor3f(1,1,1);
glBoxf(x,y,z, 1,1,1);
glSphere(&s.p.x, 1);
}
{
/* Capsule-Sphere intersection*/
int suc = 0;
struct manifold m;
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
struct capsule c = capsulef(-20.5f,-1.0f,7.5f, -20+0.5f,1.0f,8.5f, 0.2f);
struct sphere b = sphere(v3mk(-20+0.6f*sinf(dx), 3.0f*cosf(dy),8), 1);
if ((suc = capsule_test_sphere_manifold(&m, c, b))) {
float v[3];
glColor3f(0,0,1);
glBox(m.contact_point, 0.05f, 0.05f, 0.05f);
f3add(v, m.contact_point, m.normal);
glArrow(m.contact_point, v, 0.05f);
glColor3f(1,0,0);
} else glColor3f(1,1,1);
glSpheref(b.p.x, b.p.y, b.p.z, 1);
glCapsulef(-20.5f,-1.0f,7.5f, -20+0.5f,1.0f,8.5f, 0.2f);
}
{
/* Sphere-Capsule intersection*/
int suc = 0;
struct manifold m;
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
const float x = 20+0.4f*sinf(dx);
const float y = 3.0f*cosf(dy);
const float z = -8;
struct sphere s = sphere(v3mk(20,0,-8), 1);
struct capsule c = capsulef(x,y-1.0f,z, x,y+1.0f,z-1.0f, 0.2f);
if ((suc = sphere_test_capsule_manifold(&m, s, c))) {
float v[3];
glColor3f(0,0,1);
glBox(m.contact_point, 0.05f, 0.05f, 0.05f);
f3add(v, m.contact_point, m.normal);
glArrow(m.contact_point, v, 0.05f);
glColor3f(1,0,0);
} else glColor3f(1,1,1);
glCapsulef(x,y-1.0f,z, x,y+1.0f,z-1.0f, 0.2f);
glSphere(&s.p.x, 1);
}
{
/* Capsule-AABB intersection*/
int suc = 0;
struct manifold m;
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
const float x = -20+0.6f*sinf(dx);
const float y = 3.0f*cosf(dy);
const float z = 28.0f;
struct capsule c = capsulef(-20.5f,-1.0f,27.5f, -20+0.5f,1.0f,28.5f, 0.2f);
struct aabb b = aabbf(x-0.5f,y-0.5f,z-0.5f, x+0.5f,y+0.5f,z+0.5f);
if ((suc = capsule_test_aabb_manifold(&m, c, b))) {
float v[3];
glColor3f(0,0,1);
glBox(m.contact_point, 0.05f, 0.05f, 0.05f);
f3add(v, m.contact_point, m.normal);
glArrow(m.contact_point, v, 0.05f);
glColor3f(1,0,0);
} else glColor3f(1,1,1);
glBoxf(x,y,z, 1,1,1);
glCapsulef(-20.5f,-1.0f,27.5f, -20+0.5f,1.0f,28.5f, 0.2f);
}
{
/* AABB-Capsule intersection*/
int suc = 0;
struct manifold m;
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
const float x = 0.4f*sinf(dx);
const float y = 3.0f*cosf(dy);
const float z = -8;
struct aabb a = aabb(v3mk(-0.5f,-0.5f,-8.5f), v3mk(0.5f,0.5f,-7.5f));
struct capsule c = capsulef(x,y-1.0f,z, x,y+1.0f,z-1.0f, 0.2f);
if ((suc = aabb_test_capsule_manifold(&m, a, c))) {
float v[3];
glColor3f(1,0,0);
glBox(m.contact_point, 0.05f, 0.05f, 0.05f);
f3add(v, m.contact_point, m.normal);
glArrow(m.contact_point, v, 0.05f);
} else glColor3f(1,1,1);
glCapsulef(x,y-1.0f,z, x,y+1.0f,z-1.0f, 0.2f);
glBoxf(0,0,-8.0f, 1,1,1);
}
{
/* Polyhedron(Pyramid)-Sphere (GJK) intersection*/
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
float pyr[15];
struct gjk_result gjk;
struct sphere s = sphere(v3mk(-10+0.6f*sinf(dx), 3.0f*cosf(dy),-8), 1);
pyramid(pyr, -10.5f,-0.5f,-7.5f, -10.5f,1.0f,-7.5f, 1.0f);
if (polyhedron_intersect_sphere(&gjk, pyr, 5, &s.p.x, s.r))
glColor3f(1,0,0);
else glColor3f(1,1,1);
glSphere(&s.p.x, 1);
glPyramidf(-10.5f,-0.5f,-7.5f, -10.5f,1.0f,-7.5f, 1.0f);
glBox(gjk.p0, 0.05f, 0.05f, 0.05f);
glBox(gjk.p1, 0.05f, 0.05f, 0.05f);
glLine(gjk.p0, gjk.p1);
}
{
/* Polyhedron(Diamond)-Sphere (GJK) intersection*/
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
float dmd[18];
struct gjk_result gjk;
struct sphere s = sphere(v3mk(-20+0.6f*sinf(dx), 3.0f*cosf(dy),-8), 1);
diamond(dmd, -20.5f,-0.5f,-7.5f, -20.5f,1.0f,-7.5f, 0.5f);
if (polyhedron_intersect_sphere(&gjk, dmd, 6, &s.p.x, s.r))
glColor3f(1,0,0);
else glColor3f(1,1,1);
glSphere(&s.p.x, 1);
glDiamondf(-20.5f,-0.5f,-7.5f, -20.5f,1.0f,-7.5f, 0.5f);
glBox(gjk.p0, 0.05f, 0.05f, 0.05f);
glBox(gjk.p1, 0.05f, 0.05f, 0.05f);
glLine(gjk.p0, gjk.p1);
}
{
/* Polyhedron(Pyramid)-Capsule (GJK) intersection*/
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
const float x = 0.4f*sinf(dx);
const float y = 3.0f*cosf(dy);
const float z = -15;
float pyr[15];
struct gjk_result gjk;
struct capsule c = capsulef(x,y-1.0f,z, x,y+1.0f,z, 0.2f);
pyramid(pyr, -0.5f,-0.5f,-15.5f, -0.5f,1.0f,-15.5f, 1.0f);
if (polyhedron_intersect_capsule(&gjk, pyr, 5, &c.a.x, &c.b.x, c.r))
glColor3f(1,0,0);
else glColor3f(1,1,1);
glCapsule(&c.a.x, &c.b.x, c.r);
glPyramidf(-0.5f,-0.5f,-15.5f, -0.5f,1.0f,-15.5f, 1.0f);
glBox(gjk.p0, 0.05f, 0.05f, 0.05f);
glBox(gjk.p1, 0.05f, 0.05f, 0.05f);
glLine(gjk.p0, gjk.p1);
}
{
/* Polyhedron(Diamond)-Capsule (GJK) intersection*/
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
const float x = -10 + 0.4f*sinf(dx);
const float y = 3.0f*cosf(dy);
const float z = -15;
float dmd[18];
struct gjk_result gjk;
struct capsule c = capsulef(x,y-1.0f,z, x,y+1.0f,z, 0.2f);
diamond(dmd, -10.5f,-0.5f,-15.5f, -10.5f,1.0f,-15.5f, 0.5f);
if (polyhedron_intersect_capsule(&gjk, dmd, 6, &c.a.x, &c.b.x, c.r))
glColor3f(1,0,0);
else glColor3f(1,1,1);
glCapsule(&c.a.x, &c.b.x, c.r);
glDiamondf(-10.5f,-0.5f,-15.5f, -10.5f,1.0f,-15.5f, 0.5f);
glBox(gjk.p0, 0.05f, 0.05f, 0.05f);
glBox(gjk.p1, 0.05f, 0.05f, 0.05f);
glLine(gjk.p0, gjk.p1);
}
{
/* Polyhedron(Diamond)-Polyhedron(Pyramid) (GJK) intersection*/
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
const float x = -20 + 0.4f*sinf(dx);
const float y = 3.0f*cosf(dy);
const float z = -15;
float dmd[18], pyr[15];
struct gjk_result gjk;
pyramid(pyr, x,y-0.5f,z, x,y+1,z, 0.8f);
diamond(dmd, -20.5f,-0.5f,-15.5f, -20.5f,1.0f,-15.5f, 0.5f);
if (polyhedron_intersect_polyhedron(&gjk, dmd, 6, pyr, 5))
glColor3f(1,0,0);
else glColor3f(1,1,1);
glPyramidf(x,y-0.5f,z, x,y+1,z, 0.8f);
glDiamondf(-20.5f,-0.5f,-15.5f, -20.5f,1.0f,-15.5f, 0.5f);
glBox(gjk.p0, 0.05f, 0.05f, 0.05f);
glBox(gjk.p1, 0.05f, 0.05f, 0.05f);
glLine(gjk.p0, gjk.p1);
}
{
/* Polyhedron(Pyramid)-Polyhedron(Diamond) (GJK) intersection*/
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
const float x = 10 + 0.4f*sinf(dx);
const float y = 3.0f*cosf(dy);
const float z = -15;
float dmd[18], pyr[15];
struct gjk_result gjk;
diamond(dmd, x,y-0.5f,z, x,y+1,z, 0.5f);
pyramid(pyr, 10.5f,-0.5f,-15.5f, 10.5f,1.0f,-15.5f, 1.0f);
if (polyhedron_intersect_polyhedron(&gjk, dmd, 6, pyr, 5))
glColor3f(1,0,0);
else glColor3f(1,1,1);
glDiamondf(x,y-0.5f,z, x,y+1,z, 0.5f);
glPyramidf(10.5f,-0.5f,-15.5f, 10.5f,1.0f,-15.5f, 1.0f);
glBox(gjk.p0, 0.05f, 0.05f, 0.05f);
glBox(gjk.p1, 0.05f, 0.05f, 0.05f);
glLine(gjk.p0, gjk.p1);
}
{
/* Polyhedron(Diamond)-AABB (GJK) intersection*/
static float dx = 0, dy = 0;
dx = dx + sys.time.delta * 2.0f;
dy = dy + sys.time.delta * 0.8f;
const float x = 20 + 0.4f*sinf(dx);
const float y = 3.0f*cosf(dy);
const float z = -15;
float dmd[18];
struct gjk_result gjk;
diamond(dmd, x,y-0.5f,z, x,y+1,z, 0.5f);
struct aabb a = aabbf(19.5f,-0.5f,-14.5f, 20.5f,0.5f,-15.5f);
if (polyhedron_intersect_aabb(&gjk, dmd, 6, &a.min.x, &a.max.x))
glColor3f(1,0,0);
else glColor3f(1,1,1);
glDiamondf(x,y-0.5f,z, x,y+1,z, 0.5f);
glBoxf(20,0,-15, 1,1,1);
glBox(gjk.p0, 0.05f, 0.05f, 0.05f);
glBox(gjk.p1, 0.05f, 0.05f, 0.05f);
glLine(gjk.p0, gjk.p1);
}
}
sys_push(&sys);
}
sys_shutdown(&sys);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment