summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/physics.h25
-rw-r--r--libphysics/Makefile21
-rw-r--r--libphysics/_map.c20
-rw-r--r--libphysics/aabb.c52
-rw-r--r--libphysics/body.c81
-rw-r--r--libphysics/dat.h24
-rw-r--r--libphysics/event.c44
-rw-r--r--libphysics/test/Makefile19
-rw-r--r--libphysics/test/vis.c120
-rw-r--r--libphysics/tick.c138
10 files changed, 544 insertions, 0 deletions
diff --git a/include/physics.h b/include/physics.h
new file mode 100644
index 0000000..8a43915
--- /dev/null
+++ b/include/physics.h
@@ -0,0 +1,25 @@
+typedef enum {
+ BTYPE_AABB,
+ BTYPE_TILEMAP,
+} BodyType;
+
+typedef struct Body Body;
+typedef struct CollisionEvent {
+ Body *body1;
+ Body *body2;
+} CollisionEvent;
+
+Body *phxnew(BodyType type);
+void phxdel(Body *);
+
+void phxsetmap(int w, int h, int *tiles);
+
+void phxsetpos(Body *, float x, float y);
+void phxsetsize(Body *, float w, float h);
+void phxapplyaccel(Body *, float f[2]);
+
+void phxgetpos(Body *, float pos[2]);
+void phxgetsize(Body *, float size[2]);
+
+int phxnextcollevent(CollisionEvent *ev);
+void phxtick(float delta);
diff --git a/libphysics/Makefile b/libphysics/Makefile
new file mode 100644
index 0000000..cc76e28
--- /dev/null
+++ b/libphysics/Makefile
@@ -0,0 +1,21 @@
+OBJ=aabb.o\
+ body.o\
+ event.o\
+ _map.o\
+ tick.o
+
+CFLAGS=-I../include -g
+
+all: libphysics.a
+
+clean:
+ rm -f libphysics.a
+ rm -f $(OBJ)
+
+libphysics.a: $(OBJ)
+ ar rcs $@ $^
+
+%.o: %.c
+ $(CC) $< $(CFLAGS) -c -o $@
+
+.PHONY: all clean
diff --git a/libphysics/_map.c b/libphysics/_map.c
new file mode 100644
index 0000000..06d3cad
--- /dev/null
+++ b/libphysics/_map.c
@@ -0,0 +1,20 @@
+#include <vecmath.h>
+#include <physics.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dat.h"
+
+int *phxmapbuffer;
+int phxmapwidth, phxmapheight;
+
+void
+phxsetmap(int w, int h, int *tiles)
+{
+ if(phxmapbuffer)
+ free(phxmapbuffer);
+
+ phxmapbuffer = malloc(w * h * sizeof(phxmapbuffer[0]));
+ memcpy(phxmapbuffer, tiles, sizeof(phxmapbuffer[0]) * w * h);
+}
diff --git a/libphysics/aabb.c b/libphysics/aabb.c
new file mode 100644
index 0000000..59648c8
--- /dev/null
+++ b/libphysics/aabb.c
@@ -0,0 +1,52 @@
+#include <vecmath.h>
+#include <physics.h>
+#include <stdlib.h>
+
+#include "dat.h"
+
+static void mink(Body *a, Body *b, vec2 min, vec2 max);
+
+int
+phxaabbcheck(Body *a, Body *b)
+{
+ vec2 min, max;
+ mink(a, b, min, max);
+ return min[0] < 0 && max[0] > 0 && min[1] < 0 && max[1] > 0;
+}
+
+void
+phxaabbresolv(Body *a, Body *b, vec2 p, vec2 n)
+{
+ vec2 min, max;
+ mink(a, b, min, max);
+
+ float hx = fabsf(min[0]) < fabsf(max[0]) ? fabsf(min[0]) : fabsf(max[0]);
+ float hy = fabsf(min[1]) < fabsf(max[1]) ? fabsf(min[1]) : fabsf(max[1]);
+
+ if(hx < hy) {
+ n[1] = 0.0;
+ n[0] = (fabsf(min[0]) < fabsf(max[0]) ? -1.0 : 1.0);
+ } else {
+ n[0] = 0;
+ n[1] = (fabsf(min[1]) < fabsf(max[1]) ? -1.0 : 1.0);
+ }
+
+ vec2_mul(p, (vec2){ hx, hy }, n);
+}
+
+
+void
+mink(Body *a, Body *b, vec2 min, vec2 max)
+{
+ float p[2], s[2];
+
+ for(int i = 0; i < 2; i++) {
+ s[i] = a->size[i] + b->size[i];
+ p[i] = a->pos[i] - b->pos[i];
+ }
+
+ for(int i = 0; i < 2; i++) {
+ min[i] = p[i] - s[i];
+ max[i] = p[i] + s[i];
+ }
+}
diff --git a/libphysics/body.c b/libphysics/body.c
new file mode 100644
index 0000000..4ea05bd
--- /dev/null
+++ b/libphysics/body.c
@@ -0,0 +1,81 @@
+#include <vecmath.h>
+#include <physics.h>
+#include <stdlib.h>
+
+#include "dat.h"
+
+#define POOL_SIZE 2048
+
+Body phxbodypool[POOL_SIZE];
+int phxbodypoolsize;
+
+static Body *allocbody(void);
+
+Body *
+phxnew(BodyType type)
+{
+ Body *b;
+
+ if((b = allocbody()) == NULL)
+ return b;
+
+ b->type = type;
+ return b;
+}
+
+void
+phxdel(Body *b)
+{
+ b->active = 0;
+}
+
+void
+phxsetpos(Body *b, float x, float y)
+{
+ b->pos[0] = x;
+ b->pos[1] = y;
+}
+
+void
+phxsetsize(Body *b, float w, float h)
+{
+ b->size[0] = w;
+ b->size[1] = h;
+}
+
+void
+phxgetpos(Body *b, float p[2])
+{
+ vec2_dup(p, b->pos);
+}
+
+void
+phxgetsize(Body *b, float p[2])
+{
+ vec2_dup(p, b->size);
+}
+
+void
+phxapplyaccel(Body *b, float a[2])
+{
+ vec2_add(b->accel, b->accel, a);
+}
+
+Body *
+allocbody(void)
+{
+ int i;
+ for(i = 0; i < phxbodypoolsize; i++) {
+ if(!phxbodypool[i].active)
+ break;
+ }
+
+ if(i >= phxbodypoolsize) {
+ if(phxbodypoolsize == POOL_SIZE)
+ return NULL;
+ i = phxbodypoolsize++;
+ }
+
+ phxbodypool[i].active = 1;
+ return phxbodypool + i;
+}
diff --git a/libphysics/dat.h b/libphysics/dat.h
new file mode 100644
index 0000000..75869a4
--- /dev/null
+++ b/libphysics/dat.h
@@ -0,0 +1,24 @@
+typedef struct Body {
+ BodyType type;
+ int active;
+ vec2 pos;
+ vec2 size;
+ vec2 vel;
+ vec2 accel;
+} Body;
+
+void phxmapcollision(Body *);
+void phxbodycollision(Body *, Body *);
+
+int phxaabbcheck(Body *a, Body *b);
+void phxcollisionmap(Body *a);
+void phxaabbresolv(Body *a, Body *b, vec2 p, vec2 n);
+
+int phxenqevent(CollisionEvent *ev);
+int phxdeqevent(CollisionEvent *ev);
+
+extern int phxmapwidth, phxmapheight;
+extern int *phxmapbuffer;
+
+extern Body phxbodypool[];
+extern int phxbodypoolsize;
diff --git a/libphysics/event.c b/libphysics/event.c
new file mode 100644
index 0000000..da836f6
--- /dev/null
+++ b/libphysics/event.c
@@ -0,0 +1,44 @@
+#include <vecmath.h>
+#include <physics.h>
+#include <stdlib.h>
+
+#include "dat.h"
+
+#define EVENT_POOL_SIZE 2048
+
+static CollisionEvent event[EVENT_POOL_SIZE];
+static int eventi, evente, events;
+
+int
+phxenqevent(CollisionEvent *ev)
+{
+ if(events == EVENT_POOL_SIZE)
+ return 0;
+
+ event[evente] = *ev;
+ evente = (evente + 1) % EVENT_POOL_SIZE;
+ events++;
+
+ return 1;
+}
+
+int
+phxdeqevent(CollisionEvent *ev)
+{
+ CollisionEvent *evv;
+ if(events == 0)
+ return 0;
+
+ evv = event + eventi;
+ eventi = (eventi + 1) % EVENT_POOL_SIZE;
+
+ *ev = *evv;
+ events--;
+ return 1;
+}
+
+int
+phxnextcollevent(CollisionEvent *ev)
+{
+ return phxdeqevent(ev);
+}
diff --git a/libphysics/test/Makefile b/libphysics/test/Makefile
new file mode 100644
index 0000000..eb1d5cb
--- /dev/null
+++ b/libphysics/test/Makefile
@@ -0,0 +1,19 @@
+CFLAGS=-I../../include -g
+LDFLAGS=-L../ -lphysics `pkg-config --libs sdl3`
+
+all: vis
+
+clean:
+ rm -f vis
+ rm -f vis.o
+
+vis: ../libphysics.a vis.o
+ $(CC) $^ $(LDFLAGS) -o $@
+
+%.o: %.c
+ $(CC) $< $(CFLAGS) -c -o $@
+
+../libphysics.a:
+ $(MAKE) -C .. all
+
+.PHONY: all clean
diff --git a/libphysics/test/vis.c b/libphysics/test/vis.c
new file mode 100644
index 0000000..d1f3f6f
--- /dev/null
+++ b/libphysics/test/vis.c
@@ -0,0 +1,120 @@
+#include <SDL3/SDL.h>
+#include <vecmath.h>
+#include <physics.h>
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "../dat.h"
+
+#define SBODY 256
+
+static void process_events(void);
+static void render(void);
+
+static SDL_Window *window;
+static SDL_Renderer *renderer;
+static bool running;
+static Uint64 old_ticks, new_ticks;
+
+void
+renderrect(vec2 p, vec2 s)
+{
+ SDL_RenderRect(renderer, &(SDL_FRect){
+ .x = p[0] - s[0],
+ .y = p[1] - s[1],
+ .w = s[0] * 2.0,
+ .h = s[1] * 2.0
+ });
+}
+
+int
+main()
+{
+ if(!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
+ fprintf(stderr, "SDL_Init(): %s\n", SDL_GetError());
+ return 1;
+ }
+
+ if(!SDL_CreateWindowAndRenderer("window", 800, 600, SDL_WINDOW_OPENGL, &window, &renderer)) {
+ fprintf(stderr, "SDL_CreateWindowAndRenderer: %s\n", SDL_GetError());
+ return -1;
+ }
+ SDL_SetRenderVSync(renderer, 1);
+
+ #define FLOAT_RAND (2 * (rand() / (float)RAND_MAX) - 1)
+
+ for(int i = 0; i < SBODY; i++) {
+ Body *b = phxnew(BTYPE_AABB);
+ assert(b != NULL);
+ phxsetpos(b, rand() % 800, rand() % 600);
+ phxsetsize(b, 16.0, 16.0);
+ phxapplyaccel(b, (float[]){ 100000 * FLOAT_RAND, 100000 * FLOAT_RAND });
+ }
+
+ phxsetmap(8, 8, (int[]) {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 1, 0, 0,
+ 0, 1, 1, 0, 0, 1, 1, 0,
+ 0, 1, 1, 0, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 0, 0, 1, 1, 0,
+ 0, 1, 1, 0, 0, 1, 1, 0,
+ 0, 0, 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ });
+
+ running = 1;
+ old_ticks = SDL_GetTicksNS();
+ while(running) {
+ CollisionEvent event;
+ double delta;
+ process_events();
+
+ new_ticks = SDL_GetTicksNS();
+ delta = (new_ticks - old_ticks) / 1000000000.0;
+ old_ticks = new_ticks;
+ phxtick(delta);
+ while(phxnextcollevent(&event)) {
+ printf("Collision with %p and %p\n", event.body1, event.body2);
+ }
+
+ render();
+ }
+
+ SDL_DestroyWindow(window);
+ SDL_Quit();
+ return 0;
+}
+
+void
+process_events()
+{
+ SDL_Event event;
+
+ while(SDL_PollEvent(&event)) {
+ switch(event.type) {
+ case SDL_EVENT_QUIT:
+ running = false;
+ break;
+ }
+ }
+}
+
+void
+render(void)
+{
+ SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);
+ SDL_RenderClear(renderer);
+
+ SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
+ for(int i = 0; i < phxbodypoolsize; i++) {
+ vec2 p, s;
+ Body *b = phxbodypool + i;
+ if(!b->active)
+ continue;
+ renderrect(b->pos, b->size);
+ }
+
+ SDL_RenderPresent(renderer);
+}
diff --git a/libphysics/tick.c b/libphysics/tick.c
new file mode 100644
index 0000000..c6399ab
--- /dev/null
+++ b/libphysics/tick.c
@@ -0,0 +1,138 @@
+#include <vecmath.h>
+#include <physics.h>
+#include <stdlib.h>
+
+#include "dat.h"
+
+#define PHX_TICK_TIME (1.0 / 480.0)
+
+static void tick(void);
+static void mapcollision(Body *);
+static void bodycollision(Body *);
+
+#define TILE_SIZE 16
+
+void
+phxtick(float delta)
+{
+ static float time;
+
+ time += delta;
+ if(time >= PHX_TICK_TIME > 0) {
+ while(time >= PHX_TICK_TIME) {
+ tick();
+ time -= PHX_TICK_TIME;
+ }
+ for(int i = 0; i < phxbodypoolsize; i++) {
+ Body *b = phxbodypool + i;
+ if(!b->active)
+ continue;
+
+ b->accel[0] = 0.0;
+ b->accel[1] = 0.0;
+ }
+ }
+}
+
+void
+tick(void)
+{
+ for(int i = 0; i < phxbodypoolsize; i++) {
+ Body *b = phxbodypool + i;
+ if(!b->active)
+ continue;
+
+ vec2_add_scaled(b->vel, b->vel, b->accel, PHX_TICK_TIME);
+ }
+
+ for(int i = 0; i < phxbodypoolsize; i++) {
+ Body *b = phxbodypool + i;
+ if(!b->active)
+ continue;
+
+ mapcollision(b);
+ bodycollision(b);
+ }
+
+ for(int i = 0; i < phxbodypoolsize; i++) {
+ Body *b = phxbodypool + i;
+ if(!b->active)
+ continue;
+
+ vec2_add_scaled(b->pos, b->pos, b->vel, PHX_TICK_TIME);
+ }
+}
+
+void
+mapcollision(Body *a)
+{
+ int minx, miny, maxx, maxy;
+
+ minx = ((int)(a->pos[0] - a->size[0]) / TILE_SIZE) * TILE_SIZE;
+ maxx = ((int)(a->pos[0] + a->size[0]) / TILE_SIZE) * TILE_SIZE;
+ miny = ((int)(a->pos[1] - a->size[1]) / TILE_SIZE) * TILE_SIZE;
+ maxy = ((int)(a->pos[1] + a->size[1]) / TILE_SIZE) * TILE_SIZE;
+
+ for(int y = miny; y <= maxy + TILE_SIZE; y += TILE_SIZE)
+ for(int x = minx; x <= maxx + TILE_SIZE; x += TILE_SIZE) {
+ int tilex = x / (2 * TILE_SIZE);
+ int tiley = y / (2 * TILE_SIZE);
+
+ if(tilex < 0 || tilex >= phxmapwidth)
+ continue;
+ if(tiley < 0 || tiley >= phxmapheight)
+ continue;
+
+ if(!phxmapbuffer[tilex + tiley * phxmapwidth])
+ continue;
+
+ Body b = {
+ .pos = {
+ tilex * TILE_SIZE * 2.0,
+ tiley * TILE_SIZE * 2.0,
+ },
+ .size = { TILE_SIZE, TILE_SIZE },
+ };
+
+ if(phxaabbcheck(a, &b)) {
+ float p[2], n[2];
+ phxaabbresolv(a, &b, p, n);
+
+ vec2_sub(a->pos, a->pos, p);
+
+ if(p[0] != 0.0) {
+ a->vel[0] = 0;
+ } else {
+ a->vel[1] = 0;
+ }
+ }
+ }
+}
+
+void
+bodycollision(Body *a)
+{
+ CollisionEvent event;
+ for(Body *b = a + 1; b < (a + phxbodypoolsize); b++) {
+ if(!b->active)
+ continue;
+
+ if(phxaabbcheck(a, b)) {
+ float p[2], n[2], rvel[2], j;
+ phxaabbresolv(a, b, p, n);
+
+ vec2_sub(rvel, a->vel, b->vel);
+ j = vec2_dot(rvel, n);
+
+ vec2_sub_scaled(a->vel, a->vel, n, 0.5);
+ vec2_sub_scaled(b->vel, b->vel, n, -0.5);
+
+ vec2_sub_scaled(a->pos, a->pos, p, 0.5);
+ vec2_sub_scaled(b->pos, b->pos, p, -0.5);
+
+ event.body1 = a;
+ event.body2 = b;
+ phxenqevent(&event);
+ }
+ }
+}