Skip to content

Instantly share code, notes, and snippets.

@DavideGalilei
Created March 9, 2023 12:44
Show Gist options
  • Save DavideGalilei/967558a64dc9d22d77be9c701e6bf22e to your computer and use it in GitHub Desktop.
Save DavideGalilei/967558a64dc9d22d77be9c701e6bf22e to your computer and use it in GitHub Desktop.
Physics simulation test in C + SDL2
#!/usr/bin/env sh
set -xe
cc -Wall -Wextra -pedantic -pedantic-errors `sdl2-config --cflags --libs` -lm main.c -o main
# -Werror
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <SDL2/SDL.h>
#define SEQ_H_IMPLEMENTATION
#include "seq.h"
typedef struct {
int width;
int height;
} Screen;
typedef struct {
SDL_Color color;
int x;
int y;
int radius;
double accel;
} Ball;
static const SDL_Color RED = {.r=255, .g=0, .b=0, .a=255};
static int get_display_sizes(Screen *screen) {
SDL_DisplayMode dm;
if (SDL_GetDesktopDisplayMode(0, &dm) != 0) {
SDL_Log("SDL_GetDesktopDisplayMode failed: %s", SDL_GetError());
return -1;
}
*screen = (Screen) {
.width = dm.w,
.height = dm.h,
};
return 0;
}
void DrawCircle(SDL_Renderer * renderer, int32_t centreX, int32_t centreY, int32_t radius, SDL_Color color) {
// https://stackoverflow.com/a/48291620
// modified a little to add colors
Uint8 r, g, b, a;
SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a);
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
const int32_t diameter = (radius * 2);
int32_t x = (radius - 1);
int32_t y = 0;
int32_t tx = 1;
int32_t ty = 1;
int32_t error = (tx - diameter);
while (x >= y) {
// Each of the following renders an octant of the circle
SDL_RenderDrawPoint(renderer, centreX + x, centreY - y);
SDL_RenderDrawPoint(renderer, centreX + x, centreY + y);
SDL_RenderDrawPoint(renderer, centreX - x, centreY - y);
SDL_RenderDrawPoint(renderer, centreX - x, centreY + y);
SDL_RenderDrawPoint(renderer, centreX + y, centreY - x);
SDL_RenderDrawPoint(renderer, centreX + y, centreY + x);
SDL_RenderDrawPoint(renderer, centreX - y, centreY - x);
SDL_RenderDrawPoint(renderer, centreX - y, centreY + x);
if (error <= 0) {
++y;
error += ty;
ty += 2;
}
if (error > 0) {
--x;
tx += 2;
error += (tx - diameter);
}
}
// restore initial color
SDL_SetRenderDrawColor(renderer, r, g, b, a);
}
void draw_rectangle(SDL_Renderer *renderer, SDL_Rect rect, SDL_Color color) {
Uint8 r, g, b, a;
SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a);
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
SDL_RenderFillRect(renderer, &rect);
SDL_SetRenderDrawColor(renderer, r, g, b, a);
}
double distance(Ball a, Ball b) {
double dx = a.x - b.x;
double dy = a.y - b.y;
return sqrt((dx*dx) + (dy*dy));
}
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
printf("Error initializing SDL: %s\n", SDL_GetError());
}
Screen size;
assert(get_display_sizes(&size) == 0);
const int WINDOW_WIDTH = size.width / 2;
const int WINDOW_HEIGHT = size.height / 2;
const double GRAVITY = 0.3;
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
SDL_Window *window = SDL_CreateWindow(
"Physics Simulation",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
WINDOW_WIDTH,
WINDOW_HEIGHT,
0
);
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
bool running = true;
SDL_Event event;
Seq(Ball) balls = { 0 };
while (running) {
SDL_RenderClear(renderer);
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
// handling of close button
running = false;
break;
case SDL_MOUSEBUTTONDOWN: {
if (event.button.button == SDL_BUTTON_LEFT) {
for (size_t i = 0; i < 5; ++i) {
Ball ball = {
.color = RED,
.x = event.button.x,
.y = event.button.y,
.radius = 20,
};
printf("Adding ballz. x=%d y=%d\n", event.button.x, event.button.y);
Seq_add(balls, ball);
}
}
break;
}
}
}
int barrier_w = WINDOW_WIDTH / 20;
/*
##
##
##
*/
draw_rectangle(renderer, (SDL_Rect){
.x = 0,
.y = 0,
.w = barrier_w,
.h = WINDOW_HEIGHT,
}, RED);
/*
##
##
##########
*/
draw_rectangle(renderer, (SDL_Rect){
.x = 0,
.y = WINDOW_HEIGHT - barrier_w,
.w = WINDOW_WIDTH,
.h = barrier_w,
}, RED);
/*
## ##
## ##
##########
*/
draw_rectangle(renderer, (SDL_Rect){
.x = WINDOW_WIDTH - barrier_w,
.y = 0,
.w = barrier_w,
.h = WINDOW_HEIGHT,
}, RED);
for (size_t i = 0; i < balls.len;) {
if (
balls.data[i].x + balls.data[i].radius < 0
|| balls.data[i].x - balls.data[i].radius > WINDOW_WIDTH
|| balls.data[i].y + balls.data[i].radius < 0
|| balls.data[i].y - balls.data[i].radius > WINDOW_HEIGHT
) {
// destroy ball
printf("Ball destroyed\n");
Seq_pop(balls, i);
continue;
}
balls.data[i].y += GRAVITY * (1.0 + balls.data[i].accel);
balls.data[i].accel += GRAVITY * 2;
if (balls.data[i].y + balls.data[i].radius > WINDOW_HEIGHT - barrier_w) {
balls.data[i].accel = 0.0;
balls.data[i].y = WINDOW_HEIGHT - barrier_w - balls.data[i].radius;
}
DrawCircle(renderer,
balls.data[i].x,
balls.data[i].y,
balls.data[i].radius,
RED
);
++i;
}
for (size_t i = 0; i < balls.len; ++i) {
for (size_t j = 0; j < balls.len; ++j) {
if (i != j) {
if (distance(balls.data[i], balls.data[j]) < balls.data[i].radius + balls.data[j].radius) {
// solve collision
// printf("Collision!\n");
balls.data[i].x += (balls.data[i].x > balls.data[j].x) ? +4 : -4;
balls.data[i].y += (balls.data[i].y > balls.data[j].y) ? +4 : -4;
// false && ...
if (balls.data[i].y < balls.data[j].y) {
balls.data[i].accel = fmin(0.0, balls.data[i].accel - GRAVITY);
}
}
}
}
}
SDL_RenderPresent(renderer);
SDL_Delay(1000 / 60);
}
Seq_destroy(balls);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return (EXIT_SUCCESS);
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#define Seq(T) struct {T *data; T temp; size_t size; size_t len;}
#ifndef ARENA_SIZE
#define ARENA_SIZE 1024
#endif
#define Seq_add(seq, element) _Seq_add((void**)(&(seq).data), &(seq).size, &(seq).len, sizeof((seq).data[0]), ((seq).temp = (element), &(seq).temp))
#define Seq_pop(seq, index) _Seq_pop((void**)(&(seq).data), &(seq).size, &(seq).len, sizeof((seq).data[0]), (index))
#define Seq_destroy(seq) _Seq_destroy((seq).data)
int _Seq_add(void **data, size_t *data_size, size_t *len, size_t elem_size, void *element);
int _Seq_pop(void **data, size_t *data_size, size_t *len, size_t elem_size, size_t index);
void _Seq_destroy(void *data);
#ifdef SEQ_H_IMPLEMENTATION
int _Seq_add(void **data, size_t *data_size, size_t *len, size_t elem_size, void *element) {
// if len(elements + 1) doesn't fit within seq.size, enlarge the arena pool
// return -1 if an error occurs
if (((*len) + 1) * elem_size > (*data_size)) {
void *realloc_mem = realloc(*data, *data_size + ((ARENA_SIZE) * elem_size));
if (realloc_mem == NULL)
return -1;
*data = realloc_mem;
*data_size += (ARENA_SIZE) * elem_size;
}
// copy the element to its respective cell calculated by len(seq) * sizeof(elem)
memcpy((void*)((intptr_t)(*data) + (intptr_t)((*len) * elem_size)), element, elem_size);
(*len)++;
return 0;
}
int _Seq_pop(void **data, size_t *data_size, size_t *len, size_t elem_size, size_t index) {
// if index is out of bounds, return error
if (index >= (*len)) return -1;
// if the index doesn't belong to the last element, shift the elements afterwards
if (index != (*len - 1))
memmove((void*)((intptr_t)(*data) + (intptr_t)(index * elem_size)),
(void*)((intptr_t)(*data) + (intptr_t)(index * elem_size) + (intptr_t)elem_size),
((*len) - index) * elem_size);
// if len(elements + ARENA_SIZE) fits within `seq.size - ARENA_SIZE`, shrink the arena pool
// return -1 if an error occurs
if (((*len + (ARENA_SIZE)) * elem_size) < (*data_size)) {
void *realloc_mem = realloc(*data, *data_size - ((ARENA_SIZE) * elem_size));
if (realloc_mem == NULL)
return -1;
*data = realloc_mem;
*data_size -= (ARENA_SIZE) * elem_size;
}
(*len)--;
return 0;
}
void _Seq_destroy(void *data) {
// free the sequence data
free(data);
}
#ifdef _SEQ_DEBUG
// c && gcc -Wall -Wextra -Werror -pedantic -o test -x c -DSEQ_H_IMPLEMENTATION -DARENA_SIZE=3 -D_SEQ_DEBUG seq.h && ./test
int main(void) {
Seq(int) numbers = {0};
for (size_t i = 0; i < 20; i++) {
Seq_add(numbers, i);
if (!(i % 2)) printf("Added %ld'th element (value: %d). (Arena size: %ld)\n",
i + 1,
numbers.data[numbers.len - 1],
numbers.size);
if (i % 2) {
Seq_pop(numbers, numbers.len - 1);
printf("Pop'd last element (Arena size: %ld)\n",
numbers.size);
}
}
for (size_t i = 0; i < numbers.len; i++) {
printf("numbers[%ld] = %d\n",
i,
numbers.data[i]);
}
for (; numbers.len > 0 ;) {
if (Seq_pop(numbers, numbers.len - 1) == -1) {
printf("An error occurred while popping last element\n");
return (EXIT_FAILURE);
}
else {
printf("Pop'd last element (len now: %ld | Arena size: %ld)\n", numbers.len, numbers.size);
}
}
}
#endif // _SEQ_DEBUG
#endif // SEQ_H_IMPLEMENTATION
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment