Idea from https://youtu.be/9IULfQH7E90
Created
March 9, 2023 12:44
-
-
Save DavideGalilei/967558a64dc9d22d77be9c701e6bf22e to your computer and use it in GitHub Desktop.
Physics simulation test in C + SDL2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env sh | |
set -xe | |
cc -Wall -Wextra -pedantic -pedantic-errors `sdl2-config --cflags --libs` -lm main.c -o main | |
# -Werror |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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