commit 96869bcd12b762f473a74a53a9d33e460eea5a76
parent f3c041a9e08231ac1f816ae4cebccd68564847cd
Author: Henry Wilson <henry@henryandlizzy.uk>
Date: Thu, 2 May 2024 23:27:23 +0100
tmp: sdl-gl
Diffstat:
M | Tupfile | | | 1 | + |
A | src/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);
+ }
+}