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)
|
if (USE_OPENGL)
|
||||||
include(GL3WHelper)
|
include(GL3WHelper)
|
||||||
add_gl3w(gl3w)
|
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()
|
endif()
|
||||||
|
|
||||||
add_executable(${TARGET} ${SOURCES})
|
add_executable(${TARGET} ${SOURCES})
|
||||||
|
target_include_directories(${TARGET} PRIVATE
|
||||||
|
$<$<BOOL:${USE_OPENGL}>:${CMAKE_CURRENT_BINARY_DIR}>)
|
||||||
target_link_libraries(${TARGET}
|
target_link_libraries(${TARGET}
|
||||||
$<$<PLATFORM_ID:Windows>:SDL2::SDL2main>
|
$<$<PLATFORM_ID:Windows>:SDL2::SDL2main>
|
||||||
SDL2::SDL2
|
SDL2::SDL2
|
||||||
|
@ -1,20 +1,47 @@
|
|||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
|
#include "glslShaders.h"
|
||||||
#include "maths.h"
|
#include "maths.h"
|
||||||
#include <GL/gl3w.h>
|
#include <GL/gl3w.h>
|
||||||
#include <SDL_video.h>
|
#include <SDL_video.h>
|
||||||
#include <stdbool.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_MAJOR 3
|
||||||
#define OPENGL_VERSION_MINOR 3
|
#define OPENGL_VERSION_MINOR 3
|
||||||
|
|
||||||
static SDL_GLContext* ctx = NULL;
|
static SDL_GLContext* ctx = NULL;
|
||||||
static SDL_Window* window = NULL;
|
static SDL_Window* window = NULL;
|
||||||
static uint32_t colour = 0x00000000;
|
static uint32_t
|
||||||
static uint32_t clrColour = 0x00000000;
|
colour = 0x00000000,
|
||||||
static bool antialias = false;
|
drawColour = 0x00000000,
|
||||||
|
clrColour = 0x00000000;
|
||||||
|
static bool antialias = false;
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
#define DRAWLIST_MAX_SIZE 480
|
||||||
#pragma clang diagnostic ignored "-Wunused-function"
|
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(
|
static void GlErrorCb(
|
||||||
GLenum source,
|
GLenum source,
|
||||||
GLenum type,
|
GLenum type,
|
||||||
@ -27,18 +54,81 @@ static void GlErrorCb(
|
|||||||
if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
|
if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
|
||||||
printf("%s\n", message);
|
printf("%s\n", message);
|
||||||
}
|
}
|
||||||
#pragma clang diagnostic pop
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void DrawWindowHints(void)
|
void DrawWindowHints(void)
|
||||||
{
|
{
|
||||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); // Enable MSAA
|
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_MAJOR_VERSION, OPENGL_VERSION_MAJOR);
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, OPENGL_VERSION_MINOR);
|
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_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)
|
int InitDraw(SDL_Window* _window)
|
||||||
@ -51,6 +141,14 @@ int InitDraw(SDL_Window* _window)
|
|||||||
return -1;
|
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
|
// Load Core profile extensions
|
||||||
if (gl3wInit() != GL3W_OK)
|
if (gl3wInit() != GL3W_OK)
|
||||||
{
|
{
|
||||||
@ -65,23 +163,98 @@ int InitDraw(SDL_Window* _window)
|
|||||||
|
|
||||||
// Set debug callback
|
// Set debug callback
|
||||||
#if !defined NDEBUG && !defined __APPLE__
|
#if !defined NDEBUG && !defined __APPLE__
|
||||||
glDebugMessageCallback(GlErrorCb, nullptr);
|
glDebugMessageCallback(GlErrorCb, NULL);
|
||||||
glEnable(GL_DEBUG_OUTPUT);
|
glEnable(GL_DEBUG_OUTPUT);
|
||||||
#endif
|
#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
|
// Compile shaders
|
||||||
int res;
|
GLuint vert = CompilerShader(vert_glsl, GL_VERTEX_SHADER);
|
||||||
if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &res) == 0 && res == 1)
|
if (!vert)
|
||||||
if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &res) == 0 && res > 0)
|
return -1;
|
||||||
antialias = true;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuitDraw(void)
|
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_MakeCurrent(window, NULL);
|
||||||
SDL_GL_DeleteContext(ctx);
|
SDL_GL_DeleteContext(ctx);
|
||||||
ctx = NULL;
|
ctx = NULL;
|
||||||
@ -99,6 +272,49 @@ size GetDrawSizeInPixels(void)
|
|||||||
void SetDrawViewport(size size)
|
void SetDrawViewport(size size)
|
||||||
{
|
{
|
||||||
glViewport(0, 0, size.w, size.h);
|
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)
|
if (clrColour != colour)
|
||||||
{
|
{
|
||||||
const float mul = 1.0f / 255.0f;
|
GLfloat clear[4]; UnpackColour(clear);
|
||||||
glClearColor(
|
glClearColor(clear[0], clear[1], clear[2], clear[3]);
|
||||||
(GLclampf)((colour & 0xFF000000) >> 24) * mul,
|
|
||||||
(GLclampf)((colour & 0x00FF0000) >> 16) * mul,
|
|
||||||
(GLclampf)((colour & 0x0000FF00) >> 8) * mul,
|
|
||||||
(GLclampf)((colour & 0x000000FF)) * mul);
|
|
||||||
clrColour = colour;
|
clrColour = colour;
|
||||||
}
|
}
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
@ -124,29 +336,162 @@ void DrawClear(void)
|
|||||||
|
|
||||||
void DrawPoint(int x, int y)
|
void DrawPoint(int x, int y)
|
||||||
{
|
{
|
||||||
|
DrawCircleSteps(x, y, 1, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawRect(int x, int y, int w, int h)
|
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)
|
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)
|
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)
|
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)
|
void DrawPresent(void)
|
||||||
{
|
{
|
||||||
|
FlushDrawBuffers();
|
||||||
SDL_GL_SwapWindow(window);
|
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