examples

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

commit c980f07c3d481ee40e06b33b603da5010838b965
parent 668df50c1aeb24bafe3fc66e4b81b0458ff35ad3
Author: Henry Wilson <henry@henryandlizzy.uk>
Date:   Mon, 24 Jun 2024 23:34:57 +0100

sdl-gl: Wireframe mode and tidying coordinate types

Diffstat:
Msrc/sdl-gl.cpp | 224++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
1 file changed, 132 insertions(+), 92 deletions(-)

diff --git a/src/sdl-gl.cpp b/src/sdl-gl.cpp @@ -72,8 +72,29 @@ private: coroutine_owner<> waiting = {}; }; -int screen_width = 800; -int screen_height = 450; +template <typename T> +struct vec2 +{ + T x = 0, y = 0; + vec2 operator -() const { return {-x, -y}; }; + vec2 operator *(T rhs) const { return {x * rhs, y * rhs}; } + vec2 operator /(T rhs) const { return {x / rhs, y / rhs}; } + vec2 operator +(vec2 rhs) const { return {x + rhs.x, y + rhs.y}; } + vec2 operator -(vec2 rhs) const { return *this + -rhs; } + vec2& operator *=(T rhs) { return *this = *this * rhs; } + vec2& operator /=(T rhs) { return *this = *this / rhs; } + vec2& operator +=(vec2 rhs) { return *this = *this + rhs; } + vec2& operator -=(vec2 rhs) { return *this = *this - rhs; } + T magnitude_squared() const { return x*x + y*y; } +}; + +using point = vec2<float>; +using screen_coord = vec2<int>; + +screen_coord screen = {800, 450}; +float aspect_ratio = (float) screen.x / screen.y; + +bool wireframe; SDL_Window* window; @@ -84,10 +105,10 @@ void initSDL(void) if (SDL_Init(SDL_INIT_VIDEO) < 0) errx(1, "Couldn't initialize SDL: %s\n", SDL_GetError()); - window = SDL_CreateWindow("SDL OpenGL example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_width, screen_height, windowFlags); + window = SDL_CreateWindow("SDL OpenGL example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen.x, screen.y, windowFlags); if (!window) - errx(1, "Failed to open %d x %d window: %s\n", screen_width, screen_height, SDL_GetError()); + errx(1, "Failed to open %d x %d window: %s\n", screen.x, screen.y, SDL_GetError()); SDL_GLContext context = SDL_GL_CreateContext(window); if (not context) @@ -98,7 +119,8 @@ void initSDL(void) std::mt19937 gen; std::lognormal_distribution<float> dis(-1, .75); -std::normal_distribution<float> norm_dis; +std::normal_distribution<float> norm_dis{0.f, .5f}; + struct rgb { float r,g,b; @@ -120,55 +142,27 @@ void glColor(rgb const& c) } -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; } -}; - -int clamp(int v, int l, int h) +void glVertex(point p) { - if (v < l) - v = l; - else if (v > h) - v = h; - return v; + glVertex2f(p.x, p.y); } -int abs(int v) +point window_to_gl(screen_coord c) { - return v < 0 ? -v : v; + float screen_x = (2.f * c.x / screen.x - 1.f); + float screen_y = -2.f * c.y / screen.y + 1.f; + return {screen_x, screen_y}; } -int colliding(struct coord a, struct coord b, int r) +float scale = .5f; + +point window_to_local(screen_coord c) { - a -= b; - a.x = abs(a.x) < r; - a.y = abs(a.y) < r; - return a.x && a.y; + auto p = window_to_gl(c); + p.x *= aspect_ratio; + return p * (1/scale); } -struct point -{ - float x = 0, y = 0; - point operator -() const { return {-x, -y}; }; - point operator *(float rhs) const { return {x * rhs, y * rhs}; } - point operator /(float rhs) const { return {x / rhs, y / rhs}; } - point operator +(point rhs) const { return {x + rhs.x, y + rhs.y}; } - point operator -(point rhs) const { return *this + -rhs; } - point& operator *=(float rhs) { return *this = *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; } - float magnitude_squared() const { return x*x + y*y; } -}; struct rotor { float s, xy; @@ -190,56 +184,110 @@ struct circle } }; -float scale = .5f; -point pan = {1, 0}; +point pan = {0, 0}; point mouse_pos; struct object { - point pos; + circle pos; rgb col; float spin; float rotation; }; std::vector<std::shared_ptr<object>> objs = { - std::make_shared<object>(point{0, 0}, rgb{.7,.2,.2}, .2), - std::make_shared<object>(point{4, 0}, rgb{.2,.7,.2}, 0), - std::make_shared<object>(point{0, 5}, rgb{.2,.2,.7}, -.2), + std::make_shared<object>(circle{{-1.5f, -1}, 1.f}, rgb{.7,.2,.2}, .2), + std::make_shared<object>(circle{{0, 1}, .5f}, rgb{.2,.7,.2}, 0), + std::make_shared<object>(circle{{1.5f, -1}, 1.f}, rgb{.2,.2,.7}, -.2), }; std::vector<point> shape; +std::span<point> shape_wireframe; constexpr auto deref = std::views::transform([](auto const& p) -> auto& { return *p; }); void presentScene(void) { - float x_scale = (float)screen_height / screen_width; - glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); - circle mouse_area{mouse_pos, 1}; for (auto& o : objs | deref) { o.rotation += o.spin / 60; rotor r = rotor::angle(o.rotation); - if (mouse_area.colliding(o.pos)) + if (o.pos.colliding(mouse_pos)) glColor(o.col + rgb{.25f, .25f, .25f,}); else glColor(o.col); - glBegin(GL_TRIANGLE_FAN); - for (auto& p : shape) { - auto pp = (p * r + pan + o.pos) * scale; - glVertex2f(pp.x * x_scale, pp.y); + if (not wireframe) + { + glBegin(GL_TRIANGLE_FAN); + for (auto p : shape) + { + p *= o.pos.radius; + auto pp = (p * r + pan + o.pos.pos) * scale; + glVertex2f(pp.x / aspect_ratio, pp.y); + } + } + else + { + glBegin(GL_LINE_LOOP); + for (auto p : shape_wireframe) + { + p *= o.pos.radius; + auto pp = (p * r + pan + o.pos.pos) * scale; + glVertex2f(pp.x / aspect_ratio, pp.y); + } } glEnd(); } glFinish(); - SDL_GL_SwapWindow(window); +} + +struct panel +{ + screen_coord pos, size; + void draw() const; +}; + +void panel::draw() const +{ + auto tl = window_to_gl(pos); + auto tr = window_to_gl({pos.x + size.x, pos.y}); + auto bl = window_to_gl({pos.x, pos.y + size.y}); + auto br = window_to_gl(pos + size); + + glBegin(GL_TRIANGLE_STRIP); + glColor3d(.3, .3, .3); + glVertex(tl); + glVertex(tr); + glVertex(bl); + glVertex(br); + glEnd(); + + glBegin(GL_LINE_STRIP); + glColor3d(.15, .15, .15); + glVertex(bl); + glVertex(br); + glVertex(tr); + glEnd(); + + glBegin(GL_LINE_STRIP); + glColor3d(.5, .5, .5); + glVertex(bl); + glVertex(tl); + glVertex(tr); + glEnd(); +} + +void draw_UI() +{ + panel{50, 50, 99, 49}.draw(); + panel{50, 100, 99, 49}.draw(); } void key(SDL_KeyboardEvent& e) { + static std::uniform_real_distribution<float> size_dis{.5f, 1.5f}; if (e.repeat) return; static uint8_t ctrls; @@ -257,23 +305,19 @@ void key(SDL_KeyboardEvent& e) case SDLK_a: if (e.state) - objs.push_back(std::make_shared<object>(mouse_pos, rgb::random(), norm_dis(gen), 0)); + objs.push_back(std::make_shared<object>(circle{mouse_pos, size_dis(gen)}, rgb::random(), norm_dis(gen), 0)); break; case SDLK_d: if (e.state) - { - circle mouse_area{mouse_pos, 1}; - std::erase_if(objs, [mouse_area](auto& o) { return mouse_area.colliding(o->pos); }); - } + std::erase_if(objs, [](auto& o) { return o->pos.colliding(mouse_pos); }); break; case SDLK_r: if (e.state) { - circle mouse_area{mouse_pos, 1}; for (auto& obj : objs | deref) - if (mouse_area.colliding(obj.pos)) + if (obj.pos.colliding(mouse_pos)) { std::swap(obj.col.r, obj.col.g); std::swap(obj.col.g, obj.col.b); @@ -285,34 +329,26 @@ void key(SDL_KeyboardEvent& e) case SDLK_s: if (e.state) { - circle mouse_area{mouse_pos, 1}; for (auto& obj : objs | deref) - if (mouse_area.colliding(obj.pos)) + if (obj.pos.colliding(mouse_pos)) obj.spin *= -1; } break; + case SDLK_w: + if (e.state) + wireframe = !wireframe; + break; + 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}; -} - -point mouse_to_local(int x, int y) -{ - return mouse_to_screen(x, y) * (1/scale); -} - void mouse_motion(SDL_MouseMotionEvent& e) { - mouse_pos = mouse_to_local(e.x, e.y) - pan; + mouse_pos = window_to_local({e.x, e.y}) - pan; } awaitable_value<SDL_MouseButtonEvent const> click_event; @@ -323,7 +359,7 @@ coroutine_task mouse_task() for (;;) { SDL_MouseButtonEvent const& e = co_await click_event; - point drag_pos = pan - mouse_to_local(e.x, e.y); + point drag_pos = pan - window_to_local({e.x, e.y}); switch (e.button) { case SDL_BUTTON_RIGHT: @@ -332,7 +368,7 @@ coroutine_task mouse_task() { SDL_Event const& e2 = co_await motion_or_release_event; if (e2.type == SDL_MOUSEMOTION) - pan = drag_pos + mouse_to_local(e2.motion.x, e2.motion.y); + pan = drag_pos + window_to_local({e2.motion.x, e2.motion.y}); else if (auto& b = e2.button; b.button == SDL_BUTTON_RIGHT) break; } @@ -340,12 +376,11 @@ coroutine_task mouse_task() } case SDL_BUTTON_LEFT: { - circle mouse_area{mouse_pos, 1}; - auto it = std::ranges::find_if(objs, [drag_pos, mouse_area](auto const& o){return mouse_area.colliding(o->pos);}); + auto it = std::ranges::find_if(objs, [](auto const& o){return o->pos.colliding(mouse_pos);}); if (it == std::end(objs)) break; - drag_pos = (*it)->pos - mouse_to_local(e.x, e.y); + drag_pos = (*it)->pos.pos - window_to_local({e.x, e.y}); std::weak_ptr<object> wp = *it; for (;;) @@ -354,7 +389,7 @@ coroutine_task mouse_task() if (wp.expired()) break; else if (e2.type == SDL_MOUSEMOTION) - wp.lock()->pos = drag_pos + mouse_to_local(e2.motion.x, e2.motion.y); + wp.lock()->pos.pos = drag_pos + window_to_local({e2.motion.x, e2.motion.y}); else if (auto& b = e2.button; b.button == SDL_BUTTON_LEFT) break; } @@ -367,7 +402,7 @@ coroutine_task mouse_task() void wheel(SDL_MouseWheelEvent* e) { float factor = exp(0.1f * e->y); - pan += mouse_to_local(e->mouseX, e->mouseY) * (1/factor-1); + pan += window_to_local({e->mouseX, e->mouseY}) * (1/factor-1); scale *= factor; } @@ -376,9 +411,10 @@ 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); + screen.x = e->data1; + screen.y = e->data2; + aspect_ratio = (float) screen.x / screen.y; + glViewport(0, 0, screen.x, screen.y); return; } } @@ -426,6 +462,8 @@ void init_shape() shape.emplace_back(0,0); for (int i = 0; i <= n; ++i) shape.push_back(point{0, (.65f + i % 2) / -1.65f} * rotor::angle((float)i / n)); + shape_wireframe = shape; + shape_wireframe = shape_wireframe.subspan(1, shape.size() - 2); } int main() @@ -438,5 +476,7 @@ int main() { doInput(); presentScene(); + draw_UI(); + SDL_GL_SwapWindow(window); } }