examples

Toy examples in single C files.
git clone git://henryandlizzy.uk/examples
Log | Files | Refs

commit 96869bcd12b762f473a74a53a9d33e460eea5a76
parent f3c041a9e08231ac1f816ae4cebccd68564847cd
Author: Henry Wilson <henry@henryandlizzy.uk>
Date:   Thu,  2 May 2024 23:27:23 +0100

tmp: sdl-gl

Diffstat:
MTupfile | 1+
Asrc/sdl-gl.cpp | 450+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 451 insertions(+), 0 deletions(-)

diff --git a/Tupfile b/Tupfile @@ -11,6 +11,7 @@ LDLIBS_gl-lighting = -lglfw -lGL -lm LDLIBS_gl-asteroids = -lglfw -lGL -lm -lasound LDLIBS_io_uring = -luring LDLIBS_sdl = -lSDL2 +LDLIBS_sdl-gl = -lSDL2 -lGL LDLIBS_pulse-async-client = -lpulse LDLIBS_pulse-simple-client = -lpulse-simple -lm LDLIBS_sqlite-saveload = -lsqlite3 diff --git a/src/sdl-gl.cpp b/src/sdl-gl.cpp @@ -0,0 +1,450 @@ +#include <GL/gl.h> +#include <SDL2/SDL.h> +#include <SDL2/SDL_events.h> +#include <SDL2/SDL_keycode.h> +#include <SDL2/SDL_mouse.h> +#include <SDL2/SDL_video.h> +#include <err.h> +#include <numbers> + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) +#define ARRAY_SSIZE(a) ((ssize_t)ARRAY_SIZE(a)) + +extern "C" char const* __asan_default_options() { return "detect_leaks=0"; } + +int screen_width = 800; +int screen_height = 450; + +SDL_Window* window; + +void initSDL(void) +{ + int windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) + errx(1, "Couldn't initialize SDL: %s\n", SDL_GetError()); + + window = SDL_CreateWindow("Pong", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_width, screen_height, windowFlags); + + if (!window) + errx(1, "Failed to open %d x %d window: %s\n", screen_width, screen_height, SDL_GetError()); + + SDL_GLContext context = SDL_GL_CreateContext(window); + if (not context) + errx(1, "Failed to create OpenGL context: %s", SDL_GetError()); + if (SDL_GL_MakeCurrent(window, context)) + errx(1, "Failed to make OpenGL context current: %s", SDL_GetError()); +} + +static struct rgb +{ + unsigned char r,g,b; +} player_colours[] = { + {255,0,0}, + {0,255,0}, + {32,32,255}, + {192,192,0}, + {192,0,192}, + {0,192,255}, +}; + +struct coord +{ + int x, y; + coord operator -() const { return {-x, -y}; } + coord operator +(coord rhs) const { return {x+rhs.x, y+rhs.y}; } + coord operator -(coord rhs) const { return {x-rhs.x, y-rhs.y}; } + coord operator *(int rhs) const { return {x*rhs, y*rhs}; } + coord operator /(int rhs) const { return {x/rhs, y/rhs}; } + coord& operator +=(coord rhs) { return *this = *this + rhs; } + coord& operator -=(coord rhs) { return *this = *this - rhs; } + explicit operator bool() const { return x or y; } +}; + +static struct player +{ + SDL_GameController* controller; + struct coord pos, vel; + unsigned cooldown; + int shooting; + unsigned health; +} players[ARRAY_SIZE(player_colours)]; + +struct bullet +{ + struct player* player; + struct coord pos, vel, control; + unsigned ttl; +}; + +struct bullet bullets[128]; + +int clamp(int v, int l, int h) +{ + if (v < l) + v = l; + else if (v > h) + v = h; + return v; +} + +void shoot(struct player* p) +{ + p->cooldown = 30; + for (unsigned i = 0; i < ARRAY_SIZE(bullets); ++i) + { + struct bullet* b = bullets + i; + if (b->player) + continue; + b->player = p; + b->ttl = 4*60; + b->pos = p->pos; + b->vel = p->vel*2; + return; + } +} + +int abs(int v) +{ + return v < 0 ? -v : v; +} + +int colliding(struct coord a, struct coord b, int r) +{ + a -= b; + a.x = abs(a.x) < r; + a.y = abs(a.y) < r; + return a.x && a.y; +} + +void simulate(void) +{ + for (unsigned i = 0; i < ARRAY_SIZE(bullets); ++i) + { + struct bullet* b = bullets + i; + if (!b->player) + continue; + struct player* p = NULL; + for (unsigned i = 0; i < ARRAY_SIZE(player_colours); ++i) + { + if (players + i == b->player) + continue; + if (!players[i].controller) + continue; + if (!colliding(players[i].pos, b->pos, (2 + 5 + players[i].health) << 13)) + continue; + p = players + i; + break; + } + if (!p) + continue; + p->health -= !!p->health; + p->pos += b->vel * 10; + b->player = NULL; + } + + for (unsigned i = 0; i < ARRAY_SIZE(player_colours); ++i) + { + struct player* const p = players + i; + if (!p->controller) + continue; + p->pos += p->vel; + p->pos.x = clamp(p->pos.x, -(screen_width << 12), screen_width << 12); + p->pos.y = clamp(p->pos.y, -(screen_height << 12), screen_height << 12); + if (!p->cooldown) + { + if (p->shooting) + shoot(p); + } + else + --p->cooldown; + } + for (unsigned i = 0; i < ARRAY_SIZE(bullets); ++i) + { + struct bullet* b = bullets + i; + if (!b->player) + continue; + b->pos += b->vel; + if (b->ttl--) + continue; + b->player = NULL; + } +} + +void prepareScene(void) +{ + for (unsigned i = 0; i < ARRAY_SIZE(bullets); ++i) + { + struct bullet const* b = bullets + i; + if (!b->player) + continue; + // struct rgb c = player_colours[b->player - players]; + // coord pos{screen_width / 2 + (b->pos.x >> 13), screen_height / 2 + (b->pos.y >> 13)}; + // SDL_Rect rect{pos.x - 3, pos.y - 3, 6, 6}; + // SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, 255); + // SDL_RenderDrawRect(renderer, &rect); + // if (auto vel = b->vel / 512) + // SDL_RenderDrawLine(renderer, pos.x, pos.y, pos.x + vel.x, pos.y + vel.y); + } + + for (unsigned i = 0; i < ARRAY_SIZE(player_colours); ++i) + { + struct player const* const p = players + i; + // struct rgb const c = player_colours[i]; + if (!p->controller) + continue; + // SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, 255); + // int size = 5 + p->health; + // coord pos{screen_width / 2 + (p->pos.x >> 13), screen_height / 2 + (p->pos.y >> 13)}; + // SDL_Rect rect{pos.x - size, pos.y - size, 2*size, 2*size}; + // SDL_RenderDrawRect(renderer, &rect); + // ifu(auto vel = p->vel / 512) + // SDL_RenderDrawLine(renderer, pos.x, pos.y, pos.x + vel.x, pos.y + vel.y); + } +} + +struct point +{ + float x = 0, y = 0; + point operator -() { return {-x, -y}; }; + point operator *(float rhs) { return {x * rhs, y * rhs}; } + point operator +(point rhs) { return {x + rhs.x, y + rhs.y}; } + point operator -(point rhs) { return *this + -rhs; } + point& operator *=(float rhs) { return *this = *this * rhs; } + point& operator +=(point rhs) { return *this = *this + rhs; } + point& operator -=(point rhs) { return *this = *this - rhs; } +}; +struct rotor +{ + float s, xy; + static rotor angle(float turns) { float radians = turns * 2 * std::numbers::pi; return {cos(radians), sin(radians)}; } +}; +point operator *(point lhs, rotor rhs) +{ + return {lhs.x * rhs.s - lhs.y * rhs.xy, lhs.x * rhs.xy + lhs.y * rhs.s}; +} + +float scale = .5f; +point pan = {1, 0}; + +void presentScene(void) +{ + static double turns = 0; + turns += 1./60/4; + rotor r = rotor::angle(turns); + static point const square[4] = {{-1, -1}, {-1, 1}, {1, -1}, {1, 1}}; + static point const objs[] = {{0, 0}, {4, 0}, {0, 5}}; + float x_scale = (float)screen_height / screen_width; + + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + for (auto op : objs) { + glBegin(GL_TRIANGLE_STRIP); + for (auto& p : square) { + auto pp = (p * r + pan + op) * scale; + glVertex2f(pp.x * x_scale, pp.y); + } + glEnd(); + } + glFinish(); + SDL_GL_SwapWindow(window); +} + +void key(SDL_KeyboardEvent* e) +{ + switch (e->keysym.sym) + { + case SDLK_ESCAPE: + exit(0); + } +} + +point mouse_to_screen(int x, int y) +{ + float screen_x = (2.f * x / screen_width - 1.f) * screen_width / screen_height; + float screen_y = -2.f * y / screen_height + 1.f; + return {screen_x, screen_y}; +} + +bool drag = false; +point drag_pos; + +void mouse_motion(SDL_MouseMotionEvent& e) +{ + if (!drag) + return; + auto world = mouse_to_screen(e.x, e.y) * (1/scale); + pan = drag_pos + world; +} + +void mouse_button(SDL_MouseButtonEvent& e) +{ + if (e.button != SDL_BUTTON_LEFT) + return; + drag = e.state == SDL_PRESSED; + auto world = mouse_to_screen(e.x, e.y) * (1/scale); + if (drag) { + drag_pos = pan - world; + } +} + +void wheel(SDL_MouseWheelEvent* e) +{ + float factor = exp(0.1f * e->y); + auto world = mouse_to_screen(e->mouseX, e->mouseY) * (1/scale); + pan += world * (1/factor-1); + scale *= factor; +} + +void con_add(SDL_ControllerDeviceEvent* e) +{ + char const* name = SDL_GameControllerNameForIndex(e->which); + unsigned i = 0; + for (i = 0; i < ARRAY_SIZE(players); ++i) + if (!players[i].controller) + goto allocated; + printf("Controller %d '%s' has no free slot\n", e->which, name); + return; +allocated: + players[i].controller = SDL_GameControllerOpen(i); + SDL_GameControllerSetPlayerIndex(players[i].controller, i); + players[i].vel = (struct coord){0, 0}; + players[i].pos = (struct coord){(((int)i-3)*100) << 13, 0}; + players[i].health = 10; + printf("Controller %d %p '%s' added as player %d\n", e->which, players[i].controller, name, i); +} + +void con_del(SDL_ControllerDeviceEvent* e) +{ + SDL_GameController* c = SDL_GameControllerFromInstanceID(e->which); + if (!c) + { + printf("Controller %d disconnected\n", e->which); + return; + } + + for (unsigned i = 0; i < ARRAY_SIZE(players); ++i) + if (players[i].controller == c) + { + players[i].controller = NULL; + printf("Controller %d %p disconnected, player %d removed\n", e->which, c, i); + break; + } + + SDL_GameControllerClose(c); +} + +void button(SDL_ControllerButtonEvent* e) +{ + SDL_GameController* c = SDL_GameControllerFromInstanceID(e->which); + int pid = SDL_GameControllerGetPlayerIndex(c); + if (pid < 0 || pid >= ARRAY_SSIZE(players)) + return; + + switch (e->button) + { + case SDL_CONTROLLER_BUTTON_A: + players[pid].shooting = e->state; + return; + } + + if (e->state) + printf("D[%d] P[%d] B[%u] %u\n", e->which, pid, e->button, e->state); + +} + +void axis(SDL_ControllerAxisEvent* e) +{ + SDL_GameController* c = SDL_GameControllerFromInstanceID(e->which); + int pid = SDL_GameControllerGetPlayerIndex(c); + if (pid < 0 || pid >= ARRAY_SSIZE(players)) + return; + struct player* p = players + pid; + if (!p->controller) + return; + switch (e->axis) + { + case SDL_CONTROLLER_AXIS_LEFTX: + p->vel.x = e->value >> 1; + return; + case SDL_CONTROLLER_AXIS_LEFTY: + p->vel.y = e->value >> 1; + return; + default: + printf("D[%d] P[%d] A[%u] %d\n", e->which, pid, e->axis, e->value); + } +} + +void window_event(SDL_WindowEvent const* e) +{ + switch (e->event) + { + case SDL_WINDOWEVENT_SIZE_CHANGED: + screen_width = e->data1; + screen_height = e->data2; + glViewport(0, 0, screen_width, screen_height); + return; + } +} + +void doInput() +{ + SDL_Event event; + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_QUIT: + exit(0); + case SDL_KEYDOWN: + case SDL_KEYUP: + key(&event.key); + break; + case SDL_MOUSEMOTION: + mouse_motion(event.motion); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + mouse_button(event.button); + break; + case SDL_MOUSEWHEEL: + wheel(&event.wheel); + break; + case SDL_CONTROLLERDEVICEADDED: + con_add(&event.cdevice); + break; + case SDL_CONTROLLERDEVICEREMOVED: + con_del(&event.cdevice); + break; + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + button(&event.cbutton); + break; + + case SDL_CONTROLLERAXISMOTION: + axis(&event.caxis); + break; + + case SDL_WINDOWEVENT: + window_event(&event.window); + break; + + default: + break; + } + } +} + +int main() +{ + initSDL(); + atexit(SDL_Quit); + for (;;) + { + doInput(); + simulate(); + prepareScene(); + presentScene(); + //SDL_Delay(16); + } +}