You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
455 lines
11 KiB
455 lines
11 KiB
|
|
#if defined (_WIN32) |
|
#include <SDL.h> |
|
#else |
|
#include <SDL2/SDL.h> |
|
#endif |
|
#include <glm/glm.hpp> |
|
#include <glm/geometric.hpp> |
|
#include <glm/gtc/matrix_transform.hpp> |
|
|
|
#include "default_shaders.cpp" |
|
#include "dumbLog.h" |
|
#include "input.h" |
|
#include "render_object.h" |
|
#include "renderer.h" |
|
|
|
#define CLEAR_COL_R 55.f / 255.f |
|
#define CLEAR_COL_G 55.f / 255.f |
|
#define CLEAR_COL_B 55.f / 255.f |
|
#define CLEAR_COL_A 1.f |
|
#define USE_SECOND_MONITOR 0 |
|
|
|
|
|
// forward declarations |
|
|
|
bool createWindow(const char* title, |
|
SDL_Handles* handles, |
|
glm::vec2& viewport_dims); |
|
bool initContext(SDL_Handles* handles); |
|
bool initGlOptions(); |
|
bool initSDL(SDL_Handles* handles, Uint32 SDL_init_flags); |
|
bool initShaders(render_state* rs); |
|
void openglDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, |
|
GLsizei length, const GLchar* message, const void* userParam); |
|
void setDefaults(render_state* rs, glm::vec2 viewport_dims); |
|
|
|
|
|
// interface |
|
|
|
// WIP |
|
size_t |
|
getBlockFreeSpace(asset_memory_block* block) |
|
{ |
|
return (uint8_t*) block |
|
+ block->max_size |
|
- (uint8_t*) block->tail |
|
+ block->tail->total_size; |
|
} |
|
|
|
uint |
|
getAssetSize(const mesh_group& mg, uint name_len) |
|
{ |
|
uint total = sizeof(render_asset); |
|
total += name_len; |
|
|
|
for (uint i = 0; i < mg.num_meshes; i++) { |
|
mesh_info* mi = mg.meshes[i]; |
|
total += sizeof(mesh_asset); |
|
// NOTE: vertices, normals, and tex coords |
|
total += 3 * mi->num_vertices * sizeof(glm::vec3); |
|
total += mi->num_indices * sizeof(uint); |
|
} |
|
|
|
return total; |
|
} |
|
|
|
void* |
|
getAddressOffset(void* address, uint offset) |
|
{ |
|
return (void*) ((uint8_t*) address + offset); |
|
} |
|
|
|
// TODO: replace mesh_info structure |
|
mesh_asset* |
|
initMeshAsset(mesh_asset* mesh, mesh_info* mi) |
|
{ |
|
mesh->num_vertices = mi->num_vertices; |
|
mesh->num_indices = mi->num_indices; |
|
mesh->asset_size = sizeof(mesh_asset); |
|
mesh->vertices = (glm::vec3*) ((uint8_t*) mesh + mesh->asset_size); |
|
// NOTE: vertices, normals, and tex coords |
|
mesh->asset_size += 3 * mesh->num_vertices * sizeof(glm::vec3); |
|
mesh->asset_size += mesh->num_indices * sizeof(uint); |
|
mesh->asset_size += sizeof(glm::mat4); |
|
// FIXME: this should be set in the outer loop |
|
//mesh->next = (mesh_asset*) ((uint8_t*) mesh + mesh->asset_size); |
|
// FIXME: copy vertices, normals, tex coords, and xform |
|
|
|
return mesh; |
|
} |
|
|
|
render_asset* |
|
getNextFreeRenderAsset(asset_memory_block* block) |
|
{ |
|
return (render_asset*) |
|
getAddressOffset(block->tail, block->tail->total_size); |
|
} |
|
|
|
#include <cstring> |
|
render_asset* |
|
initRenderAsset( |
|
render_asset* ra, |
|
uint total_size, |
|
uint num_meshes, |
|
const char* filepath) |
|
{ |
|
ra->total_size = total_size; |
|
ra->num_meshes = num_meshes; |
|
ra->name_len = (uint) std::strlen(filepath) + 1; |
|
ra->next = nullptr; |
|
ra->filepath = (char*) ra + sizeof(render_asset); |
|
std::memcpy(ra->filepath, filepath, ra->name_len); |
|
ra->head_mesh = (mesh_asset*) ((uint8_t*) ra->filepath + ra->name_len); |
|
// TODO: texture |
|
//ra->diffuse_texture = (util_image*) ra->filepath; |
|
|
|
return ra; |
|
} |
|
|
|
asset_memory_block* |
|
memInitBlock(size_t initial_size) |
|
{ |
|
// NOTE: need to alloc as uint8_t and recast here so we can get the correct |
|
// size in bytes |
|
asset_memory_block* block = (asset_memory_block*) UTIL_ALLOC( |
|
sizeof(asset_memory_block) + initial_size, uint8_t); |
|
block->max_size = initial_size - sizeof(asset_memory_block); |
|
block->head = block->tail = |
|
(render_asset*) ((uint8_t*) block + sizeof(asset_memory_block)); |
|
return block; |
|
} |
|
|
|
void |
|
memFreeBlock(asset_memory_block* block) |
|
{ |
|
utilSafeFree(block); |
|
} |
|
|
|
render_asset* |
|
memPushAsset(asset_memory_block* block, const char* filepath) |
|
{ |
|
// TODO: rewrite mesh.cpp to load assimp structure into render_asset |
|
// directly |
|
mesh_group mg; |
|
meLoadFromFile(mg, filepath); |
|
uint name_len = (uint) std::strlen(filepath) + 1; |
|
assert(name_len <= MAX_PATH_SIZE); |
|
uint asize = getAssetSize(mg, name_len); |
|
|
|
if (getBlockFreeSpace(block) < asize) { |
|
LOG(Error) << "not enough free space in block\n"; |
|
return nullptr; |
|
} |
|
|
|
render_asset* ra = getNextFreeRenderAsset(block); |
|
initRenderAsset(ra, asize, mg.num_meshes, filepath); |
|
block->max_size -= asize; |
|
block->asset_count++; |
|
|
|
// NOTE: special case when adding the first asset |
|
if (block->head == block->tail) |
|
block->head->next = ra; |
|
else |
|
block->tail->next = ra; |
|
|
|
block->tail = ra; |
|
mesh_asset* next = ra->head_mesh; |
|
|
|
for (uint i = 0; i < mg.num_meshes; i++) { |
|
mesh_asset* mesh = initMeshAsset(next, mg.meshes[i]); |
|
next = (mesh_asset*) getAddressOffset(mesh, mesh->asset_size); |
|
// NOTE: leave the last link as nullptr |
|
(i < mg.num_meshes - 1) ? mesh->next = next : mesh->next = nullptr; |
|
} |
|
|
|
return ra; |
|
} |
|
// end WIP |
|
|
|
render_state* |
|
renInit(const char* title, glm::vec2 viewport_dims, Uint32 SDL_init_flags) |
|
{ |
|
render_state* rs = UTIL_ALLOC(1, render_state); |
|
rs->handles = UTIL_ALLOC(1, SDL_Handles); |
|
setDefaults(rs, viewport_dims); |
|
|
|
if (initSDL(rs->handles, SDL_init_flags) && |
|
createWindow(title, rs->handles, rs->viewport_dims) && |
|
initContext(rs->handles) && |
|
initGlOptions() && |
|
initShaders(rs) && |
|
meInitAssimp()) |
|
{ |
|
return rs; |
|
} |
|
|
|
LOG(Error) << "renderer initialization failed, aborting\n"; |
|
return nullptr; |
|
} |
|
|
|
void |
|
renShutdown(render_state* rs) |
|
{ |
|
for (uint i = 0; i < rs->render_group_count; i++) { |
|
for (uint j = 0; j < rs->render_groups[i]->entity_count; j++) |
|
entFree(rs->render_groups[i]->entities[j]); |
|
|
|
utilSafeFree(rs->render_groups[i]->entities); |
|
} |
|
|
|
shaderFree(rs->default_shader->program_id); |
|
utilSafeFree(rs->default_shader); |
|
rs->default_shader = nullptr; |
|
shaderFree(rs->simple_shader->program_id); |
|
utilSafeFree(rs->simple_shader); |
|
rs->simple_shader = nullptr; |
|
|
|
lightsOut(rs->lights); |
|
utilSafeFree(rs->render_groups); |
|
memFreeBlock(rs->assets); |
|
rs->assets = nullptr; |
|
meShutdownAssimp(); |
|
SDL_GL_DeleteContext(rs->handles->glContext); |
|
SDL_DestroyWindow(rs->handles->window); |
|
SDL_Quit(); |
|
utilSafeFree(rs->handles); |
|
} |
|
|
|
render_group* |
|
renAllocateGroup(uint entity_count, shader_wrapper shader) |
|
{ |
|
assert(entity_count > 0); |
|
render_group* rg = UTIL_ALLOC(1, render_group); |
|
rg->entities = UTIL_ALLOC(entity_count, entity); |
|
rg->entity_count = entity_count; |
|
rg->shader = shader; |
|
return rg; |
|
} |
|
|
|
void |
|
renDoRenderLoop(render_state* rs, |
|
uint framerate, |
|
frame_callback_fn cb_func_pre, |
|
frame_callback_fn cb_func_post) |
|
{ |
|
uint delay = (framerate > 0) ? 1 / framerate : 0; |
|
uint frameStart, frameTime; |
|
static input_state is = {}; |
|
|
|
while (rs->running) { |
|
frameStart = SDL_GetTicks(); |
|
|
|
if (cb_func_pre != nullptr) { |
|
cb_func_pre(rs); |
|
} else { |
|
inputProcessEvents(&is); |
|
|
|
if (is.window_closed || is.escape) { |
|
rs->running = false; |
|
return; |
|
} |
|
} |
|
|
|
renRenderFrame(rs); |
|
if (cb_func_post != nullptr) cb_func_post(rs); |
|
|
|
SDL_GL_SwapWindow(rs->handles->window); |
|
frameTime = SDL_GetTicks() - frameStart; |
|
|
|
if (delay > frameTime) |
|
SDL_Delay(delay - frameTime); |
|
} |
|
} |
|
|
|
void |
|
renRenderFrame(render_state* rs) |
|
{ |
|
glClearColor(rs->clear_col.R, |
|
rs->clear_col.G, |
|
rs->clear_col.B, |
|
rs->clear_col.A); |
|
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); |
|
|
|
for (uint i = 0; i < rs->render_group_count; i++) { |
|
render_group* rg = rs->render_groups[i]; |
|
|
|
for (uint j = 0; j < rg->entity_count; j++) { |
|
entity& e = rg->entities[j]; |
|
roDraw(e.render_objs, |
|
e.world_transform, |
|
rs->cam, |
|
rg->shader, |
|
rs->lights); |
|
} |
|
} |
|
} |
|
|
|
bool |
|
renAddLight(render_state* rs, glm::vec3 pos, glm::vec3 color, float intensity) |
|
{ |
|
if (!lightsAdd(rs->lights, pos, color, intensity)) { |
|
LOG(Error) << "Error adding light\n"; |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
glm::vec2 |
|
renGetWindowDims(render_state* rs) |
|
{ |
|
int x = 0, y = 0; |
|
SDL_GetWindowSize(rs->handles->window, &x, &y); |
|
glm::vec2 dims(x, y); |
|
return dims; |
|
} |
|
|
|
|
|
// internal |
|
|
|
bool |
|
createWindow(const char* title, SDL_Handles* handles, glm::vec2& viewport_dims) |
|
{ |
|
uint display_id = 0; |
|
if (USE_SECOND_MONITOR && SDL_GetNumVideoDisplays() > 1) |
|
display_id = 1; |
|
|
|
handles->window = SDL_CreateWindow( |
|
title, |
|
SDL_WINDOWPOS_CENTERED_DISPLAY(display_id), |
|
SDL_WINDOWPOS_CENTERED_DISPLAY(display_id), |
|
viewport_dims.x, |
|
viewport_dims.y, |
|
SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE |
|
); |
|
|
|
if (!handles->window) { |
|
LOG(Error) << "Error creating window: " << SDL_GetError() << "\n"; |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool |
|
initContext(SDL_Handles* handles) |
|
{ |
|
handles->glContext = SDL_GL_CreateContext(handles->window); |
|
|
|
if (!handles->glContext) { |
|
LOG(Error) << "Error creating glContext: " << SDL_GetError() << "\n"; |
|
return false; |
|
} |
|
|
|
// TODO: this doesn't work inside VM with QXL graphics |
|
if (SDL_GL_SetSwapInterval(1) != 0) // vsync |
|
LOG(Error) << "SDL Errors: " << SDL_GetError() << "\n"; |
|
|
|
return true; |
|
} |
|
|
|
bool |
|
initGlOptions() |
|
{ |
|
if (glewInit()) { |
|
LOG(Error) << "failed to initialize OpenGL\n"; |
|
return false; |
|
} |
|
|
|
LOG(Info) << "opengl vendor: " << glGetString(GL_VENDOR) << "\n"; |
|
LOG(Info)<< "opengl renderer: " << glGetString(GL_RENDERER) << "\n"; |
|
LOG(Info) << "opengl version: " << glGetString(GL_VERSION) << "\n"; |
|
|
|
glEnable(GL_DEPTH_TEST); |
|
glEnable(GL_LINE_SMOOTH); |
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
|
#if 0 |
|
// TODO: blending messes up rendering with mesa on intel graphics 4000 |
|
glEnable(GL_BLEND); |
|
glBlendEquation(GL_FUNC_ADD); |
|
glBlendFunc(GL_ONE, GL_SRC_ALPHA); |
|
#endif |
|
|
|
// TODO: glDebugMessageCallback is only availabe from >v4.3 |
|
// check and warn if context doesn't support this function here |
|
glEnable (GL_DEBUG_OUTPUT); |
|
glDebugMessageCallback((GLDEBUGPROC) openglDebugCallback, 0); |
|
// hide VRAM debug messages |
|
glDebugMessageControl(GL_DONT_CARE, 33361, GL_DONT_CARE, 0, 0, GL_FALSE); |
|
|
|
return true; |
|
} |
|
|
|
bool |
|
initSDL(SDL_Handles* handles, Uint32 SDL_init_flags) |
|
{ |
|
Uint32 flags = SDL_INIT_VIDEO | SDL_init_flags; |
|
|
|
if (SDL_Init(flags) != 0) { |
|
LOG(Error) << "Error, SDL_Init: " << SDL_GetError() << "\n"; |
|
return false; |
|
} |
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); |
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); |
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); |
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); |
|
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); |
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); |
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); |
|
SDL_GetCurrentDisplayMode(0, &handles->currentDisplayMode); |
|
|
|
return true; |
|
} |
|
|
|
bool |
|
initShaders(render_state* rs) |
|
{ |
|
rs->default_shader = |
|
shaderInitDefault(DEFAULT_VERTEX_SHADER, DEFAULT_FRAGMENT_SHADER); |
|
rs->simple_shader = |
|
shaderInitSimple(SIMPLE_VERTEX_SHADER, SIMPLE_FRAGMENT_SHADER); |
|
|
|
if (rs->default_shader == nullptr || |
|
rs->simple_shader == nullptr) |
|
{ |
|
LOG(Error) << "shader loading failed\n"; |
|
return false; |
|
} else { |
|
return true; |
|
} |
|
} |
|
|
|
void |
|
openglDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, |
|
GLsizei length, const GLchar* message, const void* userParam) |
|
{ |
|
LOG((type == GL_DEBUG_TYPE_ERROR) ? Error : Debug) |
|
<< (type == GL_DEBUG_TYPE_ERROR ? "** GL Error **" : "") |
|
<< ", type: " << type |
|
<< ", severity: " << severity |
|
<< ", message: " << message << "\n"; |
|
} |
|
|
|
void |
|
setDefaults(render_state* rs, glm::vec2 viewport_dims) |
|
{ |
|
rs->running = true; |
|
rs->viewport_dims = viewport_dims; |
|
rs->lights = lightsInit(); |
|
rs->clear_col.R = CLEAR_COL_R; |
|
rs->clear_col.B = CLEAR_COL_B; |
|
rs->clear_col.G = CLEAR_COL_G; |
|
rs->clear_col.A = CLEAR_COL_A; |
|
}
|
|
|