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:
M | src/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);
}
}