#ifndef CHAD_PHYS_H
#define CHAD_PHYS_H
#include "3dMath.h"
typedef struct {
aabb shape; mat4 localt; vec3 v; vec3 a; f_ mass; f_ bounciness; f_ airfriction; f_ friction; } phys_body;
typedef struct{
vec3 g; phys_body** bodies;
f_ ms; long nbodies; char is_2d; } phys_world;
static inline void initPhysBody(phys_body* body){
body->shape = (aabb){
.c=(vec4){.d[0] = 0,.d[1] = 0,.d[2] = 0,.d[3] = 0},
.e=(vec3){.d[0] = 0,.d[1] = 0,.d[2] = 0}
};
body->mass = 0;
body->bounciness = 0;
body->friction = 0.99; body->airfriction = 0.99;
body->a = (vec3){.d[0] = 0,.d[1] = 0,.d[2] = 0};
body->localt = identitymat4();
}
static inline mat4 getPhysBodyRenderTransform(phys_body* body){
return multm4(
translate(downv4(body->shape.c)),
body->localt
);
}
static inline void resolveBodies(phys_body* a, phys_body* b){
vec4 penvec; vec3 penvecnormalized, comvel; f_ friction, bdisplacefactor, adisplacefactor;
if(a->mass > 0 || b->mass > 0){
} else {return;}
if(a->mass < -0 || b->mass < -0) return;
penvec = (vec4){
.d[0]=0,
.d[1]=0,
.d[2]=0,
.d[3]=0
};
if(a->shape.c.d[3] > 0 && b->shape.c.d[3] > 0) {
penvec = spherevsphere(a->shape.c, b->shape.c);
} else if(a->shape.c.d[3] <= 0 && b->shape.c.d[3] <= 0) {
penvec = boxvbox(a->shape,b->shape);
} else if (a->shape.c.d[3] > 0 && b->shape.c.d[3] <= 0) {
penvec = spherevaabb(a->shape.c,b->shape);
} else if (a->shape.c.d[3] <= 0 && b->shape.c.d[3] > 0){ penvec = spherevaabb(b->shape.c,a->shape);
penvec.d[0] *= -1;
penvec.d[1] *= -1;
penvec.d[2] *= -1;
}
#ifdef CHADPHYS_DEBUG
else {
puts("\nInvalid configuration. Error.\n");
}
#endif
if(penvec.d[3] <= 0.0){return;} penvecnormalized = scalev3(1.0/penvec.d[3], downv4(penvec)); friction = a->friction * b->friction;
bdisplacefactor = a->mass / (a->mass + b->mass);
adisplacefactor = b->mass / (a->mass + b->mass);
if(!(a->mass > 0)) {
adisplacefactor = 0; bdisplacefactor = 1;
}else if(!(b->mass > 0)) {
bdisplacefactor = 0; adisplacefactor = 1;
}
comvel = addv3( scalev3(bdisplacefactor, a->v), scalev3(adisplacefactor, b->v));
if(a->mass > 0){
vec4 displacea; vec3 a_relvel, a_planarvel;
displacea = scalev4(-adisplacefactor, penvec);
a_relvel = subv3(a->v, comvel);
float test = dotv3(a_relvel, penvecnormalized);
if(test > 0)
a_planarvel = subv3(a_relvel,
scalev3(
test,
penvecnormalized
)
);
else
a_planarvel = a_relvel;
a->shape.c.d[0] += displacea.d[0];
a->shape.c.d[1] += displacea.d[1];
a->shape.c.d[2] += displacea.d[2];
a->v = addv3( comvel, scalev3(friction, a_planarvel) ); a->v = addv3(a->v, scalev3( a->bounciness, downv4(displacea) ) );
}
if(b->mass > 0){
vec4 displaceb; vec3 b_relvel, b_planarvel;
displaceb = scalev4(bdisplacefactor, penvec);
b_relvel = subv3(b->v, comvel);
float test = dotv3(b_relvel, penvecnormalized); if(test < 0)
b_planarvel = subv3(b_relvel, scalev3(
test, penvecnormalized )
);
else
b_planarvel = b_relvel;
b->shape.c.d[0] += displaceb.d[0];
b->shape.c.d[1] += displaceb.d[1];
b->shape.c.d[2] += displaceb.d[2];
b->v = addv3(comvel, scalev3(friction, b_planarvel) ); b->v = addv3(b->v, scalev3( b->bounciness, downv4(displaceb) ) );
}
}
static inline void stepPhysWorld(phys_world* world, const long collisioniter){
for(long i = 0; i < world->nbodies; i++)
if(world->bodies[i] && world->bodies[i]->mass > 0){
phys_body* body = world->bodies[i];
vec3 bodypos = addv3(downv4(body->shape.c),body->v);
body->shape.c.d[0] = bodypos.d[0];
body->shape.c.d[1] = bodypos.d[1];
body->shape.c.d[2] = bodypos.d[2];
body->v = addv3(body->v, body->a);
body->v = addv3(body->v, world->g);
if( (dotv3(body->v, body->v) > world->ms * world->ms)){
body->v = normalizev3(body->v);
body->v = scalev3(world->ms, body->v);
}
body->v = scalev3(body->airfriction, body->v);
if(world->is_2d) {body->shape.c.d[2] = 0;}
}
for(long iter = 0; iter < collisioniter; iter++)
for(long i = 0; i < (long)(world->nbodies-1); i++){
if(world->bodies[i]){
for(long j = i+1; j < (long)world->nbodies; j++)
if(world->bodies[j])
resolveBodies(world->bodies[i], world->bodies[j]);
}
}
}
#endif