Skip to content

Instantly share code, notes, and snippets.

@Journeyman1337
Last active December 15, 2020 17:18
Show Gist options
  • Save Journeyman1337/a3038f40bf7b948e08af9db174c9a395 to your computer and use it in GitHub Desktop.
Save Journeyman1337/a3038f40bf7b948e08af9db174c9a395 to your computer and use it in GitHub Desktop.
roguelike renderer header only library that works but not finished (or documented). Most of the work per frame is done in the vertex shader, which is simillar to how Dwarf Fortress does it. I didn't continue working on this because I found it to be not very practical in a header only format.
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define RLH_IMPLEMENTATION
#define RLH_OPENGL_3_3
#include <roguelike.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <iostream>
#include <chrono>
#include <functional>
struct Color24
{
rlh_byte_t r, g, b;
Color24() :
r(255),
g(255),
b(255)
{}
Color24(rlh_byte_t r, rlh_byte_t g, rlh_byte_t b) :
r(r),
g(g),
b(b)
{}
rlh_byte_t* Data()
{
return (rlh_byte_t*)this;
}
};
int main()
{
if (!glfwInit())
{
std::cout << "glfwInit()";
return 1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
#endif
GLFWwindow* window = glfwCreateWindow(1000, 1000, "rlh", nullptr, nullptr);
if (!window)
{
std::cout << "window failed\n";
glfwTerminate();
return 1;
}
glfwMakeContextCurrent(window);
//glfwSwapInterval(0);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD\n";
glfwTerminate();
return 1;
}
glViewport(0, 0, 1000, 1000);
int swidth, sheight;
uint8_t* px = (uint8_t*)stbi_load("rexpaint_cp437_10x10.png", &swidth, &sheight, nullptr, 4);
float* stpq = new float[256 * 4];
{
for (float y = 0; y < 16; y++)
{
for (float x = 0; x < 16; x++)
{
for (int q = 0; q < 4; q++)
{
switch (q)
{
case 0:
stpq[i++] = uvtilesize * x;
break;
case 1:
stpq[i++] = uvtilesize * x + uvtilesize;
break;
case 2:
stpq[i++] = uvtilesize * y;
break;
case 3:
stpq[i++] = uvtilesize * y + uvtilesize;
break;
}
}
}
}
}
if (!rlhInit(RLH_API_OPENGL_3_3, RLH_TRUE))
{
std::cout << "RLH failed to initialize!" << std::endl;
}
RlhRendererCreateInfo rci;
rci.SheetHeight = sheight;
rci.SheetWidth = swidth;
rci.SheetRGBA = px;
rci.SpriteCount = 256;
rci.FontmapSTPQ = stpq;
rci.ScreenTilesTall = 100;
rci.ScreenTilesWide = 100;
RlhRenderer* r = rlhCreateRendererGL(rci);
stbi_image_free(px);
delete[] stpq;
//glClearColor(0, 0, 0, 1);
//glEnable(GL_BLEND);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
std::chrono::microseconds time = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()).time_since_epoch();
float frametime = 0.0f;
int frames = 0;
Color24 fc = Color24(255, 255, 0);
Color24 bg = Color24(255, 0, 255);
while (!glfwWindowShouldClose(window))
{
glClear(GL_COLOR_BUFFER_BIT);
glfwPollEvents();
for (int x = 0; x < 100; x++)
{
for (int y = 0; y < 100; y++)
{
rlhSetRendererData(r, x, y, (rlh_ch_t)x, fc.Data(), bg.Data());
}
}
rlhDrawRendererGL(r);
rlhClearRendererData(r);
glfwSwapBuffers(window);
std::chrono::time_point<std::chrono::high_resolution_clock> new_time = std::chrono::high_resolution_clock::now();
std::chrono::microseconds e = std::chrono::time_point_cast<std::chrono::microseconds>(new_time).time_since_epoch();
std::chrono::microseconds duration = e - time;
time = e;
frametime += ((float)duration.count() / 1000000.0f);
frames++;
if (frametime >= 1.0f)
{
frametime = 0.0f;
std::cout << "fps: " << frames << "\n";
frames = 0;
}
}
rlhDestroyRendererGL(r);
rlhTerminate();
return 0;
}
/*
MIT License
Copyright (c) 2020 Daniel Valcour
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
typedef float rlh_float_t;
typedef uint8_t rlh_byte_t;
typedef uint8_t rlh_bool_t;
typedef uint32_t rlh_uint_t;
typedef uint16_t rlh_ch_t;
typedef size_t rlh_size_t;
typedef uint32_t rlh_enum_t;
struct RlhRenderer;
struct RlhRendererCreateInfo
{
rlh_size_t ScreenTilesWide;
rlh_size_t ScreenTilesTall;
rlh_byte_t* SheetRGBA;
rlh_size_t SheetWidth;
rlh_size_t SheetHeight;
rlh_size_t SpriteCount;
rlh_float_t* FontmapSTPQ;
};
#define RLH_NO_API 0
#define RLH_API_OPENGL_3_3 1
#define RLH_FAILURE 0
#define RLH_SUCCESS 1
#define RLH_FALSE 0
#define RLH_TRUE 1
rlh_enum_t rlhInit(rlh_enum_t api, rlh_bool_t auto_bind);
void rlhTerminate();
void rlhSetRendererData(RlhRenderer* renderer, rlh_size_t x, rlh_size_t y, rlh_ch_t ch, rlh_byte_t* fc, rlh_byte_t* bc);
void rlhClearRendererData(RlhRenderer* renderer);
#ifndef RLH_IMPLEMENTATION
extern RlhRenderer* (*rlhCreateRendererGL)(RlhRendererCreateInfo rendererCreateInfo);
extern void (*rlhBindRendererGL)(RlhRenderer* renderer);
extern void (*rlhResizeRendererGL)(RlhRenderer* renderer, rlh_size_t screen_tiles_wide, rlh_size_t screen_tiles_tall);
extern void (*rlhTransformRendererGL)(RlhRenderer* renderer, rlh_float_t* matrix4x4);
extern void (*rlhDrawRendererGL)(RlhRenderer* renderer);
extern void (*rlhDestroyRendererGL)(RlhRenderer* renderer);
#endif
#ifdef RLH_IMPLEMENTATION
static rlh_enum_t sAPI;
static rlh_bool_t sAUTOBIND;
static RlhRenderer* sBOUND;
static RlhRenderer* (*rlhCreateRendererGL)(RlhRendererCreateInfo rendererCreateInfo);
static void (*rlhBindRendererGL)(RlhRenderer* renderer);
static void (*rlhResizeRendererGL)(RlhRenderer* renderer, rlh_size_t screen_tiles_wide, rlh_size_t screen_tiles_tall);
static void (*rlhTransformRendererGL)(RlhRenderer* renderer, rlh_float_t* matrix4x4);
static void (*rlhDrawRendererGL)(RlhRenderer* renderer);
static void (*rlhDestroyRendererGL)(RlhRenderer* renderer);
#ifdef RLH_OPENGL_3_3
RlhRenderer* rlhCreateRendererOpengl33(RlhRendererCreateInfo rendererCreateInfo);
void rlhDestroyRendererOpengl33(RlhRenderer* renderer);
void rlhBindRendererOpengl33(RlhRenderer* renderer);
void rlhResizeRendererOpengl33(RlhRenderer* renderer, rlh_size_t screen_tiles_wide, rlh_size_t screen_tiles_tall);
void rlhTransformRendererOpengl33(RlhRenderer* renderer, rlh_float_t* matrix);
void rlhDrawRendererOpnegl33(RlhRenderer* renderer);
#endif
struct RlhRenderer
{
rlh_size_t SheetWidth;
rlh_size_t SheetHeight;
rlh_size_t SpriteCount;
rlh_byte_t* SheetRGBA;
rlh_float_t* FontmapSTPQ;
rlh_size_t TilesWide;
rlh_size_t TilesTall;
rlh_byte_t* Data;
#ifdef RLH_OPENGL_3_3
GLuint Program = 0;
GLuint VAO = 0;
GLuint SheetTEX = 0;
GLuint DataBUF = 0;
GLuint DataTEX;
GLuint FontmapBUF = 0;
GLuint FontmapTEX = 0;
#endif
};
rlh_enum_t rlhInit(rlh_enum_t api, rlh_bool_t auto_bind)
{
if (api == RLH_API_OPENGL_3_3)
{
#ifdef RLH_OPENGL_3_3
sAPI = api;
rlhCreateRendererGL = rlhCreateRendererOpengl33;
rlhDestroyRendererGL = rlhDestroyRendererOpengl33;
rlhBindRendererGL = rlhBindRendererOpengl33;
rlhResizeRendererGL = rlhResizeRendererOpengl33;
rlhTransformRendererGL = rlhTransformRendererOpengl33;
rlhDrawRendererGL = rlhDrawRendererOpnegl33;
sAUTOBIND = auto_bind;
return RLH_SUCCESS;
#else
return RLH_FAILURE;
#endif
}
return RLH_FAILURE;
}
void rlhTerminate()
{
sAPI = RLH_NO_API;
rlhCreateRendererGL = NULL;
rlhDestroyRendererGL = NULL;
rlhBindRendererGL = NULL;
rlhResizeRendererGL = NULL;
rlhTransformRendererGL = NULL;
rlhDrawRendererGL = NULL;
}
void rlhSetRendererData(RlhRenderer* renderer, rlh_size_t x, rlh_size_t y, rlh_ch_t ch, rlh_byte_t* fc, rlh_byte_t* bc)
{
size_t index = (y * renderer->TilesWide + x) * 8;
renderer->Data[index++] = (ch % 255);
renderer->Data[index++] = (ch / 255);
renderer->Data[index++] = fc[0];
renderer->Data[index++] = fc[1];
renderer->Data[index++] = fc[2];
renderer->Data[index++] = bc[0];
renderer->Data[index++] = bc[1];
renderer->Data[index] = bc[2];
}
void rlhClearRendererData(RlhRenderer* renderer)
{
for (size_t i = 0; i < renderer->TilesWide * renderer->TilesTall * (rlh_size_t)8; i++)
{
renderer->Data[i] = 0;
}
}
inline RlhRenderer* createRendererCommon(RlhRendererCreateInfo rendererCreateInfo, rlh_enum_t flip_y)
{
RlhRenderer* renderer = (RlhRenderer*)malloc(sizeof(RlhRenderer));
renderer->SheetWidth = rendererCreateInfo.SheetWidth;
renderer->SheetHeight = rendererCreateInfo.SheetHeight;
renderer->SpriteCount = rendererCreateInfo.SpriteCount;
renderer->TilesWide = rendererCreateInfo.ScreenTilesWide;
renderer->TilesTall = rendererCreateInfo.ScreenTilesTall;
renderer->SheetRGBA = (rlh_byte_t*)malloc(sizeof(rlh_byte_t) * renderer->SheetWidth * renderer->SheetHeight * 4);
for (size_t i = 0; i < renderer->SheetWidth * renderer->SheetHeight * (rlh_size_t)4; i++)
{
renderer->SheetRGBA[i] = rendererCreateInfo.SheetRGBA[i];
}
renderer->FontmapSTPQ = (rlh_float_t*)malloc(sizeof(rlh_float_t) * renderer->SpriteCount * (rlh_size_t)4);
if (!flip_y)
{
for (size_t i = 0; i < renderer->SpriteCount * (size_t)4; i++)
{
renderer->FontmapSTPQ[i] = rendererCreateInfo.FontmapSTPQ[i];
}
}
else
{
for (size_t i = 0; i < renderer->SpriteCount * (size_t)4; i++)
{
if (i % 4 == 2)
{
renderer->FontmapSTPQ[i] = rendererCreateInfo.FontmapSTPQ[i + 1];
}
else if (i % 4 == 3)
{
renderer->FontmapSTPQ[i] = rendererCreateInfo.FontmapSTPQ[i - 1];
}
else
{
renderer->FontmapSTPQ[i] = rendererCreateInfo.FontmapSTPQ[i];
}
}
}
renderer->Data = (rlh_byte_t*)malloc(sizeof(rlh_byte_t) * renderer->TilesWide * renderer->TilesTall * (rlh_size_t)8);
rlhClearRendererData(renderer);
return renderer;
}
#ifdef RLH_OPENGL_3_3
const char* vertex_shader_source = R"(
#version 330 core
uniform ivec2 dimensions;
uniform usamplerBuffer data;
uniform samplerBuffer fontmap;
uniform mat4 matrix;
out vec2 UV;
out vec4 fc;
out vec4 bc;
vec4 getVertexPosition(int tile, int tile_vertex)
{
int tile_x = tile % dimensions.x;
int tile_y = tile / dimensions.y;
float tile_width = 1.0 / float(dimensions.x);
float tile_height = 1.0 / float(dimensions.y);
float tile_lx = float(tile_x) * tile_width;
float tile_by = float(tile_y) * tile_height;
float tile_rx = tile_lx + tile_width;
float tile_ty = tile_by + tile_height;
vec4 position_square = vec4(tile_lx, tile_rx, tile_ty, tile_by);
vec2 vert_positions[6] = vec2[](position_square.sp, position_square.sq, position_square.tq, position_square.sp, position_square.tq, position_square.tp);
vec2 position = vert_positions[tile_vertex];
return vec4(position, 0.0, 1.0) * matrix;
}
vec2 getVertexUV(int ch, int tile_vertex)
{
vec4 uv_square = texelFetch(fontmap, ch);
vec2 vert_uvs[6] = vec2[](uv_square.sp, uv_square.sq, uv_square.tq, uv_square.sp, uv_square.tq, uv_square.tp);
vec2 uv = vert_uvs[tile_vertex];
return uv;
}
void main() {
int tile = gl_VertexID / 6;
int tile_vertex = gl_VertexID % 6;
uint ch = texelFetch(data, tile * 8).r;
ch += texelFetch(data, tile * 8 + 1).r * 255u;
fc = vec4(
float(texelFetch(data, tile * 8 + 2).r) / 255.0,
float(texelFetch(data, tile * 8 + 3).r) / 255.0,
float(texelFetch(data, tile * 8 + 4).r) / 255.0,
1.0);
bc = vec4(
float(texelFetch(data, tile * 8 + 5).r) / 255.0,
float(texelFetch(data, tile * 8 + 6).r) / 255.0,
float(texelFetch(data, tile * 8 + 7).r) / 255.0,
1.0);
gl_Position = getVertexPosition(tile, tile_vertex);
UV = getVertexUV(int(ch), tile_vertex);
}
)";
const char* fragment_shader_source = R"(
#version 330 core
uniform sampler2D cp;
in vec2 UV;
in vec4 fc;
in vec4 bc;
void main()
{
vec4 c = texture(cp, UV);
c = vec4(mix(bc, fc * c, c.a).rgb, 1.0);
gl_FragColor = c;
}
)";
const GLfloat kScreenMatrix[16] =
{
2.0f, 0.0f, 0.0f, -1.0f,
0.0f, -2.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
RlhRenderer* rlhCreateRendererOpengl33(RlhRendererCreateInfo rendererCreateInfo)
{
RlhRenderer* renderer = createRendererCommon(rendererCreateInfo, RLH_TRUE);
{
int gl_vertex_shader, gl_fragment_shader;
gl_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(gl_vertex_shader, 1, &vertex_shader_source, nullptr);
glCompileShader(gl_vertex_shader);
gl_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(gl_fragment_shader, 1, &fragment_shader_source, nullptr);
glCompileShader(gl_fragment_shader);
renderer->Program = glCreateProgram();
glAttachShader(renderer->Program, gl_vertex_shader);
glAttachShader(renderer->Program, gl_fragment_shader);
glLinkProgram(renderer->Program);
glDetachShader(renderer->Program, gl_vertex_shader);
glDetachShader(renderer->Program, gl_fragment_shader);
glDeleteShader(gl_vertex_shader);
glDeleteShader(gl_fragment_shader);
}
glGenVertexArrays(1, &renderer->VAO);
glGenTextures(1, &renderer->SheetTEX);
glBindTexture(GL_TEXTURE_2D, renderer->SheetTEX);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, renderer->SheetWidth, renderer->SheetHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->SheetRGBA);
glGenBuffers(1, &renderer->FontmapBUF);
glBindBuffer(GL_TEXTURE_BUFFER, renderer->FontmapBUF);
glBufferData(GL_TEXTURE_BUFFER, (rlh_size_t)renderer->SpriteCount * (rlh_size_t)4 * sizeof(float), renderer->FontmapSTPQ, GL_STATIC_DRAW);
glGenTextures(1, &renderer->FontmapTEX);
glBindTexture(GL_TEXTURE_BUFFER, renderer->FontmapTEX);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, renderer->FontmapBUF);
glGenBuffers(1, &renderer->DataBUF);
glBindBuffer(GL_TEXTURE_BUFFER, renderer->DataBUF);
glBufferData(GL_TEXTURE_BUFFER, (rlh_size_t)renderer->TilesTall * (rlh_size_t)renderer->TilesWide * (sizeof(uint16_t) + ((rlh_size_t)2 * sizeof(uint8_t))), renderer->Data, GL_DYNAMIC_DRAW);
glGenTextures(1, &renderer->DataTEX);
glBindTexture(GL_TEXTURE_BUFFER, renderer->DataTEX);
glTexBuffer(GL_TEXTURE_BUFFER, GL_R8UI, renderer->DataBUF);
glBindVertexArray(renderer->VAO);
glUseProgram(renderer->Program);
glUniform2i(glGetUniformLocation(renderer->Program, "dimensions"), renderer->TilesWide, renderer->TilesTall);
glUniform1i(glGetUniformLocation(renderer->Program, "sheet"), 0);
glUniform1i(glGetUniformLocation(renderer->Program, "fontmap"), 1);
glUniform1i(glGetUniformLocation(renderer->Program, "data"), 2);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, renderer->SheetTEX);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, renderer->FontmapTEX);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_BUFFER, renderer->DataTEX);
glUniformMatrix4fv(glGetUniformLocation(renderer->Program, "matrix"), 1, false, &kScreenMatrix[0]);
return renderer;
}
void rlhDestroyRendererOpengl33(RlhRenderer* renderer)
{
free(renderer->Data);
free(renderer->SheetRGBA);
free(renderer->FontmapSTPQ);
glDeleteVertexArrays(1, &renderer->VAO);
glDeleteProgram(renderer->Program);
glDeleteBuffers(1, &renderer->DataBUF);
glDeleteBuffers(1, &renderer->FontmapBUF);
glDeleteTextures(1, &renderer->DataTEX);
glDeleteTextures(1, &renderer->FontmapTEX);
glDeleteTextures(1, &renderer->SheetTEX);
renderer->SheetHeight = 0;
renderer->SheetWidth = 0;
renderer->SpriteCount = 0;
}
void rlhBindRendererOpengl33(RlhRenderer* renderer)
{
sBOUND = renderer;
glBindVertexArray(renderer->VAO);
glUseProgram(renderer->Program);
}
void rlhResizeRendererOpengl33(RlhRenderer* renderer, rlh_size_t screen_tiles_wide, rlh_size_t screen_tiles_tall)
{
if (renderer->TilesWide != screen_tiles_wide && renderer->TilesTall != screen_tiles_tall)
{
if (sAUTOBIND && sBOUND != renderer)
{
rlhBindRendererGL(renderer);
}
renderer->TilesWide = screen_tiles_wide;
renderer->TilesTall = screen_tiles_tall;
glUniform2i(glGetUniformLocation(renderer->Program, "dimensions"), renderer->TilesWide, renderer->TilesTall);
free(renderer->Data);
renderer->Data = (rlh_byte_t*)malloc(sizeof(rlh_byte_t) * renderer->TilesWide * renderer->TilesTall * (rlh_size_t)8);
rlhClearRendererData(renderer);
}
}
void rlhTransformRendererOpengl33(RlhRenderer* renderer, rlh_float_t* matrix)
{
matrix[5] *= -1.0f;
matrix[7] *= -1.0f;
if (sAUTOBIND && sBOUND != renderer)
{
rlhBindRendererGL(renderer);
}
glUniformMatrix4fv(glGetUniformLocation(renderer->Program, "matrix"), 1, false, matrix);
}
void rlhDrawRendererOpnegl33(RlhRenderer* renderer)
{
if (sAUTOBIND && sBOUND != renderer)
{
rlhBindRendererGL(renderer);
}
glBindBuffer(GL_TEXTURE_BUFFER, renderer->DataBUF);
glBufferData(GL_TEXTURE_BUFFER, (rlh_size_t)renderer->TilesWide * (rlh_size_t)renderer->TilesTall * (rlh_size_t)8, renderer->Data, GL_DYNAMIC_DRAW);
glDrawArrays(GL_TRIANGLES, 0, renderer->TilesWide * renderer->TilesTall * (rlh_size_t)6);
}
#endif
#endif
#ifdef __cplusplus
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment