mirror of
https://github.com/GayPizzaSpecifications/padlab.git
synced 2025-08-03 21:21:33 +00:00
Implement functional OpenGL 3.3 renderer
w/ crude batching
This commit is contained in:
29
cmake/BinHelper.cmake
Normal file
29
cmake/BinHelper.cmake
Normal file
@ -0,0 +1,29 @@
|
||||
include(CMakeParseArguments) # 3.4 and lower compatibility
|
||||
find_package(Python REQUIRED COMPONENTS Interpreter)
|
||||
|
||||
function (bin2h_compile)
|
||||
set(oneValueArgs OUTPUT)
|
||||
set(multiValueArgs BIN TXT)
|
||||
cmake_parse_arguments(ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
find_file(BIN2H_EXECUTABLE bin2h.py PATHS ${CMAKE_SOURCE_DIR}/tools)
|
||||
set(DEPENDS)
|
||||
set(COMMAND ${BIN2H_EXECUTABLE})
|
||||
|
||||
foreach (BIN ${ARGS_BIN})
|
||||
set(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${BIN})
|
||||
list(APPEND DEPENDS ${SOURCE})
|
||||
list(APPEND COMMAND "-b" "${SOURCE}")
|
||||
endforeach()
|
||||
|
||||
foreach (TXT ${ARGS_TXT})
|
||||
set(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${TXT})
|
||||
list(APPEND DEPENDS ${SOURCE})
|
||||
list(APPEND COMMAND "-t" "${SOURCE}")
|
||||
endforeach()
|
||||
|
||||
list(APPEND COMMAND ${ARGS_OUTPUT})
|
||||
add_custom_command(COMMAND Python::Interpreter ARGS ${COMMAND}
|
||||
DEPENDS Python::Interpreter ${BIN2H_EXECUTABLE} ${DEPENDS}
|
||||
OUTPUT ${ARGS_OUTPUT})
|
||||
endfunction()
|
@ -12,9 +12,14 @@ set(SOURCES
|
||||
if (USE_OPENGL)
|
||||
include(GL3WHelper)
|
||||
add_gl3w(gl3w)
|
||||
include(BinHelper)
|
||||
bin2h_compile(OUTPUT glslShaders.h TXT vert.glsl frag.glsl)
|
||||
list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/glslShaders.h)
|
||||
endif()
|
||||
|
||||
add_executable(${TARGET} ${SOURCES})
|
||||
target_include_directories(${TARGET} PRIVATE
|
||||
$<$<BOOL:${USE_OPENGL}>:${CMAKE_CURRENT_BINARY_DIR}>)
|
||||
target_link_libraries(${TARGET}
|
||||
$<$<PLATFORM_ID:Windows>:SDL2::SDL2main>
|
||||
SDL2::SDL2
|
||||
|
@ -1,20 +1,47 @@
|
||||
#include "draw.h"
|
||||
#include "glslShaders.h"
|
||||
#include "maths.h"
|
||||
#include <GL/gl3w.h>
|
||||
#include <SDL_video.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct { float x, y; } vertex;
|
||||
|
||||
enum { ATTRIB_VERTPOS, NUM_ATTRIBS };
|
||||
static const char* const attribNames[] =
|
||||
{
|
||||
[ATTRIB_VERTPOS] = "inPos",
|
||||
// [ATTRIB_COLOUR] = "inColour"
|
||||
};
|
||||
|
||||
|
||||
#define OPENGL_VERSION_MAJOR 3
|
||||
#define OPENGL_VERSION_MINOR 3
|
||||
|
||||
static SDL_GLContext* ctx = NULL;
|
||||
static SDL_Window* window = NULL;
|
||||
static uint32_t colour = 0x00000000;
|
||||
static uint32_t clrColour = 0x00000000;
|
||||
static uint32_t
|
||||
colour = 0x00000000,
|
||||
drawColour = 0x00000000,
|
||||
clrColour = 0x00000000;
|
||||
static bool antialias = false;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#define DRAWLIST_MAX_SIZE 480
|
||||
static vertex drawListVerts[DRAWLIST_MAX_SIZE];
|
||||
static uint16_t drawListIndices[DRAWLIST_MAX_SIZE];
|
||||
static uint16_t drawListCount = 0, drawListVertNum = 0;
|
||||
static GLuint vao = 0, drawListVbo = 0, drawListIbo = 0;
|
||||
|
||||
static GLuint program = 0;
|
||||
static GLint uView, uColour;
|
||||
|
||||
|
||||
#if DRAWLIST_MAX_SIZE < 2 || DRAWLIST_MAX_SIZE >= UINT16_MAX
|
||||
#error DRAWLIST_MAX_SIZE must be larger than 1 and smaller than 65535
|
||||
#endif
|
||||
|
||||
#if !defined NDEBUG && !defined __APPLE__
|
||||
static void GlErrorCb(
|
||||
GLenum source,
|
||||
GLenum type,
|
||||
@ -27,18 +54,81 @@ static void GlErrorCb(
|
||||
if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
|
||||
printf("%s\n", message);
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
void DrawWindowHints(void)
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); // Enable MSAA
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8); // 8x MSAA
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 16); // 16x MSAA
|
||||
|
||||
// Legacy OpenGL profile
|
||||
// Modern OpenGL profile
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, OPENGL_VERSION_MAJOR);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, OPENGL_VERSION_MINOR);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
}
|
||||
|
||||
static inline GLuint CompilerShader(const char* src, GLenum type)
|
||||
{
|
||||
GLuint shader = glCreateShader(type);
|
||||
|
||||
glShaderSource(shader, 1, &src, NULL);
|
||||
glCompileShader(shader);
|
||||
|
||||
int res, logLen;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &res);
|
||||
if (res != GL_TRUE)
|
||||
{
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLen);
|
||||
if (logLen > 0)
|
||||
{
|
||||
char* log = malloc(logLen);
|
||||
glGetShaderInfoLog(shader, logLen, NULL, log);
|
||||
fprintf(stderr, "Shader type %#06X compilation failed:\n%s\n", type, log);
|
||||
free(log);
|
||||
}
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
static inline GLuint LinkProgram(
|
||||
GLuint vertShader, GLuint fragShader,
|
||||
const char* const attrNames[], GLuint attrCount)
|
||||
{
|
||||
GLuint progId = glCreateProgram();
|
||||
|
||||
// Bind attributes
|
||||
for (GLuint i = 0; i < attrCount; ++i)
|
||||
glBindAttribLocation(progId, i, (const GLchar*)attrNames[i]);
|
||||
|
||||
// Attach shaders & link program
|
||||
glAttachShader(progId, vertShader);
|
||||
glAttachShader(progId, fragShader);
|
||||
glLinkProgram(progId);
|
||||
|
||||
int res, logLen;
|
||||
glGetProgramiv(progId, GL_LINK_STATUS, &res);
|
||||
if (res != GL_TRUE)
|
||||
{
|
||||
glGetProgramiv(progId, GL_INFO_LOG_LENGTH, &logLen);
|
||||
if (logLen > 0)
|
||||
{
|
||||
char* log = malloc(logLen);
|
||||
glGetProgramInfoLog(progId, logLen, NULL, log);
|
||||
fprintf(stderr, "Program link failed:\n%s\n", log);
|
||||
free(log);
|
||||
}
|
||||
glDeleteProgram(progId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return progId;
|
||||
}
|
||||
|
||||
int InitDraw(SDL_Window* _window)
|
||||
@ -51,6 +141,14 @@ int InitDraw(SDL_Window* _window)
|
||||
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;
|
||||
|
||||
// Load Core profile extensions
|
||||
if (gl3wInit() != GL3W_OK)
|
||||
{
|
||||
@ -65,23 +163,98 @@ int InitDraw(SDL_Window* _window)
|
||||
|
||||
// Set debug callback
|
||||
#if !defined NDEBUG && !defined __APPLE__
|
||||
glDebugMessageCallback(GlErrorCb, nullptr);
|
||||
glDebugMessageCallback(GlErrorCb, NULL);
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
#endif
|
||||
|
||||
SDL_GL_SetSwapInterval(1); // Enable vsync
|
||||
// Ensure culling & depth testing are off
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
//glEnable(GL_BLEND); // These will be relevant if proper anti aliased line drawing is implemented
|
||||
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
//glBlendEquation(GL_FUNC_ADD);
|
||||
|
||||
// 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;
|
||||
// Compile shaders
|
||||
GLuint vert = CompilerShader(vert_glsl, GL_VERTEX_SHADER);
|
||||
if (!vert)
|
||||
return -1;
|
||||
GLuint frag = CompilerShader(frag_glsl, GL_FRAGMENT_SHADER);
|
||||
if (!frag)
|
||||
{
|
||||
glDeleteShader(vert);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Link program
|
||||
program = LinkProgram(vert, frag, attribNames, NUM_ATTRIBS);
|
||||
glDeleteShader(frag);
|
||||
glDeleteShader(vert);
|
||||
if (!program)
|
||||
return -1;
|
||||
|
||||
// Get uniforms
|
||||
uView = glGetUniformLocation(program, "uView");
|
||||
uColour = glGetUniformLocation(program, "uColour");
|
||||
|
||||
glUseProgram(program); // Use program
|
||||
|
||||
// Create & bind vertex attribute array
|
||||
glGenVertexArrays(1, &vao);
|
||||
glBindVertexArray(vao);
|
||||
|
||||
// Setup draw list
|
||||
glGenBuffers(1, &drawListVbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, drawListVbo);
|
||||
glGenBuffers(1, &drawListIbo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawListIbo);
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, DRAWLIST_MAX_SIZE * sizeof(vertex), NULL, GL_DYNAMIC_DRAW);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, DRAWLIST_MAX_SIZE * sizeof(uint16_t), NULL, GL_DYNAMIC_DRAW);
|
||||
|
||||
// Setup draw list vertex attributes
|
||||
glEnableVertexAttribArray(ATTRIB_VERTPOS);
|
||||
glVertexAttribPointer(ATTRIB_VERTPOS, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)0);
|
||||
|
||||
// Reset viewport & clear
|
||||
SetDrawViewport(GetDrawSizeInPixels());
|
||||
glUniform4f(uColour, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void QuitDraw(void)
|
||||
{
|
||||
if (drawListVbo)
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glDeleteBuffers(1, &drawListVbo);
|
||||
drawListVbo = 0;
|
||||
}
|
||||
|
||||
if (drawListIbo)
|
||||
{
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
glDeleteBuffers(1, &drawListIbo);
|
||||
drawListIbo = 0;
|
||||
}
|
||||
|
||||
if (vao)
|
||||
{
|
||||
glDisableVertexAttribArray(ATTRIB_VERTPOS);
|
||||
glBindVertexArray(0);
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
vao = 0;
|
||||
}
|
||||
|
||||
if (program)
|
||||
{
|
||||
glUseProgram(0);
|
||||
glDeleteProgram(program);
|
||||
program = 0;
|
||||
}
|
||||
|
||||
SDL_GL_MakeCurrent(window, NULL);
|
||||
SDL_GL_DeleteContext(ctx);
|
||||
ctx = NULL;
|
||||
@ -99,6 +272,49 @@ size GetDrawSizeInPixels(void)
|
||||
void SetDrawViewport(size size)
|
||||
{
|
||||
glViewport(0, 0, size.w, size.h);
|
||||
vertex s = (vertex){2.0f / (float)size.w, 2.0f / (float)size.h};
|
||||
float mat[16] = {
|
||||
s.x, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, -s.y, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 0.0f,
|
||||
-1.0f, 1.0f, 0.0f, 1.0f};
|
||||
glUniformMatrix4fv(uView, 1, GL_FALSE, mat);
|
||||
}
|
||||
|
||||
|
||||
static int drawCount = 0;
|
||||
static void FlushDrawBuffers()
|
||||
{
|
||||
if (!drawListCount)
|
||||
return;
|
||||
|
||||
GLsizeiptr vboSize = drawListVertNum * (GLsizeiptr)sizeof(vertex);
|
||||
GLsizeiptr iboSize = drawListCount * (GLsizeiptr)sizeof(uint16_t);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, (GLintptr)0, vboSize, drawListVerts);
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, (GLintptr)0, iboSize, drawListIndices);
|
||||
|
||||
glDrawElements(GL_LINES, drawListCount, GL_UNSIGNED_SHORT, (GLvoid*)0);
|
||||
|
||||
drawListVertNum = 0;
|
||||
drawListCount = 0;
|
||||
++drawCount;
|
||||
}
|
||||
|
||||
static void UnpackColour(GLfloat* out)
|
||||
{
|
||||
const float mul = 1.0f / 255.0f;
|
||||
out[0] = (GLfloat)((colour & 0xFF000000) >> 24) * mul;
|
||||
out[1] = (GLfloat)((colour & 0x00FF0000) >> 16) * mul;
|
||||
out[2] = (GLfloat)((colour & 0x0000FF00) >> 8) * mul;
|
||||
out[3] = (GLfloat)((colour & 0x000000FF)) * mul;
|
||||
}
|
||||
|
||||
static void UpdateDrawColour()
|
||||
{
|
||||
FlushDrawBuffers(); // Flush anything that might still be in the buffer
|
||||
GLfloat draw[4]; UnpackColour(draw); // Convert packed RGBA32 to float
|
||||
glUniform4fv(uColour, 1, draw); // Pass new colour to shader
|
||||
drawColour = colour; // Mark state as up to date
|
||||
}
|
||||
|
||||
|
||||
@ -111,12 +327,8 @@ 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);
|
||||
GLfloat clear[4]; UnpackColour(clear);
|
||||
glClearColor(clear[0], clear[1], clear[2], clear[3]);
|
||||
clrColour = colour;
|
||||
}
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
@ -124,29 +336,162 @@ void DrawClear(void)
|
||||
|
||||
void DrawPoint(int x, int y)
|
||||
{
|
||||
|
||||
DrawCircleSteps(x, y, 1, 4);
|
||||
}
|
||||
|
||||
void DrawRect(int x, int y, int w, int h)
|
||||
{
|
||||
#if 8 <= DRAWLIST_MAX_SIZE
|
||||
if (drawColour != colour)
|
||||
UpdateDrawColour();
|
||||
else if (drawListCount > DRAWLIST_MAX_SIZE - 8)
|
||||
FlushDrawBuffers();
|
||||
|
||||
uint16_t base = drawListVertNum;
|
||||
drawListVerts[drawListVertNum++] = (vertex){(float)x, (float)y};
|
||||
drawListVerts[drawListVertNum++] = (vertex){(float)x + (float)w, (float)y};
|
||||
drawListVerts[drawListVertNum++] = (vertex){(float)x + (float)w, (float)y + (float)h};
|
||||
drawListVerts[drawListVertNum++] = (vertex){(float)x, (float)y + (float)h};
|
||||
drawListIndices[drawListCount++] = base;
|
||||
drawListIndices[drawListCount++] = base + 1;
|
||||
drawListIndices[drawListCount++] = base + 1;
|
||||
drawListIndices[drawListCount++] = base + 2;
|
||||
drawListIndices[drawListCount++] = base + 2;
|
||||
drawListIndices[drawListCount++] = base + 3;
|
||||
drawListIndices[drawListCount++] = base + 3;
|
||||
drawListIndices[drawListCount++] = base;
|
||||
|
||||
if (drawListCount == DRAWLIST_MAX_SIZE)
|
||||
FlushDrawBuffers();
|
||||
#else
|
||||
#pragma message("DRAWLIST_MAX_SIZE is too low, DrawRect functionality disabled")
|
||||
#endif
|
||||
}
|
||||
|
||||
void DrawLine(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
if (drawColour != colour)
|
||||
UpdateDrawColour();
|
||||
else if (drawListCount > DRAWLIST_MAX_SIZE - 2)
|
||||
FlushDrawBuffers();
|
||||
|
||||
vertex from = {x1, y1}, to = {x2, y2};
|
||||
if (drawListVertNum > 0 && memcmp(&from, &drawListVerts[drawListVertNum - 1], sizeof(vertex)) == 0)
|
||||
{
|
||||
// Reuse last vertex
|
||||
drawListIndices[drawListCount++] = drawListVertNum - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
drawListVerts[drawListVertNum] = from;
|
||||
drawListIndices[drawListCount++] = drawListVertNum++;
|
||||
}
|
||||
drawListVerts[drawListVertNum] = to;
|
||||
drawListIndices[drawListCount++] = drawListVertNum++;
|
||||
}
|
||||
|
||||
static void ArcSubSlice(
|
||||
float x, float y,
|
||||
float magx, float magy,
|
||||
float angle, float stride,
|
||||
int num)
|
||||
{
|
||||
if (!num)
|
||||
return;
|
||||
drawListVerts[drawListVertNum] = (vertex){
|
||||
x + cosf(angle) * magx,
|
||||
y - sinf(angle) * magy};
|
||||
for (int i = 1; i <= num; ++i)
|
||||
{
|
||||
const float theta = angle + stride * (float)i;
|
||||
float ofsx = cosf(theta) * magx;
|
||||
float ofsy = sinf(theta) * magy;
|
||||
|
||||
drawListIndices[drawListCount++] = drawListVertNum++;
|
||||
drawListVerts[drawListVertNum] = (vertex){x + ofsx, y - ofsy};
|
||||
drawListIndices[drawListCount++] = drawListVertNum;
|
||||
}
|
||||
drawListVertNum++;
|
||||
}
|
||||
|
||||
static void ArcSlice(
|
||||
float x, float y,
|
||||
float magx, float magy,
|
||||
float angle, float stride,
|
||||
int steps)
|
||||
{
|
||||
int i = 0;
|
||||
while (true)
|
||||
{
|
||||
const int subSteps = (MIN(drawListCount + (steps - i) * 2, DRAWLIST_MAX_SIZE) - drawListCount) / 2;
|
||||
if (subSteps)
|
||||
ArcSubSlice(x, y, magx, magy, angle, stride, subSteps);
|
||||
i += subSteps;
|
||||
if (i < steps)
|
||||
FlushDrawBuffers();
|
||||
else
|
||||
break;
|
||||
angle += stride * (float)subSteps;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawCircleSteps(int x, int y, int r, int steps)
|
||||
{
|
||||
if (drawColour != colour)
|
||||
UpdateDrawColour();
|
||||
|
||||
// Circles look better when offset negatively by half a pixel w/o MSAA
|
||||
const float fx = (antialias ? (float)x : (float)x - 0.5f);
|
||||
const float fy = (antialias ? (float)y : (float)y - 0.5f);
|
||||
|
||||
const float stepSz = (float)TAU / (float)abs(steps);
|
||||
const float mag = (float)r;
|
||||
// Check if whole circle can fit in the buffer
|
||||
if (drawListCount > DRAWLIST_MAX_SIZE - steps * 2)
|
||||
{
|
||||
// Draw circle as segmented arcs
|
||||
ArcSlice(fx, fy, mag, mag, 0.0f, stepSz, steps);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw whole circle in a single loop
|
||||
uint16_t base = drawListVertNum;
|
||||
drawListVerts[drawListVertNum] = (vertex){fx + mag, fy};
|
||||
drawListIndices[drawListCount++] = drawListVertNum++;
|
||||
for (int i = 1; i < steps; ++i)
|
||||
{
|
||||
const float theta = stepSz * (float)i;
|
||||
float ofsx = cosf(theta) * mag;
|
||||
float ofsy = sinf(theta) * mag;
|
||||
|
||||
drawListVerts[drawListVertNum] = (vertex){fx + ofsx, fy + ofsy};
|
||||
drawListIndices[drawListCount++] = drawListVertNum;
|
||||
drawListIndices[drawListCount++] = drawListVertNum++;
|
||||
}
|
||||
drawListIndices[drawListCount++] = base;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawArcSteps(int x, int y, int r, int startAng, int endAng, int steps)
|
||||
{
|
||||
if (drawColour != colour)
|
||||
UpdateDrawColour();
|
||||
|
||||
// Arcs look better when offset negatively by half a pixel w/o MSAA
|
||||
const float fx = (antialias ? (float)x : (float)x - 0.5f);
|
||||
const float fy = (antialias ? (float)y : (float)y - 0.5f);
|
||||
const float mag = (float)r;
|
||||
const float fstart = (float)startAng * (float)DEG2RAD;
|
||||
const float fstepSz = (float)(endAng - startAng) / (float)abs(steps) * (float)DEG2RAD;
|
||||
ArcSlice(fx, fy, mag, mag, fstart, fstepSz, steps);
|
||||
}
|
||||
|
||||
void DrawPresent(void)
|
||||
{
|
||||
FlushDrawBuffers();
|
||||
SDL_GL_SwapWindow(window);
|
||||
#ifndef NDEBUG
|
||||
fprintf(stderr, "%d draw call(s)\n", drawCount);
|
||||
#endif
|
||||
drawCount = 0;
|
||||
}
|
||||
|
10
src/frag.glsl
Normal file
10
src/frag.glsl
Normal file
@ -0,0 +1,10 @@
|
||||
#version 330 core
|
||||
|
||||
out vec4 outColour;
|
||||
|
||||
uniform vec4 uColour;
|
||||
|
||||
void main()
|
||||
{
|
||||
outColour = uColour;
|
||||
}
|
10
src/vert.glsl
Normal file
10
src/vert.glsl
Normal file
@ -0,0 +1,10 @@
|
||||
#version 330 core
|
||||
|
||||
in vec2 inPos;
|
||||
|
||||
uniform mat4 uView;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = uView * vec4(inPos, 0.0, 1.0);
|
||||
}
|
109
tools/bin2h.py
Normal file
109
tools/bin2h.py
Normal file
@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import BinaryIO, TextIO
|
||||
import re
|
||||
|
||||
|
||||
label_invalid = re.compile(r"\W") # Turn invalid label chars into underscores
|
||||
label_lstrip = re.compile(r"^[\d_]+") # Trim all underscores or numbers from the beginning
|
||||
|
||||
|
||||
def sanitise_label(label: str) -> str:
|
||||
tmp = label_invalid.sub("_", label, re.I)
|
||||
return label_lstrip.sub("", tmp, re.I)
|
||||
|
||||
|
||||
def bin2h(name: str, binf: BinaryIO, h: TextIO):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def txt2h(name: str, txt: TextIO, h: TextIO):
|
||||
h.write(f"static const char* const {sanitise_label(name)} = \n")
|
||||
h.write("\"")
|
||||
|
||||
remap = {
|
||||
"\a": "\\a", "\b": "\\b", "\f": "\\f", "\n": "\\n", "\r": "\\r",
|
||||
"\t": "\\t", "\v": "\\v", "\\": "\\\\", "\"": "\\\""}
|
||||
esc_numeric = False
|
||||
for idx, c in enumerate(txt.read()):
|
||||
if m := remap.get(c):
|
||||
h.write(m)
|
||||
else:
|
||||
if (i := ord(c)) < 0x7F:
|
||||
if c.isprintable() and not (esc_numeric and c.isnumeric()):
|
||||
h.write(c)
|
||||
else:
|
||||
if i < 8:
|
||||
h.write(f"\\{i:o}")
|
||||
else:
|
||||
h.write(f"\\x{i:02X}")
|
||||
esc_numeric = True
|
||||
continue
|
||||
elif i < 0x10000:
|
||||
h.write(f"\\u{i:04X}")
|
||||
else:
|
||||
h.write(f"\\U{i:08X}")
|
||||
esc_numeric = False
|
||||
|
||||
h.write("\";\n")
|
||||
|
||||
|
||||
def main():
|
||||
usage = "Usage [-b data.bin] [-t text.txt] <out.h>"
|
||||
|
||||
binfiles = []
|
||||
txtfiles = []
|
||||
out = None
|
||||
opt = None
|
||||
for arg in sys.argv[1:]:
|
||||
if opt is not None:
|
||||
if opt == "b":
|
||||
binfiles.append(arg)
|
||||
elif opt == "t":
|
||||
txtfiles.append(arg)
|
||||
else:
|
||||
sys.exit(usage)
|
||||
opt = None
|
||||
if arg.startswith("-"):
|
||||
opt = arg[1:]
|
||||
else:
|
||||
out = Path(arg)
|
||||
|
||||
if opt is not None or out is None:
|
||||
sys.exit(usage)
|
||||
if len(binfiles) == 0 and len(txtfiles) == 0:
|
||||
sys.exit(usage)
|
||||
|
||||
with out.open("w") as h:
|
||||
guard = f"BIN2H_{sanitise_label(out.name).upper()}"
|
||||
if not guard.endswith("_H"):
|
||||
guard += "_H"
|
||||
h.writelines([
|
||||
"/*DO NOT EDIT*/\n",
|
||||
"// Autogenerated by bin2h\n",
|
||||
"\n",
|
||||
f"#ifndef {guard}\n",
|
||||
f"#define {guard}\n",
|
||||
"\n"])
|
||||
|
||||
# Write binaries
|
||||
for i in binfiles:
|
||||
path = Path(i)
|
||||
with path.open("rb") as file:
|
||||
bin2h(path.name, file, h)
|
||||
|
||||
# Write texts
|
||||
for i in txtfiles:
|
||||
path = Path(i)
|
||||
with path.open("r") as file:
|
||||
txt2h(path.name, file, h)
|
||||
|
||||
h.writelines([
|
||||
"\n",
|
||||
f"#endif//{guard}\n"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Reference in New Issue
Block a user