rubygl 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,122 @@
1
+ require_relative './native/opengl'
2
+
3
+ module RubyGL
4
+
5
+ class VertexArray
6
+ # Allocates a GPU buffer for vertex_array and transfers the data as a vertex
7
+ # attribute array. This allows you to access the given data from within any
8
+ # shader as long as it is bound beforehand.
9
+ def initialize(vertex_array)
10
+ buff_ptr = FFI::MemoryPointer.new(:uint)
11
+ Native.glGenBuffers(1, buff_ptr)
12
+
13
+ @buffer_id = buff_ptr.get_int(0)
14
+ @buffer_elements = vertex_array.size
15
+ @buffer_valid = true
16
+
17
+ if @buffer_id <= 0 then
18
+ raise "Could Not Allocate A GPU Buffer For The VertexArray Object"
19
+ end
20
+
21
+ Native.glBindBuffer(Native::GL_ARRAY_BUFFER, @buffer_id)
22
+
23
+ vertex_data = FFI::MemoryPointer.new(:float, vertex_array.size)
24
+ vertex_data.write_array_of_float(vertex_array)
25
+
26
+ Native.glBufferData(Native::GL_ARRAY_BUFFER, vertex_data.size, vertex_data, Native::GL_STATIC_DRAW)
27
+
28
+ Native.glBindBuffer(Native::GL_ARRAY_BUFFER, 0)
29
+ end
30
+
31
+ def draw(components)
32
+ raise "Call To VertexArray#draw On Invalid Object" unless @buffer_valid
33
+
34
+ Native.glDrawArrays(Native::GL_TRIANGLES, 0, @buffer_elements / components)
35
+ end
36
+
37
+ def draw_instanced(components, iterations)
38
+ raise "Call To VertexArray#draw_instanced On Invalid Object" unless @buffer_valid
39
+
40
+ Native.glDrawArraysInstanced(Native::GL_TRIANGLES, 0, @buffer_elements / components, iterations)
41
+ end
42
+
43
+ def vertex_attrib_div(location, instances_per_change)
44
+ raise "Call To VertexArray#vertex_attrib_div On Invalid Object" unless @buffer_valid
45
+
46
+ Native.glVertexAttribDivisor(location, instances_per_change)
47
+ end
48
+
49
+ # Binds the VertexArray to the attribute at location within a shader.
50
+ def vertex_attrib_ptr(location, components)
51
+ raise "Call To VertexArray#vertex_attrib_ptr On Invalid Object" unless @buffer_valid
52
+
53
+ # TODO: Move This To Constructor In The Future
54
+ Native.glEnableVertexAttribArray(location)
55
+
56
+ Native.glBindBuffer(Native::GL_ARRAY_BUFFER, @buffer_id)
57
+
58
+ Native.glVertexAttribPointer(location, components, Native::GL_FLOAT, Native::GL_FALSE, 0, FFI::MemoryPointer::NULL)
59
+
60
+ Native.glBindBuffer(Native::GL_ARRAY_BUFFER, 0)
61
+ end
62
+
63
+ # This frees the currently allocated GPU buffer for this VertexArray and
64
+ # invalidates this VertexArray object. Any calls to this object after calling
65
+ # this method will throw a runtime error.
66
+ def release()
67
+ buff_ptr = FFI::MemoryPointer.new(:uint)
68
+ buff_ptr.write_uint(@buffer_id)
69
+
70
+ Native.glDeleteBuffers(1, buff_ptr)
71
+
72
+ @buffer_valid = false
73
+ end
74
+ end
75
+
76
+ class IndexArray
77
+ def initialize(index_array)
78
+ buff_ptr = FFI::MemoryPointer.new(:pointer)
79
+ Native.glGenBuffers(1, buff_ptr)
80
+
81
+ @buffer_id = buff_ptr.get_int(0)
82
+ @buffer_elements = index_array.size
83
+ @buffer_valid = true
84
+
85
+ if @buffer_id <= 0 then
86
+ raise "Could Not Allocate A GPU Buffer For The IndexArray Object"
87
+ end
88
+
89
+ Native.glBindBuffer(Native::GL_ELEMENT_ARRAY_BUFFER, @buffer_id)
90
+
91
+ index_data = FFI::MemoryPointer.new(:uint, index_array.size)
92
+ index_data.write_array_of_uint(index_array)
93
+
94
+ Native.glBufferData(Native::GL_ELEMENT_ARRAY_BUFFER, index_data.size, index_data, Native::GL_STATIC_DRAW)
95
+
96
+ Native.glBindBuffer(Native::GL_ELEMENT_ARRAY_BUFFER, 0)
97
+ end
98
+
99
+ def draw(components)
100
+ raise "Call To IndexArray#draw On Frozen Object" unless @buffer_valid
101
+
102
+ Native.glBindBuffer(Native::GL_ELEMENT_ARRAY_BUFFER, @buffer_id)
103
+
104
+ Native.glDrawElements(Native::GL_TRIANGLES, @buffer_elements, Native::GL_UNSIGNED_INT, FFI::MemoryPointer::NULL)
105
+
106
+ Native.glBindBuffer(Native::GL_ELEMENT_ARRAY_BUFFER, 0)
107
+ end
108
+
109
+ # This frees the currently allocated GPU buffer for this IndexArray and
110
+ # invalidates this IndexArray object. Any calls to this object after calling
111
+ # this method will throw a runtime error.
112
+ def release()
113
+ buff_ptr = FFI::MemoryPointer.new(:uint)
114
+ buff_ptr.put_int(@buffer_id)
115
+
116
+ Native.glDeleteBuffers(1, buff_ptr)
117
+
118
+ @buffer_valid = false
119
+ end
120
+ end
121
+
122
+ end
@@ -0,0 +1,51 @@
1
+ require_relative './native/glcontext'
2
+ require_relative './native/window'
3
+
4
+ module RubyGL
5
+
6
+ class DefaultSetup
7
+ # The args hash accepts position and size values to be applied to the window.
8
+ # :x, :y, :width, :height
9
+ def initialize(args = {})
10
+ RubyGL::Native.initWindow
11
+ RubyGL::Native.initInput
12
+ RubyGL::Native.loadLibrary(FFI::Pointer::NULL)
13
+
14
+ RubyGL::Native.setAttribute(:context_major_version, 4)
15
+ RubyGL::Native.setAttribute(:context_minor_version, 2)
16
+ RubyGL::Native.setAttribute(:context_profile_mask, RubyGL::Native.CONTEXT_PROFILE_COMPATIBILITY)
17
+
18
+ RubyGL::Native.setAttribute(:depth_size, 24)
19
+ RubyGL::Native.setAttribute(:doublebuffer, 1)
20
+
21
+ @window = RubyGL::Native.createWindow("RubyGL Window",
22
+ args[:x] || 50, args[:y] || 50,
23
+ args[:width] || 500, args[:height] || 500,
24
+ RubyGL::Native.OPENGL)
25
+ @context = RubyGL::Native.createContext(@window)
26
+
27
+ RubyGL::Native.makeCurrent(@window, @context)
28
+ RubyGL::Native.setSwapInterval(1)
29
+ end
30
+
31
+ def show_dialog(title, message)
32
+ RubyGL::Native.showSimpleMessageBox(0, title, message, @window)
33
+ end
34
+
35
+ def end_frame()
36
+ RubyGL::Native.swapWindow(@window)
37
+
38
+ RubyGL::Native.pumpEvents()
39
+ end
40
+
41
+ def teardown()
42
+ RubyGL::Native.deleteContext(@context)
43
+ RubyGL::Native.destroyWindow(@window)
44
+
45
+ RubyGL::Native.unloadLibrary()
46
+ RubyGL::Native.quitWindow()
47
+ RubyGL::Native.quitInput()
48
+ end
49
+ end
50
+
51
+ end
@@ -0,0 +1,203 @@
1
+ require_relative './native/opengl'
2
+
3
+ module RubyGL
4
+
5
+ class Shader
6
+ def initialize(vert_src, frag_src)
7
+ @v_shader_id = Native.glCreateShader(Native::GL_VERTEX_SHADER)
8
+ @f_shader_id = Native.glCreateShader(Native::GL_FRAGMENT_SHADER)
9
+ @attrib_locs, @uniform_locs = {}, {}
10
+
11
+ # Pointer Used To Point To The String Of Our Source Code
12
+ shader_src_ptr = FFI::MemoryPointer.new(:pointer)
13
+ shader_src_len = FFI::MemoryPointer.new(:int)
14
+
15
+ v_shader_ptr = FFI::MemoryPointer.from_string(vert_src)
16
+ f_shader_ptr = FFI::MemoryPointer.from_string(frag_src)
17
+
18
+ shader_src_ptr.write_pointer(v_shader_ptr.address)
19
+ shader_src_len.write_int(vert_src.size)
20
+ Native.glShaderSource(@v_shader_id, 1, shader_src_ptr, shader_src_len)
21
+
22
+ shader_src_ptr.write_pointer(f_shader_ptr.address)
23
+ shader_src_len.write_int(frag_src.size)
24
+ Native.glShaderSource(@f_shader_id, 1, shader_src_ptr, shader_src_len)
25
+
26
+ Native.glCompileShader(@v_shader_id)
27
+ Native.glCompileShader(@f_shader_id)
28
+
29
+ # Throw Exception With Compile Error If Compilation Failed
30
+ Shader.compile_status(@v_shader_id)
31
+ Shader.compile_status(@f_shader_id)
32
+
33
+ @program_id = Native.glCreateProgram()
34
+
35
+ Native.glAttachShader(@program_id, @v_shader_id)
36
+ Native.glAttachShader(@program_id, @f_shader_id)
37
+
38
+ Native.glLinkProgram(@program_id)
39
+
40
+ Shader.link_status(@program_id)
41
+ end
42
+
43
+ def use_program()
44
+ Native.glUseProgram(@program_id)
45
+ end
46
+
47
+ # Splat Operator Aggregates Incoming Parameters And Expands Outgoing Parameters
48
+ # If method_sym Does Not Accept A Pointer To The Data, Pass nil For data And
49
+ # Pass In The Normal Parameters Required For The Specific Method.
50
+ def send_uniform(method_sym, var_name, data = nil, *args)
51
+ loc = @uniform_locs[var_name] ||= uniform_location(var_name)
52
+
53
+ if data != nil then
54
+ data_ptr = FFI::MemoryPointer.new(:float, data.size)
55
+ data_ptr.write_array_of_float(data)
56
+
57
+ RubyGL::Native.send(method_sym, loc, *args, data_ptr)
58
+ else
59
+ RubyGL::Native.send(method_sym, loc, *args)
60
+ end
61
+ end
62
+
63
+ def attrib_location(var_name)
64
+ Native.glGetAttribLocation(@program_id, var_name)
65
+ end
66
+
67
+ private
68
+ def uniform_location(var_name)
69
+ Native.glGetUniformLocation(@program_id, var_name)
70
+ end
71
+
72
+ def self.compile_status(shader_id)
73
+ result = FFI::MemoryPointer.new(:int)
74
+ Native.glGetShaderiv(shader_id, Native::GL_COMPILE_STATUS, result)
75
+
76
+ if (result.get_int(0) != Native::GL_TRUE) then
77
+ Native.glGetShaderiv(shader_id, Native::GL_INFO_LOG_LENGTH, result)
78
+
79
+ compile_log = FFI::MemoryPointer.new(:char, result.get_int(0))
80
+
81
+ Native.glGetShaderInfoLog(shader_id, compile_log.total, FFI::MemoryPointer::NULL, compile_log)
82
+
83
+ raise compile_log.read_string_to_null()
84
+ end
85
+ end
86
+
87
+ def self.link_status(program_id)
88
+ result = FFI::MemoryPointer.new(:int)
89
+ Native.glGetProgramiv(program_id, Native::GL_LINK_STATUS, result)
90
+
91
+ if (result.get_int(0) != Native::GL_TRUE) then
92
+ Native.glGetProgramiv(program_id, Native::GL_INFO_LOG_LENGTH, result)
93
+
94
+ link_log = FFI::MemoryPointer.new(:char, result.get_int(0))
95
+
96
+ Native.glGetShaderInfoLog(program_id, link_log.total, FFI::MemoryPointer::NULL, link_log)
97
+
98
+ raise link_log.read_string_to_null()
99
+ end
100
+ end
101
+ end
102
+
103
+ class ShaderGenerator
104
+ def self.faceted_shader()
105
+ Shader.new('''
106
+ #version 130
107
+ in vec3 position;
108
+ out vec3 vPosition;
109
+ uniform mat4 modelview;
110
+ uniform mat4 perspective;
111
+
112
+ void main() {
113
+ vec4 hPosition = modelview * vec4(position, 1);
114
+ vPosition = hPosition.xyz;
115
+
116
+ gl_Position = perspective * hPosition;
117
+ }
118
+ ''','''
119
+ #version 130
120
+ in vec3 vPosition;
121
+ out vec4 fColor;
122
+ uniform vec4 color;
123
+ uniform vec3 light;
124
+
125
+ void main() {
126
+ vec3 dx = dFdy(vPosition);
127
+ vec3 dy = dFdx(vPosition);
128
+ vec3 triangle_norm = normalize(cross(dx, dy));
129
+ float factor = clamp(dot(triangle_norm, light), 0, 1);
130
+
131
+ fColor = vec4(color.xyz * factor, color.w);
132
+ }
133
+ ''')
134
+ end
135
+
136
+ def self.color_shader()
137
+ Shader.new('''
138
+ #version 130
139
+ in vec3 position;
140
+ uniform mat4 modelview;
141
+ uniform mat4 perspective;
142
+
143
+ void main() {
144
+ gl_Position = perspective * modelView * vec4(position, 1);
145
+ }
146
+ ''','''
147
+ #version 130
148
+ out vec4 fColor;
149
+ uniform vec4 color;
150
+
151
+ void main() {
152
+ fColor = color;
153
+ }
154
+ ''')
155
+ end
156
+
157
+ def self.phong_shader()
158
+ Shader.new('''
159
+ #version 130
160
+ in vec3 position;
161
+ in vec3 normal;
162
+ out vec3 vPosition;
163
+ out vec3 vNormal;
164
+ uniform mat4 modelview;
165
+ uniform mat4 perspective;
166
+
167
+ void main() {
168
+ vec4 hPosition = modelview * vec4(position, 1);
169
+ vPosition = hPosition.xyz;
170
+ vNormal = (modelview * vec4(normal, 1)).xyz;
171
+
172
+ gl_Position = perspective * hPosition;
173
+ }
174
+ ''','''
175
+ #version 130
176
+ in vec3 vPosition;
177
+ in vec3 vNormal;
178
+ out vec4 fColor;
179
+ uniform vec4 color;
180
+ uniform vec3 light;
181
+
182
+ float PhongIntensity(vec3 pos, vec3 norm) {
183
+ vec3 N = normalize(norm);
184
+ vec3 L = normalize(light - pos);
185
+ vec3 E = normalize(pos);
186
+ vec3 R = reflect(L, N);
187
+
188
+ float diffuse = abs(dot(N, L));
189
+ float specular = abs(dot(R, E));
190
+
191
+ return clamp(pow(specular, 10) + diffuse, 0, 1);
192
+ }
193
+
194
+ void main() {
195
+ float intensity = PhongIntensity(vPosition, vNormal);
196
+
197
+ fColor = vec4(intensity * color.xyz, color.w);
198
+ }
199
+ ''')
200
+ end
201
+ end
202
+
203
+ end
@@ -0,0 +1,77 @@
1
+
2
+ module RubyGL
3
+
4
+ class Util
5
+ # Returns the value of the array at the overflow wrapped index value.
6
+ def self.overflow_wrap(array, index)
7
+ array[index % array.size]
8
+ end
9
+
10
+ # Returns [vertices, indices] where vertices is an array of unique vertices
11
+ # corresponding to the vertices in vertex_array and indices is the zero based
12
+ # index array that references the unqiue vertices stored in vertices.
13
+ def self.gen_index_arrays(vertex_array, components)
14
+ unique_points, point_indices, index_hash = [], [], {}
15
+
16
+ curr_index = 0
17
+ vertex_array.each_slice(components) { |point|
18
+ index = index_hash[point]
19
+
20
+ if index then
21
+ point_indices.push(index)
22
+ else
23
+ unique_points.push(point)
24
+ point_indices.push(curr_index)
25
+
26
+ index_hash[point] = curr_index
27
+ curr_index += 1
28
+ end
29
+ }
30
+ unique_points.flatten!
31
+
32
+ [unique_points, point_indices]
33
+ end
34
+
35
+
36
+ # Returns an array of vertex normals which is indexable by the indices array.
37
+ # The components parameter should be the number of components per vertex for
38
+ # each triangle.
39
+ #
40
+ # The vertices parameters must contain 3 component vertices or this function
41
+ # will not be able to compute appropriate normals.
42
+ def self.gen_vertex_normals(indices, vertices)
43
+ normals = Array.new(vertices.size, Vec3.new)
44
+
45
+ indices.each_slice(3) { |i1, i2, i3|
46
+ # 3 Components Per Vertex (Index References 3 Components)
47
+ adj_i1, adj_i2, adj_i3 = i1 * 3, i2 * 3, i3 * 3
48
+
49
+ # Construct Triangle Vertices
50
+ v1 = Vec3.new(vertices[adj_i1..adj_i1 + 2])
51
+ v2 = Vec3.new(vertices[adj_i2..adj_i2 + 2])
52
+ v3 = Vec3.new(vertices[adj_i3..adj_i3 + 2])
53
+
54
+ d1 = v2 - v1
55
+ d2 = v3 - v2
56
+
57
+ normal = d1.cross(d2)
58
+ normal.norm!
59
+
60
+ normals[i1] += normal
61
+ normals[i2] += normal
62
+ normals[i3] += normal
63
+ }
64
+
65
+ # Normalize All Vectors And Convert From Vec3 To Flat Array
66
+ normals.map! { |normal_vector|
67
+ normal_vector.norm!
68
+
69
+ normal_vector.to_a
70
+ }
71
+ normals.flatten!
72
+
73
+ normals
74
+ end
75
+ end
76
+
77
+ end