diff options
Diffstat (limited to 'src/render/gl.cpp')
| -rw-r--r-- | src/render/gl.cpp | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/src/render/gl.cpp b/src/render/gl.cpp new file mode 100644 index 0000000..915722e --- /dev/null +++ b/src/render/gl.cpp @@ -0,0 +1,394 @@ +#include "SDL_video.h" +#include <unistd.h> +#define STB_IMAGE_IMPLEMENTATION +#include "../util/stb_image.h" + +#include "gl.h" +#include "gl_2d_font.h" +#include "../util.h" + +I32 SAMPLER_INDICES[255]; + +GL_DATA* gl_inst; + +GL_DATA* gl_instance() { + return gl_inst; +} + +GL_DATA* gl_create( I32* _canvas ) { + if( gl_inst ) { + dlog( "gl_create() : fatal: gl instance already exists\n" ); + abort(); + } + + if( !font_mutex.align ) + thread_mutex_init( &font_mutex ); + + if( !!SDL_Init( SDL_INIT_VIDEO | SDL_VIDEO_OPENGL ) ) { + dlog( "gl_create() could not init SDL: %s\n", SDL_GetError() ); + return 0; + } + + SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES ); + SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 ); + SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 2 ); + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 ); + SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 ); + SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 ); + SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 ); + SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 ); + SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 ); + + GL_DATA* gl = new GL_DATA; + gl->window = SDL_CreateWindow( + "game", + 0, + 0, + _canvas[0], + _canvas[1], + SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN + ); + + if( !gl->window ) { + dlog( "gl_init() could not create window: %s\n", SDL_GetError() ); + return 0; + } + + U32 renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE; + gl->renderer = SDL_CreateRenderer( gl->window, -1, renderer_flags ); + if( !gl->renderer ) { + dlog( "gl_init() could not create renderer: %s\n", SDL_GetError() ); + return 0; + } + + gl->ctx = SDL_GL_CreateContext( gl->window ); + if( !gl->ctx ) { + dlog( "gl_init() could not create context: %s\n", SDL_GetError() ); + return 0; + } + + glGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS, &gl->shader_texture_limit ); + if( gl->shader_texture_limit > 255 ) + gl->shader_texture_limit = 255; + for( U32 i = 0; i < 255; ++i ) + SAMPLER_INDICES[i] = i; + + gl->programs.clear(); + memcpy( gl->canvas_size, _canvas, sizeof(I32) * 2 ); + gl->clip_start = { 0, 0 }; + gl->clip_dim = { (F32)gl->canvas_size[0], (F32)gl->canvas_size[1] }; + + gl_inst = gl; + return gl; +} + +void gl_gen_buffers( GL_DATA* gl ) { + glGenBuffers( 1, &gl->vbuffer ); + + glBindBuffer( GL_ARRAY_BUFFER, gl->vbuffer ); + glBufferData( GL_ARRAY_BUFFER, 8192, 0, GL_STATIC_DRAW ); + + glBindBuffer( GL_ARRAY_BUFFER, 0 ); +} + +void gl_destroy( GL_DATA *gl ) { + gl->programs.each( fn( GL_SHADER_PROGRAM** it ) { gl_program_destroy( gl, *it ); } ); + gl->fonts.each( fn( GL_FONT** it ) { gl_font_destroy( gl, *it ); } ); + gl->textures.each( fn( GL_TEX2D** it ) { gl_texture_destroy( gl, *it ); } ); + + if( gl->ctx ) + SDL_GL_DeleteContext( gl->ctx ); + if( gl->window ) + SDL_DestroyWindow( gl->window ); + + free( gl ); +} + +void gl_update_window( GL_DATA* gl, I32* size ) { + if( !gl->window ) + return; + SDL_SetWindowSize( gl->window, size[0], size[1] ); + SDL_SetWindowPosition( gl->window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED ); + + gl->canvas_size[0] = size[0]; + gl->canvas_size[1] = size[1]; + + gl->programs.each( fn( GL_SHADER_PROGRAM** it ) { + F32 screen_ratio[] = { + 2.f / (F32)gl->canvas_size[0], + 2.f / (F32)gl->canvas_size[1], + 1.f, + 1.f + }; + + glUseProgram( (*it)->id ); + I32 ratio_location = glGetUniformLocation( (*it)->id, "g_screenratio" ); + glUniform4fv( ratio_location, 1, &screen_ratio[0] ); + } ); + + glViewport( 0, 0, gl->canvas_size[0], gl->canvas_size[1] ); + glUseProgram( 0 ); +} + +STAT gl_shader_compile( GL_DATA* gl, GL_SHADER* shader ) { + static char* log_buf = 0; + if( !log_buf ) + log_buf = (char*)malloc( 8192 ); + + I32 res; + shader->id = glCreateShader( (GLenum)shader->type ); + glShaderSource( shader->id, 1, &shader->code, 0 ); + glCompileShader( shader->id ); + glGetShaderiv( shader->id, GL_COMPILE_STATUS, &res ); + + if( !res ) { + glGetShaderInfoLog( shader->id, 8192, 0, log_buf ); + dlog( "gl_shader_compile() : error compiling shader %s. log: \n%s\n%s", shader->name, log_buf, shader->code ); + + glDeleteShader( shader->id ); + return STAT_ERR; + } + + shader->compiled = 1; + return STAT_OK; +} + +void gl_shader_destroy( GL_DATA* gl, GL_SHADER* shader ) { + if( shader->code ) + free( (void*)shader->code ); +} + +GL_SHADER_PROGRAM* gl_program_create( GL_DATA* gl, const char* name, U32 size ) { + GL_SHADER_PROGRAM* program = (GL_SHADER_PROGRAM*)malloc( size ); + char shader_string[256]; + char* shader_code; + + sprintf( shader_string, "../assets/shaders/%s.vsh", name ); + shader_code = (char*)file_read( shader_string ); + if( !shader_code ) { + dlog( "gl_program_create() : could not read shader file %s\n", shader_string ); + return 0; + } + + program->vsh.name = name; + program->vsh.type = GL_VERTEX_SHADER; + program->vsh.code = shader_code; + + sprintf( shader_string, "../assets/shaders/%s.fsh", name ); + shader_code = (char*)file_read( shader_string ); + if( !shader_code ) { + dlog( "gl_program_create() : could not read shader file %s\n", shader_string ); + return 0; + } + + program->fsh.name = name; + program->fsh.type = GL_FRAGMENT_SHADER; + program->fsh.code = shader_code; + + program->name = name; + program->gl = gl; + + gl->programs.push( program ); + return program; +} + +STAT gl_program_compile( GL_DATA* gl, GL_SHADER_PROGRAM* program ) { + static char* log_buf = 0; + if( !log_buf ) + log_buf = (char*)malloc( 8192 ); + + program->id = glCreateProgram(); + if( !OK( gl_shader_compile( gl, &program->fsh ) ) + || !OK( gl_shader_compile( gl, &program->vsh ) ) ) + return STAT_ERR; + + glAttachShader( program->id, program->fsh.id ); + glAttachShader( program->id, program->vsh.id ); + + I32 status; + glLinkProgram( program->id ); + glGetProgramiv( program->id, GL_LINK_STATUS, &status ); + if( !status ) { + glGetProgramInfoLog( program->id, 8192, 0, log_buf ); + dlog( "gl_program_compile() : error compiling program %s. log: \n%s\n", + program->name, + log_buf + ); + + return STAT_ERR; + } + + glGenBuffers( 1, &program->vbuffer ); + return STAT_OK; +} + +void gl_program_destroy( GL_DATA* gl, GL_SHADER_PROGRAM* program ) { + if( program->fsh.compiled ) + gl_shader_destroy( gl, &program->fsh ); + if( program->vsh.compiled ) + gl_shader_destroy( gl, &program->vsh ); + + I32 idx = gl->programs.idx_of( program ); + if( idx != -1 ) + gl->programs.erase( idx ); +} + +GL_TEX2D* gl_texture_from_file( GL_DATA* gl, const char* name ) { + char filename[256]; + sprintf( filename, "../assets/%s", name ); + + I32 w, h, n; + U8* data = stbi_load( filename, &w, &h, &n, STBI_rgb_alpha ); + if( !data ) { + dlog( "gl_texture_create() : could not load image %s\n", filename ); + return 0; + } + + GL_TEX2D* tex = gl_texture_create( gl, filename ); + glBindTexture( GL_TEXTURE_2D, tex->id ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); + glGenerateMipmap( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, 0 ); + + tex->width = w; + tex->height = h; + + stbi_image_free( data ); + + return tex; +} + +GL_TEX2D* gl_texture_from_bitmap( GL_DATA* gl, const char* name, U8* bitmap, U32 width, U32 height ) { + GL_TEX2D* tex = gl_texture_create( gl, name ); + glBindTexture( GL_TEXTURE_2D, tex->id ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + tex->width = width; + tex->height = height; + + glTexImage2D( + GL_TEXTURE_2D, + 0, GL_RGBA, + (I32)width, (I32)height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, bitmap + ); + + glBindTexture( GL_TEXTURE_2D, 0 ); + + return tex; +} + + +GL_TEX2D* gl_texture_create( GL_DATA* gl, const char* name ) { + GL_TEX2D* tex = (GL_TEX2D*)malloc( sizeof(GL_TEX2D) ); + strcpy( tex->name, name ); + glGenTextures( 1, &tex->id ); + + gl->textures.push( tex ); + return tex; +} + +void gl_texture_destroy( GL_DATA* gl, GL_TEX2D* tex ) { + glDeleteTextures( 1, &tex->id ); + I32 idx = gl->textures.idx_of( tex ); + if( idx != -1 ) + gl->textures.erase( idx ); + + free( tex ); +} + +STAT gl_beginframe( GL_DATA* gl ) { + SDL_SetRenderTarget( gl->renderer, 0 ); + SDL_SetRenderDrawColor( gl->renderer, 0, 0, 0, 255 ); + SDL_RenderClear( gl->renderer ); + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + gl->last_tick = u_tick(); + return STAT_OK; +} + +STAT gl_endframe( GL_DATA* gl ) { + SDL_GL_SwapWindow( gl->window ); + + // 1000 fps max be real + while( true ) { + U64 diff = u_tick() - gl->last_tick; + if( diff < 10 ) + usleep( 10 ); + else break; + } + + gl->frametime = (F32)(u_tick() - gl->last_tick) / 10000.0f; + gl->fps = 1.0f / gl->frametime; + + input_frame_end(); + + SDL_Event e; + while( SDL_PollEvent( &e ) ) { + input_on_event( &e ); + + switch( e.type ) { + case SDL_QUIT: + return STAT_BREAK; + case SDL_KEYDOWN: + return e.key.keysym.sym == SDLK_ESCAPE ? STAT_BREAK : STAT_OK; + default: break; + } + } + + return STAT_OK; +} + +void gl_set_clip( GL_DATA* gl, VEC2 start, VEC2 dim ) { + gl->clip_start = start; + gl->clip_dim = dim; + + glEnable( GL_SCISSOR_TEST ); + glScissor( start.x, gl->canvas_size[1] - (I32)(dim.y + start.y), dim.x, dim.y ); +} + +void gl_get_clip( GL_DATA* gl, VEC2* start, VEC2* dim ) { + *start = gl->clip_start; + *dim = gl->clip_dim; +} + +void gl_reset_clip( GL_DATA* gl ) { + glDisable( GL_SCISSOR_TEST ); + gl->clip_start = {}; + gl->clip_dim = { (F32)gl->canvas_size[0], (F32)gl->canvas_size[1] }; +} + +void gl_set_viewport( GL_DATA* gl, VEC2 start, VEC2 dim ) { + I32 vpy = (I32)gl->canvas_size[1] - start.y - dim.y; + glViewport( (I32)start.x, vpy, (I32)dim.x, (I32)dim.y ); + + gl->programs.each( fn( GL_SHADER_PROGRAM** it ) { + F32 screen_ratio[] = { + 2.f / (F32)dim.x, + 2.f / (F32)dim.y, + 1.f, + 1.f + }; + + glUseProgram( (*it)->id ); + I32 ratio_location = glGetUniformLocation( (*it)->id, "g_screenratio" ); + glUniform4fv( ratio_location, 1, &screen_ratio[0] ); + } ); + + gl->viewport_start = start; + gl->viewport_dim = dim; +} + +void gl_get_viewport( GL_DATA* gl, VEC2* start, VEC2* dim ) { + *start = gl->viewport_start; + *dim = gl->viewport_dim; +} |
