cyberarm_engine 0.24.4 → 0.24.5

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