|
|
@ -36,34 +36,32 @@ normalize_or_zero :: inline (using _v: Vector2) -> Vector2 #must {
|
|
|
|
negative :: (v: Vector2) -> Vector2 #must {
|
|
|
|
negative :: (v: Vector2) -> Vector2 #must {
|
|
|
|
return xy(-v.x, -v.y);
|
|
|
|
return xy(-v.x, -v.y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
drawing_in_world_space: bool = false;
|
|
|
|
|
|
|
|
WHITE :: Vector4.{1.0, 1.0, 1.0, 1.0};
|
|
|
|
WHITE :: Vector4.{1.0, 1.0, 1.0, 1.0};
|
|
|
|
RED :: Vector4.{1.0, 0.0, 0.0, 1.0};
|
|
|
|
RED :: Vector4.{1.0, 0.0, 0.0, 1.0};
|
|
|
|
GREEN :: Vector4.{0.0, 1.0, 0.0, 1.0};
|
|
|
|
GREEN :: Vector4.{0.0, 1.0, 0.0, 1.0};
|
|
|
|
drawing_color: Vector4 = WHITE;
|
|
|
|
DrawingSettings :: struct {
|
|
|
|
line_thickness: float = 1.0;
|
|
|
|
world_space: bool = true;
|
|
|
|
defaults :: () {
|
|
|
|
color: Vector4 = WHITE;
|
|
|
|
drawing_color = WHITE;
|
|
|
|
};
|
|
|
|
line_thickness = 1.0;
|
|
|
|
line :: (using d: DrawingSettings, _from: Vector2, _to: Vector2, thickness: float = 1.0) {
|
|
|
|
}
|
|
|
|
width := thickness;
|
|
|
|
line :: (_from: Vector2, _to: Vector2) {
|
|
|
|
|
|
|
|
width := line_thickness;
|
|
|
|
|
|
|
|
from := _from;
|
|
|
|
from := _from;
|
|
|
|
to := _to;
|
|
|
|
to := _to;
|
|
|
|
if drawing_in_world_space {
|
|
|
|
if world_space {
|
|
|
|
from = world_to_screen(from);
|
|
|
|
from = world_to_screen(from);
|
|
|
|
to = world_to_screen(to);
|
|
|
|
to = world_to_screen(to);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Simp.set_shader_for_color(true);
|
|
|
|
Simp.set_shader_for_color(true);
|
|
|
|
normal := rotate(unit_vector(to - from), PI/2.0);
|
|
|
|
normal := rotate(unit_vector(to - from), PI/2.0);
|
|
|
|
Simp.immediate_quad(from + normal*width, from - normal*width, to + normal*width, to - normal*width, color = drawing_color);
|
|
|
|
Simp.immediate_quad(from + normal*width, from - normal*width, to + normal*width, to - normal*width, color = color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LastingPip :: struct {
|
|
|
|
LastingPip :: struct {
|
|
|
|
alive_for: float;
|
|
|
|
alive_for: float;
|
|
|
|
pos: Vector2;
|
|
|
|
pos: Vector2;
|
|
|
|
|
|
|
|
d: DrawingSettings;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pips : [10] LastingPip;
|
|
|
|
pips : [10] LastingPip;
|
|
|
|
push_pip :: (at: Vector2)
|
|
|
|
push_pip :: (d: DrawingSettings, at: Vector2)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
for * pips
|
|
|
|
for * pips
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -71,413 +69,78 @@ push_pip :: (at: Vector2)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
it.alive_for = 1.0;
|
|
|
|
it.alive_for = 1.0;
|
|
|
|
it.pos = at;
|
|
|
|
it.pos = at;
|
|
|
|
|
|
|
|
it.d = d;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
draw_pips :: (dt: float)
|
|
|
|
draw_pips :: (dt: float)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
drawing_in_world_space = true;
|
|
|
|
|
|
|
|
drawing_color = RED;
|
|
|
|
|
|
|
|
for * pips if it.alive_for > 0.0
|
|
|
|
for * pips if it.alive_for > 0.0
|
|
|
|
{
|
|
|
|
{
|
|
|
|
drawing_color.w = it.alive_for;
|
|
|
|
d := it.d;
|
|
|
|
pip(it.pos);
|
|
|
|
d.color.w *= it.alive_for;
|
|
|
|
|
|
|
|
pip(d, it.pos);
|
|
|
|
it.alive_for -= dt;
|
|
|
|
it.alive_for -= dt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
defaults();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pip :: (_at: Vector2, size: float = 0.05) {
|
|
|
|
pip :: (using d: DrawingSettings, _at: Vector2, size: float = 0.05) {
|
|
|
|
at := _at;
|
|
|
|
at := _at;
|
|
|
|
Simp.set_shader_for_color(true);
|
|
|
|
Simp.set_shader_for_color(true);
|
|
|
|
if drawing_in_world_space {
|
|
|
|
if world_space {
|
|
|
|
at = world_to_screen(at);
|
|
|
|
at = world_to_screen(at);
|
|
|
|
size /= camera.zoom;
|
|
|
|
size /= camera.zoom;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Simp.immediate_quad(at + xy(-size, size), at + xy(size, size), at + xy(size, -size), at + xy(-size, -size), color = drawing_color);
|
|
|
|
Simp.immediate_quad(at + xy(-size, size), at + xy(size, size), at + xy(size, -size), at + xy(-size, -size), color = color);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rect :: struct {
|
|
|
|
|
|
|
|
halfsize: Vector2;
|
|
|
|
|
|
|
|
pos , vel , force : Vector2; // pos is in world space
|
|
|
|
|
|
|
|
angle, angle_vel, torque: float;
|
|
|
|
|
|
|
|
static: bool;
|
|
|
|
|
|
|
|
mass: float = 1.0;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
moment_of_inertia :: (using r: Rect) -> float {
|
|
|
|
|
|
|
|
return mass*(halfsize.y*halfsize.y + halfsize.x*halfsize.x)/12.0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
apply_force_at_point :: (r: *Rect, force: Vector2, point_world_space: Vector2) {
|
|
|
|
|
|
|
|
r.force += force;
|
|
|
|
|
|
|
|
offset_from_center_of_mass := point_world_space - r.pos;
|
|
|
|
|
|
|
|
r.torque += offset_from_center_of_mass.x * force.y - offset_from_center_of_mass.y * force.x;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
apply_impulse_at_point :: (using r: *Rect, impulse: Vector2, point_world_space: Vector2) {
|
|
|
|
|
|
|
|
if !static
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
vel += impulse / mass;
|
|
|
|
|
|
|
|
offset_from_center_of_mass := point_world_space - pos;
|
|
|
|
|
|
|
|
angle_vel += cross(offset_from_center_of_mass, impulse) / moment_of_inertia(r);
|
|
|
|
|
|
|
|
#if DEBUGGING { if isnan(angle_vel) Debug.breakpoint(); }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
velocity_of_point :: (using r: *Rect, point: Vector2) -> Vector2 #must {
|
|
|
|
|
|
|
|
r: Vector2 = point - pos;
|
|
|
|
|
|
|
|
return vel + perp(r) * angle_vel;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// everything needed to resolve the collision
|
|
|
|
ShapeType :: enum {
|
|
|
|
Manifold :: struct {
|
|
|
|
Circle;
|
|
|
|
a, b: *Rect;
|
|
|
|
Rectangle;
|
|
|
|
count: int;
|
|
|
|
|
|
|
|
depths: [2] float;
|
|
|
|
|
|
|
|
contact_points: [2] Vector2; // in absolute coordinates
|
|
|
|
|
|
|
|
normal: Vector2; // always points from shape A to shape B
|
|
|
|
|
|
|
|
};
|
|
|
|
};
|
|
|
|
cross :: (v1: Vector2, v2: Vector2) -> float {
|
|
|
|
|
|
|
|
return v1.x*v2.y - v1.y*v2.x;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a perpendicular vector. (90 degree rotation)
|
|
|
|
|
|
|
|
perp :: (v: Vector2) -> Vector2
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return xy(-v.y, v.x);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
handle_collision :: (m: Manifold, dt: float) {
|
|
|
|
|
|
|
|
a := m.a;
|
|
|
|
|
|
|
|
b := m.b;
|
|
|
|
|
|
|
|
for 0..m.count-1
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
total_momentum: float = length(a.vel) * a.mass + length(b.vel) * b.mass;
|
|
|
|
|
|
|
|
impulse_strength: float = 0.0;
|
|
|
|
|
|
|
|
//impulse_strength += m.depths[it] * 3.0;
|
|
|
|
|
|
|
|
//impulse_strength += total_momentum / 2.0;
|
|
|
|
|
|
|
|
restitution: float = 0.5;
|
|
|
|
|
|
|
|
v_point_a := velocity_of_point(a, m.contact_points[it]);
|
|
|
|
|
|
|
|
v_point_b := velocity_of_point(b, m.contact_points[it]);
|
|
|
|
|
|
|
|
vr := v_point_b - v_point_a;
|
|
|
|
|
|
|
|
r1 := m.contact_points[it] - a.pos;
|
|
|
|
|
|
|
|
r2 := m.contact_points[it] - b.pos;
|
|
|
|
|
|
|
|
I1 := moment_of_inertia(a);
|
|
|
|
|
|
|
|
I2 := moment_of_inertia(b);
|
|
|
|
|
|
|
|
make3d :: (v2: Vector2) -> Vector3 { return .{v2.x, v2.y, 0.0}; }
|
|
|
|
|
|
|
|
make2d :: (v3: Vector3) -> Vector2 { return .{v3.x, v3.y}; }
|
|
|
|
|
|
|
|
cross_products: Vector2 = make2d(cross(cross(make3d(r1), make3d(m.normal))/I1, make3d(r1))) + make2d(cross(cross(make3d(r2), make3d(m.normal)), make3d(r2)))/I2;
|
|
|
|
|
|
|
|
divisor: float = 1.0/a.mass + 1.0/b.mass + dot(cross_products, m.normal);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impulse_strength += (-(1.0 + restitution) * dot(vr, m.normal)) / divisor;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impulse := m.normal * impulse_strength;
|
|
|
|
|
|
|
|
apply_impulse_at_point(b, impulse, m.contact_points[it]);
|
|
|
|
|
|
|
|
apply_impulse_at_point(a, -impulse, m.contact_points[it]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*k_scalar :: (a: Rectangle, b: Rectangle, r1: Vector2, r2: Vector2, n: Vector2) {
|
|
|
|
|
|
|
|
k_scalar_rect :: (a: Rectangle, r: Vector2, n: Vector2)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
rcn := cross(r, n);
|
|
|
|
|
|
|
|
return 1.0/a.mass + rcn*rcn / moment_of_inertia(a);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cpFloat value = k_scalar_rect(a, r1, n) + k_scalar_rect(b, r2, n);
|
|
|
|
|
|
|
|
assert(value != 0.0, "Unsolvable collision or constraint.");
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
r1 := m.contact_points[0] - a.pos;
|
|
|
|
|
|
|
|
r2 := m.contact_points[0] - b.pos;
|
|
|
|
|
|
|
|
nMass := 1.0 / k_scalar(a, b, r1, r2, */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
MAX_POLYGON_VERTS :: 8;
|
|
|
|
|
|
|
|
Polygon :: struct {
|
|
|
|
|
|
|
|
// verts.count needs to equal norms.count
|
|
|
|
|
|
|
|
count: int;
|
|
|
|
|
|
|
|
verts: [MAX_POLYGON_VERTS] Vector2;
|
|
|
|
|
|
|
|
norms: [MAX_POLYGON_VERTS] Vector2;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
// a halfspace (aka plane, aka line)
|
|
|
|
|
|
|
|
Halfspace :: struct {
|
|
|
|
|
|
|
|
n: Vector2;
|
|
|
|
|
|
|
|
d: float;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
CCW90 :: (a: Vector2) -> Vector2 {
|
|
|
|
|
|
|
|
b: Vector2;
|
|
|
|
|
|
|
|
b.x = a.y;
|
|
|
|
|
|
|
|
b.y = -a.x;
|
|
|
|
|
|
|
|
return b;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
distance :: (h: Halfspace, p: Vector2) -> float {
|
|
|
|
|
|
|
|
return dot(h.n, p) - h.d;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
intersect :: (a: Vector2, b: Vector2, da: float, db: float) -> Vector2 {
|
|
|
|
|
|
|
|
return a + (b - a) * (da / (da - db));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
poly_from :: (using r: Rect) -> Polygon {
|
|
|
|
|
|
|
|
to_return: Polygon;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
compute_normals :: (p: *Polygon) {
|
|
|
|
|
|
|
|
for 0..p.count-1 {
|
|
|
|
|
|
|
|
a: int = it;
|
|
|
|
|
|
|
|
b: int = ifx it + 1 < p.count then it + 1 else 0;
|
|
|
|
|
|
|
|
e: Vector2 = p.verts[b] - p.verts[a];
|
|
|
|
|
|
|
|
p.norms[it] = normalize(CCW90(e));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
facing_to_right := rotate(xy(halfsize.x,0.0), angle);
|
|
|
|
|
|
|
|
facing_to_up := rotate(xy(0.0,halfsize.y), angle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
to_return.count = 4;
|
|
|
|
Shape :: struct {
|
|
|
|
|
|
|
|
type: ShapeType;
|
|
|
|
// the ordering here is important for collision algorithms, not exactly sure why.
|
|
|
|
offset: Vector2; // in local space of body
|
|
|
|
// Perhaps it's just important that the winding is counter clockwise
|
|
|
|
radius: float; // only valid on circle
|
|
|
|
to_return.verts[0] = facing_to_right + -facing_to_up; // lower right
|
|
|
|
halfsize: Vector2; // only valid on rectangle
|
|
|
|
to_return.verts[1] = facing_to_right + facing_to_up; // upper right
|
|
|
|
mass: float = 1.0;
|
|
|
|
to_return.verts[2] = -facing_to_right + facing_to_up; // upper left
|
|
|
|
moment_of_inertia: float;
|
|
|
|
to_return.verts[3] = -facing_to_right + -facing_to_up; // lower left
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//for 0..to_return.count-1 to_return.norms[it] = rotate(to_return.norms[it], angle);
|
|
|
|
|
|
|
|
for 0..to_return.count-1 to_return.verts[it] += pos;
|
|
|
|
|
|
|
|
compute_normals(*to_return);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return to_return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
check_faces :: (a: Polygon, b: Polygon) -> (separation: float, face_index: int) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
plane_at :: (p : Polygon, i: int) -> Halfspace {
|
|
|
|
|
|
|
|
return .{n = p.norms[i], d = dot(p.norms[i], p.verts[i])};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
support :: (verts: [] Vector2, d: Vector2) -> int {
|
|
|
|
|
|
|
|
imax: int = 0;
|
|
|
|
|
|
|
|
dmax: float = dot(verts[0], d);
|
|
|
|
|
|
|
|
for 1..verts.count-1 {
|
|
|
|
|
|
|
|
dot_output: float = dot(verts[it], d);
|
|
|
|
|
|
|
|
if(dot_output > dmax)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
imax = it;
|
|
|
|
|
|
|
|
dmax = dot_output;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return imax;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sep : float = -FLOAT32_MAX;
|
|
|
|
|
|
|
|
index: int = ~0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for 0..a.count-1 {
|
|
|
|
|
|
|
|
h := plane_at(a, it);
|
|
|
|
|
|
|
|
verts_to_use: [] Vector2 = b.verts;
|
|
|
|
|
|
|
|
verts_to_use.count = b.count;
|
|
|
|
|
|
|
|
idx := support(verts_to_use, negative(h.n));
|
|
|
|
|
|
|
|
p := b.verts[idx];
|
|
|
|
|
|
|
|
d := distance(h, p);
|
|
|
|
|
|
|
|
if (d > sep)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
sep = d;
|
|
|
|
|
|
|
|
index = it;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return sep, index;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rect_to_rect :: (a: *Rect, b: *Rect) -> Manifold {
|
|
|
|
Body :: struct {
|
|
|
|
to_return : Manifold;
|
|
|
|
pos , vel , force : Vector2; // pos is in world space
|
|
|
|
|
|
|
|
angle, angle_vel, torque: float;
|
|
|
|
poly_a := poly_from(a);
|
|
|
|
static: bool;
|
|
|
|
poly_b := poly_from(b);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sa, ea := check_faces(poly_a, poly_b);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if sa >= 0 return .{};
|
|
|
|
|
|
|
|
sb, eb := check_faces(poly_b, poly_a);
|
|
|
|
|
|
|
|
if sb >= 0 return .{};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rp, ip: *Polygon;
|
|
|
|
|
|
|
|
re: int;
|
|
|
|
|
|
|
|
flip: int;
|
|
|
|
|
|
|
|
kRelTol := 0.95;
|
|
|
|
|
|
|
|
kAbsTol := 0.01;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(sa * kRelTol > sb + kAbsTol)
|
|
|
|
shape: Shape;
|
|
|
|
{
|
|
|
|
|
|
|
|
rp = *poly_a;
|
|
|
|
|
|
|
|
ip = *poly_b;
|
|
|
|
|
|
|
|
re = ea;
|
|
|
|
|
|
|
|
flip = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
rp = *poly_b;
|
|
|
|
|
|
|
|
ip = *poly_a;
|
|
|
|
|
|
|
|
re = eb;
|
|
|
|
|
|
|
|
flip = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
incident: [2] Vector2;
|
|
|
|
|
|
|
|
// calculate incident
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
rn_in_incident_space := rp.norms[re];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
index: int = ~0;
|
|
|
|
local_to_world :: (using b: Body, local_point: Vector2) -> Vector2 {
|
|
|
|
min_dot: float = FLOAT32_MAX;
|
|
|
|
return rotate(local_point, angle) + pos;
|
|
|
|
for 0..ip.count-1 {
|
|
|
|
|
|
|
|
dot_output := dot(rn_in_incident_space, ip.norms[it]);
|
|
|
|
|
|
|
|
if(dot_output < min_dot)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
min_dot = dot_output;
|
|
|
|
|
|
|
|
index = it;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
incident[0] = ip.verts[index];
|
|
|
|
|
|
|
|
incident[1] = ip.verts[ifx index + 1 == ip.count then 0 else index + 1];
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// clip a segment to the "side planes" of another segment.
|
|
|
|
draw_body :: (using b: Body) {
|
|
|
|
// side planes are planes orthogonal to a segment and attached to the
|
|
|
|
if shape.type == {
|
|
|
|
// endpoints of the segment
|
|
|
|
case .Circle;
|
|
|
|
// side planes from poly
|
|
|
|
d: DrawingSettings = .{true, GREEN};
|
|
|
|
rh: Halfspace;
|
|
|
|
POINTS :: 31;
|
|
|
|
side_planes_poly_return: int;
|
|
|
|
drawing_circle_at: Vector2 = local_to_world(b, shape.offset);
|
|
|
|
{
|
|
|
|
for 0..POINTS {
|
|
|
|
seg: [2] Vector2 = incident;
|
|
|
|
theta: float = (it/cast(float)POINTS) * PI*2.0;
|
|
|
|
p: *Polygon = rp;
|
|
|
|
theta_next: float = (it+1)/cast(float)POINTS * PI*2.0;
|
|
|
|
e: int = re;
|
|
|
|
from := drawing_circle_at + .{cos(theta),sin(theta)} * shape.radius;
|
|
|
|
|
|
|
|
to := drawing_circle_at + .{cos(theta_next),sin(theta_next)} * shape.radius;
|
|
|
|
ra := p.verts[e];
|
|
|
|
line(d, from, to);
|
|
|
|
rb := p.verts[ifx e + 1 == p.count then 0 else e + 1];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// side planes
|
|
|
|
|
|
|
|
side_planes :: (seg: [] Vector2, ra: Vector2, rb: Vector2) -> int, Halfspace {
|
|
|
|
|
|
|
|
in: Vector2 = normalize(rb - ra);
|
|
|
|
|
|
|
|
left: Halfspace = .{ n = negative(in), d = dot(negative(in), ra) };
|
|
|
|
|
|
|
|
right: Halfspace = .{ n = in, d = dot(in, rb) };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// clip a segment to a plane
|
|
|
|
|
|
|
|
clip :: (seg: [] Vector2, h: Halfspace) -> int {
|
|
|
|
|
|
|
|
out: [2] Vector2;
|
|
|
|
|
|
|
|
sp: int = 0;
|
|
|
|
|
|
|
|
d0: float = distance(h, seg[0]);
|
|
|
|
|
|
|
|
d1: float = distance(h, seg[1]);
|
|
|
|
|
|
|
|
if d0 < 0 { out[sp] = seg[0]; sp+=1; }
|
|
|
|
|
|
|
|
if d1 < 0 { out[sp] = seg[1]; sp+=1; }
|
|
|
|
|
|
|
|
if d0 == 0 && d1 == 0 {
|
|
|
|
|
|
|
|
out[sp] = seg[0];
|
|
|
|
|
|
|
|
sp+=1;
|
|
|
|
|
|
|
|
out[sp] = seg[1];
|
|
|
|
|
|
|
|
sp+=1;
|
|
|
|
|
|
|
|
} else if d0 * d1 <= 0 {
|
|
|
|
|
|
|
|
out[sp] = intersect(seg[0], seg[1], d0, d1);
|
|
|
|
|
|
|
|
sp+=1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
seg[0] = out[0];
|
|
|
|
|
|
|
|
seg[1] = out[1];
|
|
|
|
|
|
|
|
return sp;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if clip(seg, left) < 2 return 0, .{};
|
|
|
|
|
|
|
|
if clip(seg, right) < 2 return 0, .{};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
h: Halfspace;
|
|
|
|
|
|
|
|
h.n = CCW90(in);
|
|
|
|
|
|
|
|
h.d = dot(CCW90(in), ra);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 1, h;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
side_planes_poly_return, rh = side_planes(seg, ra, rb);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if !side_planes_poly_return return .{};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m: Manifold;
|
|
|
|
|
|
|
|
// keep deep
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
seg: [2] Vector2 = incident;
|
|
|
|
|
|
|
|
h: Halfspace = rh;
|
|
|
|
|
|
|
|
cp: int = 0;
|
|
|
|
|
|
|
|
for 0..1 {
|
|
|
|
|
|
|
|
p := seg[it];
|
|
|
|
|
|
|
|
d: float = distance(h, p);
|
|
|
|
|
|
|
|
if (d <= 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
m.contact_points[cp] = p;
|
|
|
|
|
|
|
|
m.depths[cp] = -d;
|
|
|
|
|
|
|
|
cp+=1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
m.count = cp;
|
|
|
|
|
|
|
|
m.normal = h.n;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if flip m.normal = negative(m.normal);
|
|
|
|
|
|
|
|
for 0..m.count-1 {
|
|
|
|
|
|
|
|
push_pip(m.contact_points[it]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
m.a = a;
|
|
|
|
|
|
|
|
m.b = b;
|
|
|
|
|
|
|
|
return m;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
dump_polygon :: (poly: Polygon) {
|
|
|
|
|
|
|
|
for poly.verts print("V(%, %), ", it.x, it.y);
|
|
|
|
|
|
|
|
print("\n");
|
|
|
|
|
|
|
|
for poly.norms print("VN(%, %), ", it.x, it.y);
|
|
|
|
|
|
|
|
print("\n");
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
do_test :: () {
|
|
|
|
case .Rectangle;
|
|
|
|
print("Doing test\n");
|
|
|
|
assert(false);
|
|
|
|
|
|
|
|
|
|
|
|
size := xy(1.0, 1.0);
|
|
|
|
|
|
|
|
A_rect: Rect = .{halfsize = size};
|
|
|
|
|
|
|
|
B_rect: Rect = .{halfsize = size, pos = xy(0.5, 0.0)};
|
|
|
|
|
|
|
|
#if true {
|
|
|
|
|
|
|
|
print("A %\n", A_rect);
|
|
|
|
|
|
|
|
print("B %\n", B_rect);
|
|
|
|
|
|
|
|
print("Collision %\n", rect_to_rect(*A_rect, *B_rect));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if false {
|
|
|
|
|
|
|
|
A: Polygon;
|
|
|
|
|
|
|
|
B: Polygon;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A = poly_from(A_rect);
|
|
|
|
|
|
|
|
B = poly_from(B_rect);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print("A\n");
|
|
|
|
|
|
|
|
dump_polygon(A);
|
|
|
|
|
|
|
|
print("B\n");
|
|
|
|
|
|
|
|
dump_polygon(B);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*for A.verts print("V(%,%), ", it.x, it.y);
|
|
|
|
|
|
|
|
print("\n");
|
|
|
|
|
|
|
|
for A.norms print("VN(%,%), ", it.x, it.y);
|
|
|
|
|
|
|
|
print("\n");*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*A.count = 3;
|
|
|
|
|
|
|
|
A.verts[0] = xy(-1.0, 0.0);
|
|
|
|
|
|
|
|
A.verts[1] = xy(-1.0, 1.0);
|
|
|
|
|
|
|
|
A.verts[2] = xy(0.0, 0.0);
|
|
|
|
|
|
|
|
A.norms[0] = #run normalize(xy(-1.0, 0.0));
|
|
|
|
|
|
|
|
A.norms[1] = #run normalize(xy(0.0, 1.0));
|
|
|
|
|
|
|
|
A.norms[2] = #run normalize(xy(1.0, 0.0));
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*B.count = 3;
|
|
|
|
|
|
|
|
B.verts[0] = xy(2.0, 0.0);
|
|
|
|
|
|
|
|
B.verts[1] = xy(2.0, 1.0);
|
|
|
|
|
|
|
|
B.verts[2] = xy(1.0, 0.0);
|
|
|
|
|
|
|
|
B.norms[0] = #run normalize(xy(1.0, 0.0));
|
|
|
|
|
|
|
|
B.norms[1] = #run normalize(xy(0.0, 1.0));
|
|
|
|
|
|
|
|
B.norms[2] = #run normalize(xy(-1.0, 0.0));
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//for 0..B.count-1 B.verts[it] += xy(-0.5, 0.0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
seperation, face_index := check_faces(A, B);
|
|
|
|
|
|
|
|
print("Separation % face_index %\n", seperation, face_index);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Debug.breakpoint();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
draw_rect :: (using r: Rect) {
|
|
|
|
draw_rect :: (using r: Shape) {
|
|
|
|
|
|
|
|
assert(type == .Rectangle);
|
|
|
|
facing_to_right := rotate(xy(halfsize.x,0.0), angle);
|
|
|
|
facing_to_right := rotate(xy(halfsize.x,0.0), angle);
|
|
|
|
facing_to_up := rotate(xy(0.0,halfsize.y), angle);
|
|
|
|
facing_to_up := rotate(xy(0.0,halfsize.y), angle);
|
|
|
|
|
|
|
|
|
|
|
@ -491,6 +154,8 @@ draw_rect :: (using r: Rect) {
|
|
|
|
line(lower_right, lower_left);
|
|
|
|
line(lower_right, lower_left);
|
|
|
|
line(lower_left, upper_left);
|
|
|
|
line(lower_left, upper_left);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mouse :: () -> Vector2 {
|
|
|
|
mouse :: () -> Vector2 {
|
|
|
|
x, y := get_mouse_pointer_position();
|
|
|
|
x, y := get_mouse_pointer_position();
|
|
|
@ -521,12 +186,6 @@ world_to_screen :: (world: Vector2) -> Vector2 {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
main :: () {
|
|
|
|
main :: () {
|
|
|
|
#if false
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
do_test();
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
window := create_window(window_width, window_height, "A Window");
|
|
|
|
window := create_window(window_width, window_height, "A Window");
|
|
|
|
// Actual render size in pixels can be different from the window dimensions we specified above (for example on high-resolution displays on macOS/iOS).
|
|
|
|
// Actual render size in pixels can be different from the window dimensions we specified above (for example on high-resolution displays on macOS/iOS).
|
|
|
|
window_width, window_height = Simp.get_render_dimensions(window);
|
|
|
|
window_width, window_height = Simp.get_render_dimensions(window);
|
|
|
@ -536,11 +195,8 @@ main :: () {
|
|
|
|
|
|
|
|
|
|
|
|
Simp.set_render_target(window);
|
|
|
|
Simp.set_render_target(window);
|
|
|
|
|
|
|
|
|
|
|
|
rects: [..]Rect;
|
|
|
|
bodies: [..]Body;
|
|
|
|
//array_add(*rects, .{pos = #run xy(0.0, 0.0), halfsize = #run xy(0.3)});
|
|
|
|
array_add(*bodies, .{pos = .{0.0, 0.0}, shape = .{type = .Circle, radius = 1.0}});
|
|
|
|
array_add(*rects, .{pos = #run xy(2.5, 0.0), halfsize = #run xy(0.3)});
|
|
|
|
|
|
|
|
//array_add(*rects, .{pos = #run xy(-2.5, 0.0), halfsize = #run xy(0.3)});
|
|
|
|
|
|
|
|
array_add(*rects, .{pos = #run xy(0.0, -3.0), halfsize = #run xy(3.0, 0.2), static = true});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
quit := false;
|
|
|
|
quit := false;
|
|
|
|
last_time := get_time();
|
|
|
|
last_time := get_time();
|
|
|
@ -579,7 +235,7 @@ main :: () {
|
|
|
|
// process physics
|
|
|
|
// process physics
|
|
|
|
unprocessed_time += dt;
|
|
|
|
unprocessed_time += dt;
|
|
|
|
{
|
|
|
|
{
|
|
|
|
dt: string = "do not use";
|
|
|
|
dt: string = "do not use, physics processes with TIMESTEP not dt";
|
|
|
|
while unprocessed_time > TIMESTEP
|
|
|
|
while unprocessed_time > TIMESTEP
|
|
|
|
{
|
|
|
|
{
|
|
|
|
defer unprocessed_time -= TIMESTEP;
|
|
|
|
defer unprocessed_time -= TIMESTEP;
|
|
|
@ -588,43 +244,24 @@ main :: () {
|
|
|
|
{
|
|
|
|
{
|
|
|
|
//apply_force_at_point(*rects[0], xy(3, 0), rects[0].pos + xy(-0.1, 0.03));
|
|
|
|
//apply_force_at_point(*rects[0], xy(3, 0), rects[0].pos + xy(-0.1, 0.03));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
collisions: [..] Manifold;
|
|
|
|
for * bodies {
|
|
|
|
defer array_free(collisions);
|
|
|
|
|
|
|
|
for * from_rect: rects {
|
|
|
|
|
|
|
|
for * to_rect: rects {
|
|
|
|
|
|
|
|
if to_rect != from_rect
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
manifold_out := rect_to_rect(from_rect, to_rect);
|
|
|
|
|
|
|
|
if manifold_out.count > 0 {
|
|
|
|
|
|
|
|
unique := true;
|
|
|
|
|
|
|
|
for collisions {
|
|
|
|
|
|
|
|
if (it.a == manifold_out.a && it.b == manifold_out.b) || (it.a == manifold_out.b && it.b == manifold_out.a) {
|
|
|
|
|
|
|
|
unique = false;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if unique array_add(*collisions, manifold_out);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for collisions {
|
|
|
|
|
|
|
|
handle_collision(it, TIMESTEP);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for * rects {
|
|
|
|
|
|
|
|
defer it.force = .{};
|
|
|
|
defer it.force = .{};
|
|
|
|
defer it.torque = 0.0;
|
|
|
|
defer it.torque = 0.0;
|
|
|
|
|
|
|
|
|
|
|
|
// gravity
|
|
|
|
// calculate moment of inertia
|
|
|
|
it.force.y += -9.81;
|
|
|
|
if it.shape.type == {
|
|
|
|
|
|
|
|
case .Circle;
|
|
|
|
|
|
|
|
it.shape.moment_of_inertia = PI * pow(it.shape.radius, 4.0) / 4.0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//if !it.static dbgprint("%\n", it.angle_vel);
|
|
|
|
// gravity
|
|
|
|
|
|
|
|
//it.force.y += -9.81;
|
|
|
|
|
|
|
|
|
|
|
|
if !it.static
|
|
|
|
if !it.static
|
|
|
|
{
|
|
|
|
{
|
|
|
|
it.vel += (it.force/it.mass) * TIMESTEP;
|
|
|
|
it.vel += (it.force/it.shape.mass) * TIMESTEP;
|
|
|
|
it.pos += it.vel * TIMESTEP;
|
|
|
|
it.pos += it.vel * TIMESTEP;
|
|
|
|
it.angle_vel += (it.torque / moment_of_inertia(it)) * TIMESTEP;
|
|
|
|
it.angle_vel += (it.torque / it.shape.moment_of_inertia) * TIMESTEP;
|
|
|
|
it.angle += it.angle_vel * TIMESTEP;
|
|
|
|
it.angle += it.angle_vel * TIMESTEP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -637,24 +274,15 @@ main :: () {
|
|
|
|
Simp.clear_render_target(0.0, 0.0, 0.0, 1.0);
|
|
|
|
Simp.clear_render_target(0.0, 0.0, 0.0, 1.0);
|
|
|
|
|
|
|
|
|
|
|
|
// draw grid
|
|
|
|
// draw grid
|
|
|
|
drawing_in_world_space = true;
|
|
|
|
grid_d: DrawingSettings = .{true,.{0.2, 0.2, 0.2, 0.5}};
|
|
|
|
drawing_color = .{0.2, 0.2, 0.2, 0.5};
|
|
|
|
|
|
|
|
for x: -30..30 {
|
|
|
|
for x: -30..30 {
|
|
|
|
line(xy(x, 30), xy(x, -30));
|
|
|
|
line(grid_d, xy(x, 30), xy(x, -30));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for y: -30..30 {
|
|
|
|
for y: -30..30 {
|
|
|
|
line(xy(30, y), xy(-30, y));
|
|
|
|
line(grid_d, xy(30, y), xy(-30, y));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
defaults();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
draw_pips(dt);
|
|
|
|
draw_pips(dt);
|
|
|
|
|
|
|
|
for bodies draw_body(it);
|
|
|
|
drawing_in_world_space = true;
|
|
|
|
|
|
|
|
line_thickness = 2.0;
|
|
|
|
|
|
|
|
drawing_color = GREEN;
|
|
|
|
|
|
|
|
for rects draw_rect(it);
|
|
|
|
|
|
|
|
line_thickness = 1.0;
|
|
|
|
|
|
|
|
defaults();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Simp.immediate_flush();
|
|
|
|
Simp.immediate_flush();
|
|
|
|
|
|
|
|
|
|
|
|