cyberarm_engine 0.19.0 → 0.19.1

Sign up to get free protection for your applications and to get access to all the features.
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