cyberarm_engine 0.13.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -8
  3. data/.rubocop.yml +8 -0
  4. data/.travis.yml +5 -5
  5. data/Gemfile +6 -6
  6. data/LICENSE.txt +21 -21
  7. data/README.md +73 -43
  8. data/Rakefile +10 -10
  9. data/assets/textures/default.png +0 -0
  10. data/bin/console +14 -14
  11. data/bin/setup +8 -8
  12. data/cyberarm_engine.gemspec +39 -36
  13. data/lib/cyberarm_engine.rb +64 -47
  14. data/lib/cyberarm_engine/animator.rb +56 -54
  15. data/lib/cyberarm_engine/background.rb +179 -175
  16. data/lib/cyberarm_engine/background_nine_slice.rb +125 -0
  17. data/lib/cyberarm_engine/bounding_box.rb +150 -150
  18. data/lib/cyberarm_engine/cache.rb +4 -0
  19. data/lib/cyberarm_engine/cache/download_manager.rb +121 -0
  20. data/lib/cyberarm_engine/common.rb +96 -96
  21. data/lib/cyberarm_engine/config_file.rb +46 -0
  22. data/lib/cyberarm_engine/game_object.rb +248 -257
  23. data/lib/cyberarm_engine/game_state.rb +92 -89
  24. data/lib/cyberarm_engine/model.rb +207 -0
  25. data/lib/cyberarm_engine/model/material.rb +21 -0
  26. data/lib/cyberarm_engine/model/model_object.rb +131 -0
  27. data/lib/cyberarm_engine/model/parser.rb +74 -0
  28. data/lib/cyberarm_engine/model/parsers/collada_parser.rb +138 -0
  29. data/lib/cyberarm_engine/model/parsers/wavefront_parser.rb +154 -0
  30. data/lib/cyberarm_engine/model_cache.rb +31 -0
  31. data/lib/cyberarm_engine/opengl.rb +28 -0
  32. data/lib/cyberarm_engine/opengl/light.rb +50 -0
  33. data/lib/cyberarm_engine/opengl/orthographic_camera.rb +46 -0
  34. data/lib/cyberarm_engine/opengl/perspective_camera.rb +38 -0
  35. data/lib/cyberarm_engine/opengl/renderer/bounding_box_renderer.rb +249 -0
  36. data/lib/cyberarm_engine/opengl/renderer/g_buffer.rb +164 -0
  37. data/lib/cyberarm_engine/opengl/renderer/opengl_renderer.rb +289 -0
  38. data/lib/cyberarm_engine/opengl/renderer/renderer.rb +22 -0
  39. data/lib/cyberarm_engine/opengl/shader.rb +406 -0
  40. data/lib/cyberarm_engine/opengl/texture.rb +69 -0
  41. data/lib/cyberarm_engine/ray.rb +56 -56
  42. data/lib/cyberarm_engine/stats.rb +21 -0
  43. data/lib/cyberarm_engine/text.rb +160 -146
  44. data/lib/cyberarm_engine/timer.rb +23 -23
  45. data/lib/cyberarm_engine/transform.rb +296 -273
  46. data/lib/cyberarm_engine/ui/border_canvas.rb +102 -101
  47. data/lib/cyberarm_engine/ui/dsl.rb +138 -99
  48. data/lib/cyberarm_engine/ui/element.rb +315 -276
  49. data/lib/cyberarm_engine/ui/elements/button.rb +160 -67
  50. data/lib/cyberarm_engine/ui/elements/check_box.rb +51 -59
  51. data/lib/cyberarm_engine/ui/elements/container.rb +256 -176
  52. data/lib/cyberarm_engine/ui/elements/edit_box.rb +179 -0
  53. data/lib/cyberarm_engine/ui/elements/edit_line.rb +262 -172
  54. data/lib/cyberarm_engine/ui/elements/flow.rb +15 -17
  55. data/lib/cyberarm_engine/ui/elements/image.rb +72 -52
  56. data/lib/cyberarm_engine/ui/elements/label.rb +156 -50
  57. data/lib/cyberarm_engine/ui/elements/list_box.rb +82 -0
  58. data/lib/cyberarm_engine/ui/elements/progress.rb +51 -50
  59. data/lib/cyberarm_engine/ui/elements/radio.rb +6 -0
  60. data/lib/cyberarm_engine/ui/elements/slider.rb +104 -0
  61. data/lib/cyberarm_engine/ui/elements/stack.rb +11 -13
  62. data/lib/cyberarm_engine/ui/elements/text_block.rb +156 -0
  63. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +65 -56
  64. data/lib/cyberarm_engine/ui/event.rb +47 -47
  65. data/lib/cyberarm_engine/ui/gui_state.rb +226 -135
  66. data/lib/cyberarm_engine/ui/style.rb +38 -37
  67. data/lib/cyberarm_engine/ui/theme.rb +182 -120
  68. data/lib/cyberarm_engine/vector.rb +293 -203
  69. data/lib/cyberarm_engine/version.rb +4 -4
  70. data/lib/cyberarm_engine/{engine.rb → window.rb} +114 -101
  71. metadata +88 -18
  72. data/lib/cyberarm_engine/gosu_ext/circle.rb +0 -9
  73. data/lib/cyberarm_engine/shader.rb +0 -262
@@ -0,0 +1,289 @@
1
+ module CyberarmEngine
2
+ class OpenGLRenderer
3
+ @@immediate_mode_warning = false
4
+
5
+ attr_accessor :show_wireframe
6
+
7
+ def initialize(width:, height:, show_wireframe: false)
8
+ @width = width
9
+ @height = height
10
+ @show_wireframe = show_wireframe
11
+
12
+ @g_buffer = GBuffer.new(width: @width, height: @height)
13
+ end
14
+
15
+ def canvas_size_changed
16
+ @g_buffer.unbind_framebuffer
17
+ @g_buffer.clean_up
18
+
19
+ @g_buffer = GBuffer.new(width: @width, height: @height)
20
+ end
21
+
22
+ def render(camera, lights, entities)
23
+ glViewport(0, 0, @width, @height)
24
+ glEnable(GL_DEPTH_TEST)
25
+
26
+ if Shader.available?("g_buffer") && Shader.available?("lighting")
27
+ @g_buffer.bind_for_writing
28
+ gl_error?
29
+
30
+ glClearColor(0.0, 0.0, 0.0, 0.0)
31
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
32
+
33
+ Shader.use("g_buffer") do |shader|
34
+ gl_error?
35
+
36
+ entities.each do |entity|
37
+ next unless entity.visible && entity.renderable
38
+
39
+ shader.uniform_transform("projection", camera.projection_matrix)
40
+ shader.uniform_transform("view", camera.view_matrix)
41
+ shader.uniform_transform("model", entity.model_matrix)
42
+ shader.uniform_vec3("cameraPosition", camera.position)
43
+
44
+ gl_error?
45
+ draw_model(entity.model, shader)
46
+ entity.draw
47
+ end
48
+ end
49
+
50
+ @g_buffer.unbind_framebuffer
51
+ gl_error?
52
+
53
+ @g_buffer.bind_for_reading
54
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)
55
+
56
+ lighting(lights)
57
+ gl_error?
58
+
59
+ post_processing
60
+ gl_error?
61
+
62
+ # render_framebuffer
63
+ gl_error?
64
+
65
+ @g_buffer.unbind_framebuffer
66
+ gl_error?
67
+ else
68
+ unless @@immediate_mode_warning
69
+ puts "Shaders are disabled or failed to compile, using immediate mode for rendering..."
70
+ end
71
+ @@immediate_mode_warning = true
72
+
73
+ gl_error?
74
+ lights.each(&:draw)
75
+ camera.draw
76
+
77
+ glEnable(GL_NORMALIZE)
78
+ entities.each do |entity|
79
+ next unless entity.visible && entity.renderable
80
+
81
+ glPushMatrix
82
+
83
+ glTranslatef(entity.position.x, entity.position.y, entity.position.z)
84
+ glScalef(entity.scale.x, entity.scale.y, entity.scale.z)
85
+ glRotatef(entity.orientation.x, 1.0, 0, 0)
86
+ glRotatef(entity.orientation.y, 0, 1.0, 0)
87
+ glRotatef(entity.orientation.z, 0, 0, 1.0)
88
+
89
+ gl_error?
90
+ draw_mesh(entity.model)
91
+ entity.draw
92
+ glPopMatrix
93
+ end
94
+ end
95
+
96
+ gl_error?
97
+ end
98
+
99
+ def copy_g_buffer_to_screen
100
+ @g_buffer.set_read_buffer(:position)
101
+ glBlitFramebuffer(0, 0, @g_buffer.width, @g_buffer.height,
102
+ 0, 0, @g_buffer.width / 2, @g_buffer.height / 2,
103
+ GL_COLOR_BUFFER_BIT, GL_LINEAR)
104
+
105
+ @g_buffer.set_read_buffer(:diffuse)
106
+ glBlitFramebuffer(0, 0, @g_buffer.width, @g_buffer.height,
107
+ 0, @g_buffer.height / 2, @g_buffer.width / 2, @g_buffer.height,
108
+ GL_COLOR_BUFFER_BIT, GL_LINEAR)
109
+
110
+ @g_buffer.set_read_buffer(:normal)
111
+ glBlitFramebuffer(0, 0, @g_buffer.width, @g_buffer.height,
112
+ @g_buffer.width / 2, @g_buffer.height / 2, @g_buffer.width, @g_buffer.height,
113
+ GL_COLOR_BUFFER_BIT, GL_LINEAR)
114
+
115
+ @g_buffer.set_read_buffer(:texcoord)
116
+ glBlitFramebuffer(0, 0, @g_buffer.width, @g_buffer.height,
117
+ @g_buffer.width / 2, 0, @g_buffer.width, @g_buffer.height / 2,
118
+ GL_COLOR_BUFFER_BIT, GL_LINEAR)
119
+ end
120
+
121
+ def lighting(lights)
122
+ Shader.use("lighting") do |shader|
123
+ glBindVertexArray(@g_buffer.screen_vbo)
124
+
125
+ glDisable(GL_DEPTH_TEST)
126
+ glEnable(GL_BLEND)
127
+
128
+ glActiveTexture(GL_TEXTURE0)
129
+ glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:diffuse))
130
+ shader.uniform_integer("diffuse", 0)
131
+
132
+ glActiveTexture(GL_TEXTURE1)
133
+ glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:position))
134
+ shader.uniform_integer("position", 1)
135
+
136
+ glActiveTexture(GL_TEXTURE2)
137
+ glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:texcoord))
138
+ shader.uniform_integer("texcoord", 2)
139
+
140
+ glActiveTexture(GL_TEXTURE3)
141
+ glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:normal))
142
+ shader.uniform_integer("normal", 3)
143
+
144
+ glActiveTexture(GL_TEXTURE4)
145
+ glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:depth))
146
+ shader.uniform_integer("depth", 4)
147
+
148
+ lights.each_with_index do |light, _i|
149
+ shader.uniform_integer("light[0].type", light.type)
150
+ shader.uniform_vec3("light[0].direction", light.direction)
151
+ shader.uniform_vec3("light[0].position", light.position)
152
+ shader.uniform_vec3("light[0].diffuse", light.diffuse)
153
+ shader.uniform_vec3("light[0].ambient", light.ambient)
154
+ shader.uniform_vec3("light[0].specular", light.specular)
155
+
156
+ glDrawArrays(GL_TRIANGLES, 0, @g_buffer.vertices.size)
157
+ end
158
+
159
+ glBindVertexArray(0)
160
+ end
161
+ end
162
+
163
+ def post_processing
164
+ end
165
+
166
+ def render_framebuffer
167
+ if Shader.available?("lighting")
168
+ Shader.use("lighting") do |shader|
169
+ glBindVertexArray(@g_buffer.screen_vbo)
170
+
171
+ glDisable(GL_DEPTH_TEST)
172
+ glEnable(GL_BLEND)
173
+
174
+ glActiveTexture(GL_TEXTURE0)
175
+ glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:diffuse))
176
+ shader.uniform_integer("diffuse_texture", 0)
177
+
178
+ glDrawArrays(GL_TRIANGLES, 0, @g_buffer.vertices.size)
179
+
180
+ glBindVertexArray(0)
181
+ end
182
+ end
183
+ end
184
+
185
+ def draw_model(model, shader)
186
+ glBindVertexArray(model.vertex_array_id)
187
+ glEnableVertexAttribArray(0)
188
+ glEnableVertexAttribArray(1)
189
+ glEnableVertexAttribArray(2)
190
+ if model.has_texture?
191
+ glEnableVertexAttribArray(3)
192
+ glEnableVertexAttribArray(4)
193
+ end
194
+
195
+ if @show_wireframe
196
+ glLineWidth(2)
197
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
198
+ Shader.active_shader.uniform_boolean("disableLighting", true)
199
+
200
+ glDrawArrays(GL_TRIANGLES, 0, model.faces.count * 3)
201
+
202
+ Shader.active_shader.uniform_boolean("disableLighting", false)
203
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
204
+ glLineWidth(1)
205
+ end
206
+
207
+ offset = 0
208
+ model.objects.each do |object|
209
+ shader.uniform_boolean("hasTexture", object.has_texture?)
210
+
211
+ if object.has_texture?
212
+ glBindTexture(GL_TEXTURE_2D, object.materials.find { |mat| mat.texture_id }.texture_id)
213
+ else
214
+ glBindTexture(GL_TEXTURE_2D, 0)
215
+ end
216
+
217
+ glDrawArrays(GL_TRIANGLES, offset, object.faces.count * 3)
218
+ offset += object.faces.count * 3
219
+ end
220
+
221
+ if model.has_texture?
222
+ glDisableVertexAttribArray(4)
223
+ glDisableVertexAttribArray(3)
224
+
225
+ glBindTexture(GL_TEXTURE_2D, 0)
226
+ end
227
+ glDisableVertexAttribArray(2)
228
+ glDisableVertexAttribArray(1)
229
+ glDisableVertexAttribArray(0)
230
+
231
+ glBindBuffer(GL_ARRAY_BUFFER, 0)
232
+ glBindVertexArray(0)
233
+ end
234
+
235
+ def draw_mesh(model)
236
+ model.objects.each_with_index do |o, _i|
237
+ glEnable(GL_COLOR_MATERIAL)
238
+ glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
239
+ glShadeModel(GL_FLAT) unless o.faces.first[4]
240
+ glShadeModel(GL_SMOOTH) if o.faces.first[4]
241
+ glEnableClientState(GL_VERTEX_ARRAY)
242
+ glEnableClientState(GL_COLOR_ARRAY)
243
+ glEnableClientState(GL_NORMAL_ARRAY)
244
+
245
+ if o.has_texture?
246
+ glEnable(GL_TEXTURE_2D)
247
+ glBindTexture(GL_TEXTURE_2D, o.materials.find { |mat| mat.texture_id }.texture_id)
248
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY)
249
+ glTexCoordPointer(3, GL_FLOAT, 0, o.flattened_uvs)
250
+ end
251
+
252
+ glVertexPointer(4, GL_FLOAT, 0, o.flattened_vertices)
253
+ glColorPointer(3, GL_FLOAT, 0, o.flattened_materials)
254
+ glNormalPointer(GL_FLOAT, 0, o.flattened_normals)
255
+
256
+ if @show_wireframe # This is kinda expensive
257
+ glDisable(GL_LIGHTING)
258
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
259
+ glPolygonOffset(2, 0.5)
260
+ glLineWidth(3)
261
+
262
+ glDrawArrays(GL_TRIANGLES, 0, o.flattened_vertices_size / 4)
263
+
264
+ glLineWidth(1)
265
+ glPolygonOffset(0, 0)
266
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
267
+ glEnable(GL_LIGHTING)
268
+
269
+ glDrawArrays(GL_TRIANGLES, 0, o.flattened_vertices_size / 4)
270
+ else
271
+ glDrawArrays(GL_TRIANGLES, 0, o.flattened_vertices_size / 4)
272
+ end
273
+
274
+ # glBindBuffer(GL_ARRAY_BUFFER, 0)
275
+
276
+ glDisableClientState(GL_VERTEX_ARRAY)
277
+ glDisableClientState(GL_COLOR_ARRAY)
278
+ glDisableClientState(GL_NORMAL_ARRAY)
279
+
280
+ if o.has_texture?
281
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY)
282
+ glDisable(GL_TEXTURE_2D)
283
+ end
284
+
285
+ glDisable(GL_COLOR_MATERIAL)
286
+ end
287
+ end
288
+ end
289
+ end
@@ -0,0 +1,22 @@
1
+ module CyberarmEngine
2
+ class Renderer
3
+ attr_reader :opengl_renderer, :bounding_box_renderer
4
+
5
+ def initialize
6
+ @bounding_box_renderer = BoundingBoxRenderer.new
7
+ @opengl_renderer = OpenGLRenderer.new(width: $window.width, height: $window.height)
8
+ end
9
+
10
+ def draw(camera, lights, entities)
11
+ @opengl_renderer.render(camera, lights, entities)
12
+ @bounding_box_renderer.render(entities) if @show_bounding_boxes
13
+ end
14
+
15
+ def canvas_size_changed
16
+ @opengl_renderer.canvas_size_changed
17
+ end
18
+
19
+ def finalize # cleanup
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,406 @@
1
+ module CyberarmEngine
2
+ # Ref: https://github.com/vaiorabbit/ruby-opengl/blob/master/sample/OrangeBook/brick.rb
3
+ class Shader
4
+ include OpenGL
5
+ @@shaders = {} # Cache for {Shader} instances
6
+ PREPROCESSOR_CHARACTER = "@".freeze # magic character for preprocessor phase of {Shader} compilation
7
+
8
+ # add instance of {Shader} to cache
9
+ #
10
+ # @param name [String]
11
+ # @param instance [Shader]
12
+ def self.add(name, instance)
13
+ @@shaders[name] = instance
14
+ end
15
+
16
+ # removes {Shader} from cache and cleans up
17
+ #
18
+ # @param name [String]
19
+ def self.delete(name)
20
+ shader = @@shaders.dig(name)
21
+
22
+ if shader
23
+ @@shaders.delete(name)
24
+
25
+ glDeleteProgram(shader.program) if shader.compiled?
26
+ end
27
+ end
28
+
29
+ ##
30
+ # runs _block_ using {Shader} with _name_
31
+ #
32
+ # @example
33
+ #
34
+ # CyberarmEngine::Shader.use("blur") do |shader|
35
+ # shader.uniform_float("radius", 20.0)
36
+ # # OpenGL Code that uses shader
37
+ # end
38
+ #
39
+ # @param name [String] name of {Shader} to use
40
+ # @return [void]
41
+ def self.use(name, &block)
42
+ shader = @@shaders.dig(name)
43
+ if shader
44
+ shader.use(&block)
45
+ else
46
+ raise ArgumentError, "Shader '#{name}' not found!"
47
+ end
48
+ end
49
+
50
+ # returns whether {Shader} with _name_ is in cache
51
+ #
52
+ # @param name [String]
53
+ # @return [Boolean]
54
+ def self.available?(name)
55
+ @@shaders.dig(name).is_a?(Shader)
56
+ end
57
+
58
+ # returns instance of {Shader}, if it exists
59
+ #
60
+ # @param name [String]
61
+ # @return [Shader?]
62
+ def self.get(name)
63
+ @@shaders.dig(name)
64
+ end
65
+
66
+ # returns currently active {Shader}, if one is active
67
+ #
68
+ # @return [Shader?]
69
+ class << self
70
+ attr_reader :active_shader
71
+ end
72
+
73
+ # sets currently active {Shader}
74
+ #
75
+ # @param instance [Shader] instance of {Shader} to set as active
76
+ class << self
77
+ attr_writer :active_shader
78
+ end
79
+
80
+ # stops using currently active {Shader}
81
+ def self.stop
82
+ shader = Shader.active_shader
83
+
84
+ if shader
85
+ shader.stop
86
+ else
87
+ raise ArgumentError, "No active shader to stop!"
88
+ end
89
+ end
90
+
91
+ # returns location of OpenGL Shader uniform
92
+ #
93
+ # @param variable [String]
94
+ def self.attribute_location(variable)
95
+ raise "No active shader!" unless Shader.active_shader
96
+
97
+ Shader.active_shader.attribute_location(variable)
98
+ end
99
+
100
+ # sets _variable_ to _value_
101
+ #
102
+ # @param variable [String]
103
+ # @param value
104
+ def self.set_uniform(variable, value)
105
+ raise "No active shader!" unless Shader.active_shader
106
+
107
+ Shader.active_shader.set_uniform(variable, value)
108
+ end
109
+
110
+ attr_reader :name, :program
111
+
112
+ def initialize(name:, fragment:, includes_dir: nil, vertex: "shaders/default.vert")
113
+ raise "Shader name can not be blank" if name.length == 0
114
+
115
+ @name = name
116
+ @includes_dir = includes_dir
117
+ @compiled = false
118
+
119
+ @program = nil
120
+
121
+ @error_buffer_size = 1024 * 8
122
+ @variable_missing = {}
123
+
124
+ @data = { shaders: {} }
125
+
126
+ unless shader_files_exist?(vertex: vertex, fragment: fragment)
127
+ raise ArgumentError, "Shader files not found: #{vertex} or #{fragment}"
128
+ end
129
+
130
+ create_shader(type: :vertex, source: File.read(vertex))
131
+ create_shader(type: :fragment, source: File.read(fragment))
132
+
133
+ compile_shader(type: :vertex)
134
+ compile_shader(type: :fragment)
135
+ link_shaders
136
+
137
+ @data[:shaders].each { |_key, id| glDeleteShader(id) }
138
+
139
+ # Only add shader if it successfully compiles
140
+ if @compiled
141
+ puts "compiled!"
142
+ puts "Compiled shader: #{@name}"
143
+ Shader.add(@name, self)
144
+ else
145
+ glDeleteProgram(@program)
146
+ warn "FAILED to compile shader: #{@name}", ""
147
+ end
148
+ end
149
+
150
+ # whether vertex and fragment files exist on disk
151
+ #
152
+ # @return [Boolean]
153
+ def shader_files_exist?(vertex:, fragment:)
154
+ File.exist?(vertex) && File.exist?(fragment)
155
+ end
156
+
157
+ # creates an OpenGL Shader of _type_ using _source_
158
+ #
159
+ # @param type [Symbol] valid values are: :vertex, :fragment
160
+ # @param source [String] source code for shader
161
+ def create_shader(type:, source:)
162
+ _shader = nil
163
+
164
+ case type
165
+ when :vertex
166
+ _shader = glCreateShader(GL_VERTEX_SHADER)
167
+ when :fragment
168
+ _shader = glCreateShader(GL_FRAGMENT_SHADER)
169
+ else
170
+ raise ArgumentError, "Unsupported shader type: #{type.inspect}"
171
+ end
172
+
173
+ processed_source = preprocess_source(source: source)
174
+
175
+ _source = [processed_source].pack("p")
176
+ _size = [processed_source.length].pack("I")
177
+ glShaderSource(_shader, 1, _source, _size)
178
+
179
+ @data[:shaders][type] = _shader
180
+ end
181
+
182
+ # evaluates shader preprocessors
183
+ #
184
+ # currently supported preprocessors:
185
+ #
186
+ # @include "file/path" "another/file/path" # => Replace line with contents of file; Shader includes_dir must be specified in constructor
187
+ #
188
+ # @example
189
+ # # Example Vertex Shader #
190
+ # # #version 330 core
191
+ # # @include "material_struct"
192
+ # # void main() {
193
+ # # gl_Position = vec4(1, 1, 1, 1);
194
+ # # }
195
+ #
196
+ # Shader.new(name: "model_renderer", includes_dir: "path/to/includes", vertex: "path/to/vertex_shader.glsl")
197
+ #
198
+ # @param source shader source code
199
+ def preprocess_source(source:)
200
+ lines = source.lines
201
+
202
+ lines.each_with_index do |line, i|
203
+ next unless line.start_with?(PREPROCESSOR_CHARACTER)
204
+
205
+ preprocessor = line.strip.split(" ")
206
+ lines.delete(line)
207
+
208
+ case preprocessor.first
209
+ when "@include"
210
+ unless @includes_dir
211
+ raise ArgumentError,
212
+ "Shader preprocessor include directory was not given for shader #{@name}"
213
+ end
214
+
215
+ preprocessor[1..preprocessor.length - 1].join.scan(/"([^"]*)"/).flatten.each do |file|
216
+ source = File.read("#{@includes_dir}/#{file}.glsl")
217
+
218
+ lines.insert(i, source)
219
+ end
220
+ else
221
+ warn "Unsupported preprocessor #{preprocessor.first} for #{@name}"
222
+ end
223
+ end
224
+
225
+ lines.join
226
+ end
227
+
228
+ # compile OpenGL Shader of _type_
229
+ #
230
+ # @return [Boolean] whether compilation succeeded
231
+ def compile_shader(type:)
232
+ _compiled = false
233
+ _shader = @data[:shaders][type]
234
+ raise ArgumentError, "No shader for #{type.inspect}" unless _shader
235
+
236
+ glCompileShader(_shader)
237
+ buffer = " "
238
+ glGetShaderiv(_shader, GL_COMPILE_STATUS, buffer)
239
+ compiled = buffer.unpack1("L")
240
+
241
+ if compiled == 0
242
+ log = " " * @error_buffer_size
243
+ glGetShaderInfoLog(_shader, @error_buffer_size, nil, log)
244
+ puts "Shader Error: Program \"#{@name}\""
245
+ puts " #{type.to_s.capitalize} Shader InfoLog:", " #{log.strip.split("\n").join("\n ")}\n\n"
246
+ puts " Shader Compiled status: #{compiled}"
247
+ puts " NOTE: assignment of uniforms in shaders is illegal!"
248
+ puts
249
+ else
250
+ _compiled = true
251
+ end
252
+
253
+ _compiled
254
+ end
255
+
256
+ # link compiled OpenGL Shaders in to a OpenGL Program
257
+ #
258
+ # @note linking must succeed or shader cannot be used
259
+ #
260
+ # @return [Boolean] whether linking succeeded
261
+ def link_shaders
262
+ @program = glCreateProgram
263
+ @data[:shaders].values.each do |_shader|
264
+ glAttachShader(@program, _shader)
265
+ end
266
+ glLinkProgram(@program)
267
+
268
+ buffer = " "
269
+ glGetProgramiv(@program, GL_LINK_STATUS, buffer)
270
+ linked = buffer.unpack1("L")
271
+
272
+ if linked == 0
273
+ log = " " * @error_buffer_size
274
+ glGetProgramInfoLog(@program, @error_buffer_size, nil, log)
275
+ puts "Shader Error: Program \"#{@name}\""
276
+ puts " Program InfoLog:", " #{log.strip.split("\n").join("\n ")}\n\n"
277
+ end
278
+
279
+ @compiled = !(linked == 0)
280
+ end
281
+
282
+ # Returns the location of a uniform _variable_
283
+ #
284
+ # @param variable [String]
285
+ # @return [Integer] location of uniform
286
+ def variable(variable)
287
+ loc = glGetUniformLocation(@program, variable)
288
+ if loc == -1
289
+ unless @variable_missing[variable]
290
+ puts "Shader Error: Program \"#{@name}\" has no such uniform named \"#{variable}\"",
291
+ " Is it used in the shader? GLSL may have optimized it out.", " Is it miss spelled?"
292
+ end
293
+ @variable_missing[variable] = true
294
+ end
295
+ loc
296
+ end
297
+
298
+ # @see Shader.use Shader.use
299
+ def use(&block)
300
+ return unless compiled?
301
+ raise "Another shader is already in use! #{Shader.active_shader.name.inspect}" if Shader.active_shader
302
+
303
+ Shader.active_shader = self
304
+
305
+ glUseProgram(@program)
306
+
307
+ if block
308
+ block.call(self)
309
+ stop
310
+ end
311
+ end
312
+
313
+ # stop using shader, if shader is active
314
+ def stop
315
+ Shader.active_shader = nil if Shader.active_shader == self
316
+ glUseProgram(0)
317
+ end
318
+
319
+ # @return [Boolean] whether {Shader} successfully compiled
320
+ def compiled?
321
+ @compiled
322
+ end
323
+
324
+ # returns location of a uniform _variable_
325
+ #
326
+ # @note Use {#variable} for friendly error handling
327
+ # @see #variable Shader#variable
328
+ #
329
+ # @param variable [String]
330
+ # @return [Integer]
331
+ def attribute_location(variable)
332
+ glGetUniformLocation(@program, variable)
333
+ end
334
+
335
+ # send {Transform} to {Shader}
336
+ #
337
+ # @param variable [String]
338
+ # @param value [Transform]
339
+ # @param location [Integer]
340
+ # @return [void]
341
+ def uniform_transform(variable, value, location = nil)
342
+ attr_loc = location || attribute_location(variable)
343
+
344
+ glUniformMatrix4fv(attr_loc, 1, GL_FALSE, value.to_gl.pack("F16"))
345
+ end
346
+
347
+ # send Boolean to {Shader}
348
+ #
349
+ # @param variable [String]
350
+ # @param value [Boolean]
351
+ # @param location [Integer]
352
+ # @return [void]
353
+ def uniform_boolean(variable, value, location = nil)
354
+ attr_loc = location || attribute_location(variable)
355
+
356
+ glUniform1i(attr_loc, value ? 1 : 0)
357
+ end
358
+
359
+ # send Integer to {Shader}
360
+ # @param variable [String]
361
+ # @param value [Integer]
362
+ # @param location [Integer]
363
+ # @return [void]
364
+ def uniform_integer(variable, value, location = nil)
365
+ attr_loc = location || attribute_location(variable)
366
+
367
+ glUniform1i(attr_loc, value)
368
+ end
369
+
370
+ # send Float to {Shader}
371
+ #
372
+ # @param variable [String]
373
+ # @param value [Float]
374
+ # @param location [Integer]
375
+ # @return [void]
376
+ def uniform_float(variable, value, location = nil)
377
+ attr_loc = location || attribute_location(variable)
378
+
379
+ glUniform1f(attr_loc, value)
380
+ end
381
+
382
+ # send {Vector} (x, y, z) to {Shader}
383
+ #
384
+ # @param variable [String]
385
+ # @param value [Vector]
386
+ # @param location [Integer]
387
+ # @return [void]
388
+ def uniform_vec3(variable, value, location = nil)
389
+ attr_loc = location || attribute_location(variable)
390
+
391
+ glUniform3f(attr_loc, *value.to_a[0..2])
392
+ end
393
+
394
+ # send {Vector} to {Shader}
395
+ #
396
+ # @param variable [String]
397
+ # @param value [Vector]
398
+ # @param location [Integer]
399
+ # @return [void]
400
+ def uniform_vec4(variable, value, location = nil)
401
+ attr_loc = location || attribute_location(variable)
402
+
403
+ glUniform4f(attr_loc, *value.to_a)
404
+ end
405
+ end
406
+ end