File DrawContext.c
File List > engine > src > rendering > DrawContext.c
Go to the documentation of this file
#include "DrawContext.h"
#include <string.h>
#include <glad/glad.h>
#include <cglm/cglm.h>
#include "ObjectPool.h"
#include "Widget.h"
#include "DynArray.h"
#include "AssertLib.h"
#include "PlatformDefs.h"
#include "Game2DLayer.h"
#include "Log.h"
const char* uiVert =
#if GAME_GL_API_TYPE == GAME_GL_API_TYPE_CORE
"#version 330 core\n"
"layout (location = 0) in vec2 aPos;\n"
"layout (location = 1) in vec2 aUv;\n"
"layout (location = 2) in vec4 aColour;\n"
"out vec2 UV;\n"
"out vec4 Colour;\n"
"uniform mat4 screenToClipMatrix;\n"
"void main()\n"
"{\n"
"gl_Position = screenToClipMatrix * vec4(aPos, 0.0, 1.0);\n"
"UV = aUv;\n"
"Colour = aColour;"
"}\n"
#elif GAME_GL_API_TYPE == GAME_GL_API_TYPE_ES
"#version 300 es\n"
"in vec2 aPos;\n"
"in vec2 aUv;\n"
"in vec4 aColour;\n"
"out vec2 UV;\n"
"out vec4 Colour;\n"
"uniform mat4 screenToClipMatrix;\n"
"void main()\n"
"{\n"
"gl_Position = screenToClipMatrix * vec4(aPos, 0.0, 1.0);\n"
"UV = aUv;\n"
"Colour = aColour;"
"}\n"
#endif
;
const char* uiFrag =
#if GAME_GL_API_TYPE == GAME_GL_API_TYPE_CORE
"#version 330 core\n"
"out vec4 FragColor;\n"
"in vec4 Colour;\n"
"in vec2 UV;\n"
"uniform sampler2D ourTexture;\n"
"void main()\n"
"{\n"
"FragColor = texture(ourTexture, UV) * Colour;\n"
"}\n"
#elif GAME_GL_API_TYPE == GAME_GL_API_TYPE_ES
"#version 300 es\n"
"precision highp float;\n"
"out vec4 FragColor;\n"
"in vec4 Colour;\n"
"in vec2 UV;\n"
"uniform sampler2D ourTexture;\n"
"void main()\n"
"{\n"
"FragColor = texture(ourTexture, UV) * Colour;\n"
"}\n"
#endif
;
const char* worldspaceVert =
"#version 300 es\n"
"in vec2 aPos;\n"
"in vec2 aUv;\n"
"out vec2 UV;\n"
"uniform mat4 vp;\n"
"void main()\n"
"{\n"
"gl_Position = vp * vec4(aPos, 0.0, 1.0);\n"
"UV = aUv;\n"
"}\n"
;
const char* worldspaceFrag =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 FragColor;\n"
"in vec2 UV;\n"
"uniform sampler2D ourTexture;\n"
"void main()\n"
"{\n"
"FragColor = texture(ourTexture, UV);\n"
"}\n"
;
//
//const char* tilemapVert =
//"#version 330 core\n"
struct IndexedVertexBuffer
{
GLuint vao;
GLuint vbo;
GLuint ebo;
int capacity;
int eboCapacity;
};
struct VertexBuffer
{
GLuint vao;
GLuint vbo;
int capacity;
};
enum ShaderType
{
ST_Vertex,
ST_Fragment,
ST_Program,
ST_NUM
};
static const char* gShaderTypeNameLUT[ST_NUM] =
{
"Vertex",
"Fragment",
"Program"
};
struct Shader
{
GLuint program;
GLuint frag;
GLuint vert;
};
struct Shader gUIShader = {0,0,0};
struct Shader gWorldspace2DShader = { 0,0,0 };
mat4 gScreenspaceOrtho;
OBJECT_POOL(struct VertexBuffer) gVertexBuffersPool = NULL;
OBJECT_POOL(struct IndexedVertexBuffer) gIndexedVertexBuffersPool = NULL;
static bool OpenGlGPULoadTexture(const unsigned char* data, unsigned int width, unsigned int height, unsigned int* id)
{
glGenTextures(1, id);
glBindTexture(GL_TEXTURE_2D, *id);
// set the texture wrapping/filtering options (on the currently bound texture object)
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_MIPMAP_NEAREST); // GL_NEAREST is the better filtering option for this game
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // as gives better more "pixelated" (less "smoothed out") textures
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
return true;
}
static void TestShaderStatus(GLuint shader, enum ShaderType type)
{
GLint success;
GLchar infoLog[1024];
if (type != ST_Program)
{
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
Log_Error("ERROR::SHADER_COMPILATION_ERROR of type: %s\n%s\n -- --------------------------------------------------- -- \n",
gShaderTypeNameLUT[type],
infoLog);
}
}
else
{
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
Log_Error("ERROR::PROGRAM_LINKING_ERROR of type: %s\n%s\n -- --------------------------------------------------- -- \n",
gShaderTypeNameLUT[type],
infoLog);
}
}
}
static void UIVertexBufferData(HUIVertexBuffer hBuf, WidgetVertex* src, size_t size)
{
struct VertexBuffer* pBuf = &gVertexBuffersPool[hBuf];
glBindBuffer(GL_ARRAY_BUFFER, pBuf->vbo);
if (size * sizeof(WidgetVertex) > pBuf->capacity)
{
glBufferData(GL_ARRAY_BUFFER, size * sizeof(WidgetVertex), src, GL_DYNAMIC_DRAW);
pBuf->capacity = size * sizeof(WidgetVertex);
}
else
{
glBufferSubData(GL_ARRAY_BUFFER, 0, size * sizeof(WidgetVertex), src);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
static void CreateShader(const char* vert, const char* frag, struct Shader* pShader)
{
int success = GL_FALSE;
pShader->vert = glCreateShader(GL_VERTEX_SHADER);
const char* v = vert;
const char* f = frag;
glShaderSource(pShader->vert, 1, (const char**)&v, 0);
glCompileShader(pShader->vert);
TestShaderStatus(pShader->vert, ST_Vertex);
pShader->frag = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(pShader->frag, 1, (const char**)&f, 0);
glCompileShader(pShader->frag);
TestShaderStatus(pShader->frag, ST_Fragment);
pShader->program = glCreateProgram();
glAttachShader(pShader->program, pShader->vert);
glAttachShader(pShader->program, pShader->frag);
glLinkProgram(pShader->program);
TestShaderStatus(pShader->program, ST_Program);
}
static void CreateShaders()
{
CreateShader(uiVert, uiFrag, &gUIShader);
CreateShader(worldspaceVert, worldspaceFrag, &gWorldspace2DShader);
};
static HUIVertexBuffer NewUIVertexBuffer(int size)
{
HUIVertexBuffer buf = -1;
GetObjectPoolIndex(gVertexBuffersPool, &buf);
struct VertexBuffer* pBuf = &gVertexBuffersPool[buf];
pBuf->capacity = 0;
glGenVertexArrays(1, &pBuf->vao);
glBindVertexArray(pBuf->vao);
glGenBuffers(1, &pBuf->vbo);
glBindBuffer(GL_ARRAY_BUFFER, pBuf->vbo);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(WidgetVertex), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(WidgetVertex), (void*)(sizeof(float) * 2));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(WidgetVertex), (void*)(sizeof(float) * 4));
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
return buf;
}
static void DrawUIVertexBuffer(HUIVertexBuffer hBuf, size_t vertexCount)
{
const struct VertexBuffer* vertexBuffer = &gVertexBuffersPool[hBuf];
glUseProgram(gUIShader.program);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer->vbo);
glBindVertexArray(vertexBuffer->vao);
unsigned int projectionViewUniform = glGetUniformLocation(gUIShader.program, "screenToClipMatrix");
glUniformMatrix4fv(projectionViewUniform, 1, false, &gScreenspaceOrtho[0][0]);
glDrawArrays(GL_TRIANGLES, 0, vertexCount);
}
static void DestroyUIVertexBuffer(HUIVertexBuffer hBuf)
{
const struct VertexBuffer* vertexBuffer = &gVertexBuffersPool[hBuf];
glDeleteBuffers(1, &vertexBuffer->vbo);
glDeleteVertexArrays(1, &vertexBuffer->vao);
}
static hTexture UploadTexture(void* src, int channels, int pxWidth, int pxHeight)
{
EASSERT(channels == 4);
hTexture txture = 0;
OpenGlGPULoadTexture(src, pxWidth, pxHeight, &txture);
return txture;
}
static void SetCurrentAtlas(hTexture atlas)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, atlas);
}
static void DestroyTexture(hTexture tex)
{
glDeleteTextures(1, &tex);
}
static HWorldspaceVertexBuffer NewWorldspaceVertexBuffer(int size)
{
HWorldspaceVertexBuffer buf = -1;
GetObjectPoolIndex(gIndexedVertexBuffersPool, &buf);
struct IndexedVertexBuffer* pBuf = &gIndexedVertexBuffersPool[buf];
pBuf->capacity = 0;
pBuf->eboCapacity = 0;
glGenVertexArrays(1, &pBuf->vao);
glBindVertexArray(pBuf->vao);
glGenBuffers(1, &pBuf->ebo);
glGenBuffers(1, &pBuf->vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pBuf->ebo);
glBindBuffer(GL_ARRAY_BUFFER, pBuf->vbo);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Worldspace2DVert), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Worldspace2DVert), (void*)(sizeof(float) * 2));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
return buf;
}
void WorldspaceVertexBufferData(HUIVertexBuffer hBuf, Worldspace2DVert* src, size_t size, VertIndexT* indices, u32 numIndices)
{
struct IndexedVertexBuffer* pBuf = &gIndexedVertexBuffersPool[hBuf];
glBindBuffer(GL_ARRAY_BUFFER, pBuf->vbo);
if (size * sizeof(Worldspace2DVert) > pBuf->capacity)
{
glBufferData(GL_ARRAY_BUFFER, size * sizeof(Worldspace2DVert), src, GL_DYNAMIC_DRAW);
pBuf->capacity = size;
}
else
{
glBufferSubData(GL_ARRAY_BUFFER, 0, size * sizeof(Worldspace2DVert), src);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pBuf->ebo);
if (numIndices * sizeof(VertIndexT) > pBuf->eboCapacity)
{
glBufferData(GL_ELEMENT_ARRAY_BUFFER, numIndices * sizeof(unsigned int), &indices[0], GL_DYNAMIC_DRAW);
pBuf->eboCapacity = numIndices * sizeof(VertIndexT);
}
else
{
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, size * sizeof(VertIndexT), indices);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void DrawWorldspaceVertexBuffer(H2DWorldspaceVertexBuffer hBuf, size_t indexCount, mat4 view)
{
const struct IndexedVertexBuffer* vertexBuffer = &gIndexedVertexBuffersPool[hBuf];
glUseProgram(gWorldspace2DShader.program);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer->vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexBuffer->ebo);
glBindVertexArray(vertexBuffer->vao);
unsigned int projectionViewUniform = glGetUniformLocation(gWorldspace2DShader.program, "vp");
mat4 m;
glm_mat4_mul(&gScreenspaceOrtho[0], &view[0], &m[0]);
glUniformMatrix4fv(projectionViewUniform, 1, false, &m[0][0]);
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, (void*)0);
}
void DestroyWorldspaceVertexBuffer(H2DWorldspaceVertexBuffer hBuf)
{
const struct IndexedVertexBuffer* vertexBuffer = &gIndexedVertexBuffersPool[hBuf];
glDeleteBuffers(1, &vertexBuffer->vbo);
glDeleteBuffers(1, &vertexBuffer->ebo);
glDeleteVertexArrays(1, &vertexBuffer->vao);
}
DrawContext Dr_InitDrawContext()
{
DrawContext d;
memset(&d, 0, sizeof(DrawContext));
d.DestroyVertexBuffer = &DestroyUIVertexBuffer;
d.DrawUIVertexBuffer = &DrawUIVertexBuffer;
d.NewUIVertexBuffer = &NewUIVertexBuffer;
d.UIVertexBufferData = &UIVertexBufferData;
d.SetCurrentAtlas = &SetCurrentAtlas;
d.UploadTexture = &UploadTexture;
d.NewWorldspaceVertBuffer = &NewWorldspaceVertexBuffer;
d.WorldspaceVertexBufferData = &WorldspaceVertexBufferData;
d.DrawWorldspaceVertexBuffer = &DrawWorldspaceVertexBuffer;
d.DestroyWorldspaceVertexBuffer = &DestroyWorldspaceVertexBuffer;
gVertexBuffersPool = NEW_OBJECT_POOL(struct VertexBuffer, 256);
gIndexedVertexBuffersPool = NEW_OBJECT_POOL(struct IndexedVertexBuffer, 256);
glm_mat4_identity(gScreenspaceOrtho);
CreateShaders();
return d;
}
void Dr_OnScreenDimsChange(DrawContext* pCtx, int newW, int newH)
{
pCtx->screenWidth = newW;
pCtx->screenHeight = newH;
glm_ortho(0.0f, newW, newH, 0.0f, -1.0f, 1.0f, gScreenspaceOrtho);
}