cyberarm_engine 0.12.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/Gemfile +1 -1
  4. data/README.md +33 -3
  5. data/Rakefile +1 -1
  6. data/assets/textures/default.png +0 -0
  7. data/cyberarm_engine.gemspec +11 -8
  8. data/lib/cyberarm_engine.rb +24 -4
  9. data/lib/cyberarm_engine/animator.rb +56 -0
  10. data/lib/cyberarm_engine/background.rb +19 -15
  11. data/lib/cyberarm_engine/background_nine_slice.rb +125 -0
  12. data/lib/cyberarm_engine/bounding_box.rb +18 -18
  13. data/lib/cyberarm_engine/cache.rb +4 -0
  14. data/lib/cyberarm_engine/cache/download_manager.rb +121 -0
  15. data/lib/cyberarm_engine/common.rb +16 -16
  16. data/lib/cyberarm_engine/config_file.rb +46 -0
  17. data/lib/cyberarm_engine/game_object.rb +63 -72
  18. data/lib/cyberarm_engine/game_state.rb +7 -4
  19. data/lib/cyberarm_engine/model.rb +207 -0
  20. data/lib/cyberarm_engine/model/material.rb +21 -0
  21. data/lib/cyberarm_engine/model/model_object.rb +131 -0
  22. data/lib/cyberarm_engine/model/parser.rb +74 -0
  23. data/lib/cyberarm_engine/model/parsers/collada_parser.rb +138 -0
  24. data/lib/cyberarm_engine/model/parsers/wavefront_parser.rb +154 -0
  25. data/lib/cyberarm_engine/model_cache.rb +31 -0
  26. data/lib/cyberarm_engine/opengl.rb +28 -0
  27. data/lib/cyberarm_engine/opengl/light.rb +50 -0
  28. data/lib/cyberarm_engine/opengl/orthographic_camera.rb +46 -0
  29. data/lib/cyberarm_engine/opengl/perspective_camera.rb +38 -0
  30. data/lib/cyberarm_engine/opengl/renderer/bounding_box_renderer.rb +249 -0
  31. data/lib/cyberarm_engine/opengl/renderer/g_buffer.rb +164 -0
  32. data/lib/cyberarm_engine/opengl/renderer/opengl_renderer.rb +289 -0
  33. data/lib/cyberarm_engine/opengl/renderer/renderer.rb +22 -0
  34. data/lib/cyberarm_engine/opengl/shader.rb +406 -0
  35. data/lib/cyberarm_engine/opengl/texture.rb +69 -0
  36. data/lib/cyberarm_engine/ray.rb +5 -5
  37. data/lib/cyberarm_engine/stats.rb +21 -0
  38. data/lib/cyberarm_engine/text.rb +45 -31
  39. data/lib/cyberarm_engine/timer.rb +1 -1
  40. data/lib/cyberarm_engine/transform.rb +221 -9
  41. data/lib/cyberarm_engine/ui/border_canvas.rb +4 -3
  42. data/lib/cyberarm_engine/ui/dsl.rb +68 -50
  43. data/lib/cyberarm_engine/ui/element.rb +57 -18
  44. data/lib/cyberarm_engine/ui/elements/button.rb +86 -16
  45. data/lib/cyberarm_engine/ui/elements/check_box.rb +24 -32
  46. data/lib/cyberarm_engine/ui/elements/container.rb +88 -24
  47. data/lib/cyberarm_engine/ui/elements/edit_box.rb +179 -0
  48. data/lib/cyberarm_engine/ui/elements/edit_line.rb +180 -27
  49. data/lib/cyberarm_engine/ui/elements/flow.rb +1 -3
  50. data/lib/cyberarm_engine/ui/elements/image.rb +12 -9
  51. data/lib/cyberarm_engine/ui/elements/label.rb +96 -15
  52. data/lib/cyberarm_engine/ui/elements/list_box.rb +68 -0
  53. data/lib/cyberarm_engine/ui/elements/progress.rb +6 -5
  54. data/lib/cyberarm_engine/ui/elements/radio.rb +6 -0
  55. data/lib/cyberarm_engine/ui/elements/slider.rb +104 -0
  56. data/lib/cyberarm_engine/ui/elements/stack.rb +1 -3
  57. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +40 -31
  58. data/lib/cyberarm_engine/ui/event.rb +8 -7
  59. data/lib/cyberarm_engine/ui/gui_state.rb +89 -6
  60. data/lib/cyberarm_engine/ui/style.rb +10 -9
  61. data/lib/cyberarm_engine/ui/theme.rb +39 -21
  62. data/lib/cyberarm_engine/vector.rb +132 -38
  63. data/lib/cyberarm_engine/version.rb +2 -2
  64. data/lib/cyberarm_engine/{engine.rb → window.rb} +30 -19
  65. metadata +87 -16
  66. data/lib/cyberarm_engine/shader.rb +0 -205
@@ -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