diff --git a/CMakeLists.txt b/CMakeLists.txt index fb77a85..50ad91c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,15 +2,26 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(padlab C) set(CMAKE_C_STANDARD 99) set(TARGET padlab) + +find_package(SDL2 REQUIRED) +find_package(OpenGL) + set(SOURCES maths.h - draw.c draw.h + draw.h stick.c stick.h analogue.c) -find_package(SDL2 REQUIRED) +if (OPENGL_FOUND) + list(APPEND SOURCES draw_opengl.c) +else() + list(APPEND SOURCES draw.c) +endif() add_executable(${TARGET} ${SOURCES}) target_link_libraries(${TARGET} SDL2::SDL2 m) +if (OPENGL_FOUND) + target_link_libraries(${TARGET} OpenGL::GL) +endif() target_compile_options(${TARGET} PRIVATE -Wall -Wextra -pedantic -Wno-unused-parameter) diff --git a/analogue.c b/analogue.c index dc3ccec..370c11f 100644 --- a/analogue.c +++ b/analogue.c @@ -38,6 +38,7 @@ int main(int argc, char** argv) const int winflg = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; int winw = WINDOW_WIDTH; int winh = WINDOW_HEIGHT; + DrawWindowHints(); window = SDL_CreateWindow(CAPTION, winpos, winpos, winw, winh, winflg); FATAL(window == NULL, -1) diff --git a/draw.c b/draw.c index 2ade969..615f163 100644 --- a/draw.c +++ b/draw.c @@ -4,6 +4,8 @@ static SDL_Renderer* rend = NULL; +void DrawWindowHints(void) {} + int InitDraw(SDL_Window* window) { const int rendflags = SDL_RENDERER_PRESENTVSYNC; @@ -68,7 +70,7 @@ void DrawCircleSteps(int x, int y, int r, int steps) double stepsz = (double)TAU / steps; int lastx = r; int lasty = 0; - for (int i = 0; i <= steps; ++i) + for (int i = 1; i <= steps; ++i) { const double mag = (double)r; int ofsx = (int)round(cos(stepsz * i) * mag); diff --git a/draw.h b/draw.h index c660f89..97c5251 100644 --- a/draw.h +++ b/draw.h @@ -8,6 +8,10 @@ typedef struct SDL_Window SDL_Window; +// Call before window creation to setup backend-specific +// hints and attributes. +void DrawWindowHints(void); + // Initialise the drawing subsystem. // // Params: diff --git a/draw_opengl.c b/draw_opengl.c new file mode 100644 index 0000000..3eccf96 --- /dev/null +++ b/draw_opengl.c @@ -0,0 +1,176 @@ +#include "draw.h" +#include +#include +#include + +static SDL_GLContext* ctx = NULL; +static SDL_Window* window = NULL; +static uint32_t colour = 0x00000000; +static uint32_t clrColour = 0x00000000; +static double scaleWidth = 0.0; +static double scaleHeight = 0.0; +static bool antialias = false; + +void DrawWindowHints(void) +{ + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); // Enable MSAA + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8); // 8x MSAA + + // Legacy OpenGL profile + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); +} + +int InitDraw(SDL_Window* w) +{ + ctx = SDL_GL_CreateContext(w); + window = w; + if (ctx == NULL || window == NULL) + return -1; + + SDL_GL_SetSwapInterval(1); // Enable vsync + + // Detect if MSAA is available & active + int res; + if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &res) == 0 && res == 1) + if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &res) == 0 && res > 0) + antialias = true; + + // Disable depth test & culling, enable MSAA + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glEnable(GL_MULTISAMPLE); + + GetDrawSizeInPixels(); // Fills scaleWidth & scaleHeight + + // Setup orthographic viewport + glMatrixMode(GL_PROJECTION); + glOrtho(0.0, 1.0f, 1.0f, 0.0, 1.0, -1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + return 0; +} + +void QuitDraw(void) +{ + SDL_GL_DeleteContext(ctx); + ctx = NULL; + window = NULL; +} + + +size GetDrawSizeInPixels(void) +{ + size out; + SDL_GL_GetDrawableSize(SDL_GL_GetCurrentWindow(), &out.w, &out.h); + scaleWidth = 1.0 / (double)out.w; + scaleHeight = 1.0 / (double)out.h; + return out; +} + + +void SetDrawColour(uint32_t c) +{ + colour = c; +} + +void DrawClear(void) +{ + if (clrColour != colour) + { + const float mul = 1.0f / 255.0f; + glClearColor( + (GLclampf)((colour & 0xFF000000) >> 24) * mul, + (GLclampf)((colour & 0x00FF0000) >> 16) * mul, + (GLclampf)((colour & 0x0000FF00) >> 8) * mul, + (GLclampf)((colour & 0x000000FF)) * mul); + clrColour = colour; + } + glClear(GL_COLOR_BUFFER_BIT); +} + +static inline void GlColour(void) +{ + const float mul = 1.0f / 255.0f; + glColor4f( + (GLclampf)((colour & 0xFF000000) >> 24) * mul, + (GLclampf)((colour & 0x00FF0000) >> 16) * mul, + (GLclampf)((colour & 0x0000FF00) >> 8) * mul, + (GLclampf)((colour & 0x000000FF)) * mul); +} + +void DrawPoint(int x, int y) +{ + const double fx = ((double)x) * scaleWidth; + const double fy = ((double)y) * scaleHeight; + + glBegin(GL_POINTS); + GlColour(); + glVertex2d(fx, fy); + glEnd(); +} + +void DrawRect(int x, int y, int w, int h) +{ + const double fx = (double)x * scaleWidth; + const double fy = (double)y * scaleHeight; + const double fw = (double)w * scaleWidth; + const double fh = (double)h * scaleHeight; + + glBegin(GL_LINE_LOOP); + GlColour(); + glVertex2d(fx, fy); + glVertex2d(fx + fw, fy); + glVertex2d(fx + fw, fy + fh); + glVertex2d(fx, fy + fh); + glEnd(); +} + +void DrawLine(int x1, int y1, int x2, int y2) +{ + const double fx1 = (double)x1 * scaleWidth; + const double fy1 = (double)y1 * scaleHeight; + const double fx2 = (double)x2 * scaleWidth; + const double fy2 = (double)y2 * scaleHeight; + + glBegin(GL_LINES); + GlColour(); + glVertex2d(fx1, fy1); + glVertex2d(fx2, fy2); + glEnd(); +} + +void DrawCircle(int x, int y, int r) +{ + DrawCircleSteps(x, y, r, (int)(sqrt((double)r) * 8.0)); +} + +void DrawCircleSteps(int x, int y, int r, int steps) +{ + // Circles look better when offset negatively by half a pixel w/o MSAA + const double fx = (antialias ? (double)x : (double)x - 0.5f) * scaleWidth; + const double fy = (antialias ? (double)y : (double)y - 0.5f) * scaleHeight; + + const double stepsz = (double)TAU / (double)steps; + const double magw = (double)r * scaleWidth; + const double magh = (double)r * scaleHeight; + + glBegin(GL_LINE_LOOP); + GlColour(); + glVertex2d(fx + magw, fy); + for (int i = 1; i < steps; ++i) + { + const double theta = stepsz * (double)i; + double ofsx = cos(theta) * magw; + double ofsy = sin(theta) * magh; + glVertex2d(fx + ofsx, fy - ofsy); + } + glEnd(); +} + +void DrawPresent(void) +{ + SDL_GL_SwapWindow(window); +}