summaryrefslogtreecommitdiff
path: root/src/render/gl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/render/gl.cpp')
-rw-r--r--src/render/gl.cpp394
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;
+}