diff --git a/runtime/lib/gl.rb b/runtime/lib/gl.rb new file mode 100644 index 0000000..daffd32 --- /dev/null +++ b/runtime/lib/gl.rb @@ -0,0 +1,34 @@ +module GL + @programs = {} + + class << self + + def load_shaders + program_sources = { + text: ["text.v.glsl", "text.f.glsl"], + basic: ["basic.v.glsl", "basic.f.glsl"], + rect: ["rect.v.glsl", "basic.f.glsl"], + } + + program_sources.each do |program, (v_src_fname, f_src_fname)| + v_src = Runtime.read(:shader, v_src_fname) + v_shader = GLShader.new(:vertex, v_src) + v_shader or raise "Could not find shader #{v_src_fname}" + + f_src = Runtime.read(:shader, f_src_fname) + f_shader = GLShader.new(:fragment, f_src) + f_shader or raise "Could not find shader #{f_src_fname}" + + uniforms = (v_src + f_src).lines.map do |line| + if line =~ /\buniform\s.*\s(\S+);/ + $1 + end + end.compact + + @programs[program] = GLProgram.new(v_shader, f_shader, + uniforms: uniforms) + end + end + + end +end diff --git a/runtime/lib/window.rb b/runtime/lib/window.rb index 651d427..1201e20 100644 --- a/runtime/lib/window.rb +++ b/runtime/lib/window.rb @@ -1,6 +1,7 @@ require "set" class Window + include Widget @windows = Set.new @@ -16,42 +17,12 @@ class Window end def initialize - super - @programs = {} - load_shaders Window.register(self) + super end def close Window.remove(self) end - private - - def load_shaders - program_sources = { - text: ["text.v.glsl", "text.f.glsl"], - basic: ["basic.v.glsl", "basic.f.glsl"], - rect: ["rect.v.glsl", "basic.f.glsl"], - } - - program_sources.each do |program, (v_src_fname, f_src_fname)| - v_src = Runtime.read(:shader, v_src_fname) - v_shader = GLShader.new(:vertex, v_src) - v_shader or raise "Could not find shader #{v_src_fname}" - - f_src = Runtime.read(:shader, f_src_fname) - f_shader = GLShader.new(:fragment, f_src) - f_shader or raise "Could not find shader #{f_src_fname}" - - uniforms = (v_src + f_src).lines.map do |line| - if line =~ /\buniform\s.*\s(\S+);/ - $1 - end - end.compact - - @programs[program] = GLProgram.new(v_shader, f_shader, - uniforms: uniforms) - end - end end diff --git a/runtime/main.rb b/runtime/main.rb index e74529d..b1b7ec6 100644 --- a/runtime/main.rb +++ b/runtime/main.rb @@ -3,6 +3,7 @@ def init_loadpath end def load_lib_files + require "gl" require "gl_program" require "runtime" require "widget" diff --git a/src/GL.cc b/src/GL.cc new file mode 100644 index 0000000..391d6e2 --- /dev/null +++ b/src/GL.cc @@ -0,0 +1,101 @@ +#include "GL.h" +#include "gl3w.h" +#include "ruby.h" +#include "GLBuffer.h" +#include "GLProgram.h" + +enum +{ + PROGRAM_TEXT, + PROGRAM_BASIC, + PROGRAM_RECT, + PROGRAM_COUNT +}; + +static const char * program_names[PROGRAM_COUNT] = { + "text", + "basic", + "rect", +}; + +static VALUE ruby_module; +static GLBufferRef rect_buffer; +static GLProgram * programs[PROGRAM_COUNT]; + +void GL_Init() +{ + ruby_module = rb_define_module("GL"); +} + +static void load_gl_buffers() +{ + static const GLint rect_coords[4][2] = { + {0, 0}, + {1, 0}, + {0, 1}, + {1, 1}, + }; + + rect_buffer = new GLBuffer(); + if (!rect_buffer->create(GL_ARRAY_BUFFER, GL_STATIC_DRAW, + &rect_coords, sizeof(rect_coords))) + { + rb_raise(rb_eRuntimeError, "Failed to create rectangle VBO"); + } +} + +void GL_Load() +{ + static bool loaded = false; + + if (loaded) + return; + + if (gl3wInit() != 0) + { + rb_raise(rb_eRuntimeError, "Failed to gl3wInit()"); + } + + if (!gl3wIsSupported(3, 0)) + { + rb_raise(rb_eRuntimeError, "OpenGL 3.0 is not supported"); + } + + load_gl_buffers(); + + rb_funcall(ruby_module, rb_intern("load_shaders"), 0); + + VALUE programs_hash = rb_iv_get(ruby_module, "@programs"); + for (int i = 0; i < PROGRAM_COUNT; i++) + { + VALUE program = rb_funcall(programs_hash, + rb_intern("[]"), + 1, + ID2SYM(rb_intern(program_names[i]))); + if (RTEST(program)) + { + programs[i] = GLProgram_FromRuby(program); + } + else + { + rb_raise(rb_eLoadError, + "Error resolving %s program", + program_names[i]); + } + } + + glEnable(GL_SCISSOR_TEST); + + loaded = true; +} + +void GL_Resize(int width, int height) +{ + GLint viewport_size[] = {width, height}; + glViewport(0, 0, width, height); + for (int i = 0; i < PROGRAM_COUNT; i++) + { + glUseProgram(programs[i]->id); + glUniform2iv(programs[i]->uniforms[UNIFORM_VIEWPORT_SIZE], 1, &viewport_size[0]); + } +} diff --git a/src/GL.h b/src/GL.h new file mode 100644 index 0000000..ff5ad40 --- /dev/null +++ b/src/GL.h @@ -0,0 +1,8 @@ +#ifndef GL_H +#define GL_H + +void GL_Init(); +void GL_Load(); +void GL_Resize(int width, int height); + +#endif diff --git a/src/Window.cc b/src/Window.cc index 7002ccc..cf8dc4a 100644 --- a/src/Window.cc +++ b/src/Window.cc @@ -1,4 +1,5 @@ #include "gl3w.h" +#include "GL.h" #include "GLBuffer.h" #include "GLProgram.h" #include "ruby.h" @@ -8,25 +9,9 @@ #define WIDTH 500 #define HEIGHT 500 -enum -{ - PROGRAM_TEXT, - PROGRAM_BASIC, - PROGRAM_RECT, - PROGRAM_COUNT -}; - -const char * program_names[PROGRAM_COUNT] = { - "text", - "basic", - "rect", -}; - typedef struct { SDL_Window * sdl_window; - GLBufferRef rect_buffer; - GLProgram * programs[PROGRAM_COUNT]; } Window; static VALUE ruby_class; @@ -49,47 +34,6 @@ static void Init_SDL() initialized = true; } -static void load_gl_buffers(Window * window) -{ - static const GLint rect_coords[4][2] = { - {0, 0}, - {1, 0}, - {0, 1}, - {1, 1}, - }; - - window->rect_buffer = new GLBuffer(); - if (!window->rect_buffer->create(GL_ARRAY_BUFFER, GL_STATIC_DRAW, - &rect_coords, sizeof(rect_coords))) - { - rb_raise(rb_eRuntimeError, "Failed to create rectangle VBO"); - } -} - -static void Init_OpenGL() -{ - static bool initialized = false; - - if (initialized) - return; - - if (gl3wInit() != 0) - { - SDL_Quit(); - rb_raise(rb_eRuntimeError, "Failed to gl3wInit()"); - } - - if (!gl3wIsSupported(3, 0)) - { - SDL_Quit(); - rb_raise(rb_eRuntimeError, "OpenGL 3.0 is not supported"); - } - - glEnable(GL_SCISSOR_TEST); - - initialized = true; -} - static void Window_free(void * ptr) { Window * window = (Window *)ptr; @@ -102,23 +46,19 @@ static void Window_free(void * ptr) static void resize() { - GLint viewport_size[2]; + int width; + int height; Window * window; Data_Get_Struct(window_instance, Window, window); - SDL_GetWindowSize(window->sdl_window, &viewport_size[0], &viewport_size[1]); - glViewport(0, 0, viewport_size[0], viewport_size[1]); - for (int i = 0; i < PROGRAM_COUNT; i++) - { - glUseProgram(window->programs[i]->id); - glUniform2iv(window->programs[i]->uniforms[UNIFORM_VIEWPORT_SIZE], 1, &viewport_size[0]); - } + SDL_GetWindowSize(window->sdl_window, &width, &height); + GL_Resize(width, height); rb_funcall(window_instance, rb_intern("resize"), 4, INT2FIX(0), INT2FIX(0), - INT2FIX(viewport_size[0]), - INT2FIX(viewport_size[1])); + INT2FIX(width), + INT2FIX(height)); } static VALUE Window_new(VALUE klass) @@ -149,33 +89,12 @@ static VALUE Window_new(VALUE klass) (void)SDL_GL_CreateContext(window->sdl_window); - Init_OpenGL(); - - load_gl_buffers(window); + GL_Load(); one_created = true; VALUE rv = Data_Wrap_Struct(ruby_class, NULL, Window_free, window); rb_obj_call_init(rv, 0, NULL); - VALUE programs = rb_iv_get(rv, "@programs"); - for (int i = 0; i < PROGRAM_COUNT; i++) - { - VALUE program = rb_funcall(programs, - rb_intern("[]"), - 1, - ID2SYM(rb_intern(program_names[i]))); - if (RTEST(program)) - { - window->programs[i] = GLProgram_FromRuby(program); - } - else - { - delete window; - rb_raise(rb_eLoadError, - "Error resolving %s program", - program_names[i]); - } - } /* TODO: remove window_instance and map SDL_Window * to Window Ruby object * to support multiple windows. */ diff --git a/src/main.cc b/src/main.cc index f22c041..1a52104 100644 --- a/src/main.cc +++ b/src/main.cc @@ -2,6 +2,7 @@ #include "ruby.h" #include "Font.h" +#include "GL.h" #include "GLProgram.h" #include "GLShader.h" #include "Widget.h" @@ -54,6 +55,7 @@ static int bootstrap() int err_state = 0; Font_Init(); + GL_Init(); GLProgram_Init(); GLShader_Init(); Widget_Init();