cyberarm_engine 0.19.0 → 0.19.1

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 (74) 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/LICENSE.txt +21 -21
  7. data/README.md +74 -74
  8. data/Rakefile +10 -10
  9. data/bin/console +14 -14
  10. data/bin/setup +8 -8
  11. data/cyberarm_engine.gemspec +39 -39
  12. data/lib/cyberarm_engine/animator.rb +219 -219
  13. data/lib/cyberarm_engine/background.rb +179 -179
  14. data/lib/cyberarm_engine/background_nine_slice.rb +142 -142
  15. data/lib/cyberarm_engine/bounding_box.rb +150 -150
  16. data/lib/cyberarm_engine/builtin/intro_state.rb +130 -130
  17. data/lib/cyberarm_engine/cache/download_manager.rb +121 -121
  18. data/lib/cyberarm_engine/cache.rb +4 -4
  19. data/lib/cyberarm_engine/common.rb +113 -113
  20. data/lib/cyberarm_engine/config_file.rb +46 -46
  21. data/lib/cyberarm_engine/console/command.rb +157 -157
  22. data/lib/cyberarm_engine/console/commands/help_command.rb +43 -43
  23. data/lib/cyberarm_engine/console/subcommand.rb +99 -99
  24. data/lib/cyberarm_engine/console.rb +248 -248
  25. data/lib/cyberarm_engine/game_object.rb +248 -248
  26. data/lib/cyberarm_engine/game_state.rb +97 -97
  27. data/lib/cyberarm_engine/model/material.rb +21 -21
  28. data/lib/cyberarm_engine/model/model_object.rb +131 -131
  29. data/lib/cyberarm_engine/model/parser.rb +74 -74
  30. data/lib/cyberarm_engine/model/parsers/collada_parser.rb +138 -138
  31. data/lib/cyberarm_engine/model/parsers/wavefront_parser.rb +154 -154
  32. data/lib/cyberarm_engine/model.rb +212 -212
  33. data/lib/cyberarm_engine/model_cache.rb +31 -31
  34. data/lib/cyberarm_engine/opengl/light.rb +50 -50
  35. data/lib/cyberarm_engine/opengl/orthographic_camera.rb +46 -46
  36. data/lib/cyberarm_engine/opengl/perspective_camera.rb +38 -38
  37. data/lib/cyberarm_engine/opengl/renderer/bounding_box_renderer.rb +249 -249
  38. data/lib/cyberarm_engine/opengl/renderer/g_buffer.rb +164 -164
  39. data/lib/cyberarm_engine/opengl/renderer/opengl_renderer.rb +298 -298
  40. data/lib/cyberarm_engine/opengl/renderer/renderer.rb +22 -22
  41. data/lib/cyberarm_engine/opengl/shader.rb +406 -406
  42. data/lib/cyberarm_engine/opengl/texture.rb +69 -69
  43. data/lib/cyberarm_engine/opengl.rb +28 -28
  44. data/lib/cyberarm_engine/ray.rb +56 -56
  45. data/lib/cyberarm_engine/stats.rb +21 -21
  46. data/lib/cyberarm_engine/text.rb +197 -197
  47. data/lib/cyberarm_engine/timer.rb +23 -23
  48. data/lib/cyberarm_engine/transform.rb +296 -296
  49. data/lib/cyberarm_engine/ui/border_canvas.rb +102 -102
  50. data/lib/cyberarm_engine/ui/dsl.rb +139 -139
  51. data/lib/cyberarm_engine/ui/element.rb +488 -488
  52. data/lib/cyberarm_engine/ui/elements/button.rb +97 -97
  53. data/lib/cyberarm_engine/ui/elements/check_box.rb +54 -54
  54. data/lib/cyberarm_engine/ui/elements/container.rb +256 -256
  55. data/lib/cyberarm_engine/ui/elements/edit_box.rb +179 -179
  56. data/lib/cyberarm_engine/ui/elements/edit_line.rb +263 -263
  57. data/lib/cyberarm_engine/ui/elements/flow.rb +15 -15
  58. data/lib/cyberarm_engine/ui/elements/image.rb +72 -72
  59. data/lib/cyberarm_engine/ui/elements/list_box.rb +88 -82
  60. data/lib/cyberarm_engine/ui/elements/progress.rb +51 -51
  61. data/lib/cyberarm_engine/ui/elements/radio.rb +6 -6
  62. data/lib/cyberarm_engine/ui/elements/slider.rb +104 -104
  63. data/lib/cyberarm_engine/ui/elements/stack.rb +11 -11
  64. data/lib/cyberarm_engine/ui/elements/text_block.rb +162 -162
  65. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +65 -65
  66. data/lib/cyberarm_engine/ui/event.rb +54 -54
  67. data/lib/cyberarm_engine/ui/gui_state.rb +256 -256
  68. data/lib/cyberarm_engine/ui/style.rb +49 -49
  69. data/lib/cyberarm_engine/ui/theme.rb +207 -207
  70. data/lib/cyberarm_engine/vector.rb +293 -293
  71. data/lib/cyberarm_engine/version.rb +4 -4
  72. data/lib/cyberarm_engine/window.rb +120 -120
  73. data/lib/cyberarm_engine.rb +71 -71
  74. metadata +3 -3
@@ -1,406 +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
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