cyberarm_engine 0.12.1 → 0.13.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -8
  3. data/.travis.yml +5 -5
  4. data/Gemfile +6 -6
  5. data/LICENSE.txt +21 -21
  6. data/README.md +43 -43
  7. data/Rakefile +10 -10
  8. data/bin/console +14 -14
  9. data/bin/setup +8 -8
  10. data/cyberarm_engine.gemspec +36 -36
  11. data/lib/cyberarm_engine.rb +47 -46
  12. data/lib/cyberarm_engine/animator.rb +54 -0
  13. data/lib/cyberarm_engine/background.rb +175 -175
  14. data/lib/cyberarm_engine/bounding_box.rb +149 -149
  15. data/lib/cyberarm_engine/common.rb +96 -96
  16. data/lib/cyberarm_engine/engine.rb +101 -101
  17. data/lib/cyberarm_engine/game_object.rb +256 -256
  18. data/lib/cyberarm_engine/game_state.rb +88 -88
  19. data/lib/cyberarm_engine/gosu_ext/circle.rb +8 -8
  20. data/lib/cyberarm_engine/ray.rb +55 -55
  21. data/lib/cyberarm_engine/shader.rb +262 -205
  22. data/lib/cyberarm_engine/text.rb +146 -146
  23. data/lib/cyberarm_engine/timer.rb +22 -22
  24. data/lib/cyberarm_engine/transform.rb +272 -83
  25. data/lib/cyberarm_engine/ui/border_canvas.rb +100 -100
  26. data/lib/cyberarm_engine/ui/dsl.rb +98 -101
  27. data/lib/cyberarm_engine/ui/element.rb +275 -259
  28. data/lib/cyberarm_engine/ui/elements/button.rb +66 -66
  29. data/lib/cyberarm_engine/ui/elements/check_box.rb +58 -58
  30. data/lib/cyberarm_engine/ui/elements/container.rb +176 -162
  31. data/lib/cyberarm_engine/ui/elements/edit_line.rb +171 -102
  32. data/lib/cyberarm_engine/ui/elements/flow.rb +16 -16
  33. data/lib/cyberarm_engine/ui/elements/image.rb +51 -51
  34. data/lib/cyberarm_engine/ui/elements/label.rb +49 -49
  35. data/lib/cyberarm_engine/ui/elements/progress.rb +49 -49
  36. data/lib/cyberarm_engine/ui/elements/stack.rb +12 -12
  37. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +55 -55
  38. data/lib/cyberarm_engine/ui/event.rb +46 -45
  39. data/lib/cyberarm_engine/ui/gui_state.rb +134 -134
  40. data/lib/cyberarm_engine/ui/style.rb +36 -36
  41. data/lib/cyberarm_engine/ui/theme.rb +120 -119
  42. data/lib/cyberarm_engine/vector.rb +202 -198
  43. data/lib/cyberarm_engine/version.rb +4 -4
  44. metadata +6 -5
@@ -1,89 +1,89 @@
1
- module CyberarmEngine
2
- class GameState
3
- include Common
4
-
5
- attr_accessor :options, :global_pause
6
- attr_reader :game_objects, :containers
7
-
8
- def initialize(options={})
9
- @options = options
10
- @game_objects = []
11
- @global_pause = false
12
- $window.text_input = nil unless options[:preserve_text_input]
13
-
14
- @down_keys = {}
15
- end
16
-
17
- def setup
18
- end
19
-
20
- def draw
21
- @game_objects.each(&:draw)
22
- end
23
-
24
- def update
25
- @game_objects.each(&:update)
26
- end
27
-
28
- def draw_bounding_box(box)
29
- x,y, max_x, max_y = box.x, box.y, box.max_x, box.max_y
30
-
31
- color = Gosu::Color.rgba(255, 127, 64, 240)
32
-
33
- # pipe = 4
34
- # Gosu.draw_rect(x-width, y-height, x+(width*2), y+(height*2), color, Float::INFINITY)
35
- # puts "BB render: #{x}:#{y} w:#{x.abs+width} h:#{y.abs+height}"
36
- # Gosu.draw_rect(x, y, x.abs+width, y.abs+height, color, Float::INFINITY)
37
-
38
- # TOP LEFT to BOTTOM LEFT
39
- $window.draw_line(
40
- x, y, color,
41
- x, max_y, color,
42
- Float::INFINITY
43
- )
44
- # BOTTOM LEFT to BOTTOM RIGHT
45
- $window.draw_line(
46
- x, max_y, color,
47
- max_x, max_y, color,
48
- Float::INFINITY
49
- )
50
- # BOTTOM RIGHT to TOP RIGHT
51
- $window.draw_line(
52
- max_x, max_y, color,
53
- max_x, y, color,
54
- Float::INFINITY
55
- )
56
- # TOP RIGHT to TOP LEFT
57
- $window.draw_line(
58
- max_x, y, color,
59
- x, y, color,
60
- Float::INFINITY
61
- )
62
- end
63
-
64
- def destroy
65
- @options.clear
66
- @game_objects.clear
67
- end
68
-
69
- def button_down(id)
70
- @down_keys[id] = true
71
-
72
- @game_objects.each do |o|
73
- o.button_down(id)
74
- end
75
- end
76
-
77
- def button_up(id)
78
- @down_keys.delete(id)
79
-
80
- @game_objects.each do |o|
81
- o.button_up(id)
82
- end
83
- end
84
-
85
- def add_game_object(object)
86
- @game_objects << object
87
- end
88
- end
1
+ module CyberarmEngine
2
+ class GameState
3
+ include Common
4
+
5
+ attr_accessor :options, :global_pause
6
+ attr_reader :game_objects, :containers
7
+
8
+ def initialize(options={})
9
+ @options = options
10
+ @game_objects = []
11
+ @global_pause = false
12
+ $window.text_input = nil unless options[:preserve_text_input]
13
+
14
+ @down_keys = {}
15
+ end
16
+
17
+ def setup
18
+ end
19
+
20
+ def draw
21
+ @game_objects.each(&:draw)
22
+ end
23
+
24
+ def update
25
+ @game_objects.each(&:update)
26
+ end
27
+
28
+ def draw_bounding_box(box)
29
+ x,y, max_x, max_y = box.x, box.y, box.max_x, box.max_y
30
+
31
+ color = Gosu::Color.rgba(255, 127, 64, 240)
32
+
33
+ # pipe = 4
34
+ # Gosu.draw_rect(x-width, y-height, x+(width*2), y+(height*2), color, Float::INFINITY)
35
+ # puts "BB render: #{x}:#{y} w:#{x.abs+width} h:#{y.abs+height}"
36
+ # Gosu.draw_rect(x, y, x.abs+width, y.abs+height, color, Float::INFINITY)
37
+
38
+ # TOP LEFT to BOTTOM LEFT
39
+ $window.draw_line(
40
+ x, y, color,
41
+ x, max_y, color,
42
+ Float::INFINITY
43
+ )
44
+ # BOTTOM LEFT to BOTTOM RIGHT
45
+ $window.draw_line(
46
+ x, max_y, color,
47
+ max_x, max_y, color,
48
+ Float::INFINITY
49
+ )
50
+ # BOTTOM RIGHT to TOP RIGHT
51
+ $window.draw_line(
52
+ max_x, max_y, color,
53
+ max_x, y, color,
54
+ Float::INFINITY
55
+ )
56
+ # TOP RIGHT to TOP LEFT
57
+ $window.draw_line(
58
+ max_x, y, color,
59
+ x, y, color,
60
+ Float::INFINITY
61
+ )
62
+ end
63
+
64
+ def destroy
65
+ @options.clear
66
+ @game_objects.clear
67
+ end
68
+
69
+ def button_down(id)
70
+ @down_keys[id] = true
71
+
72
+ @game_objects.each do |o|
73
+ o.button_down(id)
74
+ end
75
+ end
76
+
77
+ def button_up(id)
78
+ @down_keys.delete(id)
79
+
80
+ @game_objects.each do |o|
81
+ o.button_up(id)
82
+ end
83
+ end
84
+
85
+ def add_game_object(object)
86
+ @game_objects << object
87
+ end
88
+ end
89
89
  end
@@ -1,9 +1,9 @@
1
- module Gosu
2
- # Sourced from https://gist.github.com/ippa/662583
3
- def self.draw_circle(cx,cy,r, z = 9999,color = Gosu::Color::GREEN, step = 10)
4
- 0.step(360, step) do |a1|
5
- a2 = a1 + step
6
- draw_line(cx + Gosu.offset_x(a1, r), cy + Gosu.offset_y(a1, r), color, cx + Gosu.offset_x(a2, r), cy + Gosu.offset_y(a2, r), color, z)
7
- end
8
- end
1
+ module Gosu
2
+ # Sourced from https://gist.github.com/ippa/662583
3
+ def self.draw_circle(cx,cy,r, z = 9999,color = Gosu::Color::GREEN, step = 10)
4
+ 0.step(360, step) do |a1|
5
+ a2 = a1 + step
6
+ draw_line(cx + Gosu.offset_x(a1, r), cy + Gosu.offset_y(a1, r), color, cx + Gosu.offset_x(a2, r), cy + Gosu.offset_y(a2, r), color, z)
7
+ end
8
+ end
9
9
  end
@@ -1,56 +1,56 @@
1
- module CyberarmEngine
2
- class Ray
3
- def initialize(origin, direction, range = Float::INFINITY)
4
- raise "Origin must be a Vector!" unless origin.is_a?(Vector)
5
- raise "Direction must be a Vector!" unless direction.is_a?(Vector)
6
-
7
- @origin = origin
8
- @direction = direction
9
- @range = range
10
-
11
- @inverse_direction = @direction.inverse
12
- end
13
-
14
- def intersect?(intersectable)
15
- if intersectable.is_a?(BoundingBox)
16
- intersect_bounding_box?(intersectable)
17
- else
18
- raise NotImplementedError, "Ray intersection test for #{intersectable.class} not implemented."
19
- end
20
- end
21
-
22
- # Based on: https://tavianator.com/fast-branchless-raybounding-box-intersections/
23
- def intersect_bounding_box?(box)
24
- tmin = -@range
25
- tmax = @range
26
-
27
- tx1 = (box.min.x - @origin.x) * @inverse_direction.x
28
- tx2 = (box.max.x - @origin.x) * @inverse_direction.x
29
-
30
- tmin = max(tmin, min(tx1, tx2))
31
- tmax = min(tmax, max(tx1, tx2))
32
-
33
- ty1 = (box.min.y - @origin.y) * @inverse_direction.y
34
- ty2 = (box.max.y - @origin.y) * @inverse_direction.y
35
-
36
- tmin = max(tmin, min(ty1, ty2))
37
- tmax = min(tmax, max(ty1, ty2))
38
-
39
- tz1 = (box.min.z - @origin.z) * @inverse_direction.z
40
- tz2 = (box.max.z - @origin.z) * @inverse_direction.z
41
-
42
- tmin = max(tmin, min(tz1, tz2))
43
- tmax = min(tmax, max(tz1, tz2))
44
-
45
- return tmax >= max(tmin, 0.0);
46
- end
47
-
48
- def min(x, y)
49
- ((x) < (y) ? (x) : (y))
50
- end
51
-
52
- def max(x, y)
53
- ((x) > (y) ? (x) : (y))
54
- end
55
- end
1
+ module CyberarmEngine
2
+ class Ray
3
+ def initialize(origin, direction, range = Float::INFINITY)
4
+ raise "Origin must be a Vector!" unless origin.is_a?(Vector)
5
+ raise "Direction must be a Vector!" unless direction.is_a?(Vector)
6
+
7
+ @origin = origin
8
+ @direction = direction
9
+ @range = range
10
+
11
+ @inverse_direction = @direction.inverse
12
+ end
13
+
14
+ def intersect?(intersectable)
15
+ if intersectable.is_a?(BoundingBox)
16
+ intersect_bounding_box?(intersectable)
17
+ else
18
+ raise NotImplementedError, "Ray intersection test for #{intersectable.class} not implemented."
19
+ end
20
+ end
21
+
22
+ # Based on: https://tavianator.com/fast-branchless-raybounding-box-intersections/
23
+ def intersect_bounding_box?(box)
24
+ tmin = -@range
25
+ tmax = @range
26
+
27
+ tx1 = (box.min.x - @origin.x) * @inverse_direction.x
28
+ tx2 = (box.max.x - @origin.x) * @inverse_direction.x
29
+
30
+ tmin = max(tmin, min(tx1, tx2))
31
+ tmax = min(tmax, max(tx1, tx2))
32
+
33
+ ty1 = (box.min.y - @origin.y) * @inverse_direction.y
34
+ ty2 = (box.max.y - @origin.y) * @inverse_direction.y
35
+
36
+ tmin = max(tmin, min(ty1, ty2))
37
+ tmax = min(tmax, max(ty1, ty2))
38
+
39
+ tz1 = (box.min.z - @origin.z) * @inverse_direction.z
40
+ tz2 = (box.max.z - @origin.z) * @inverse_direction.z
41
+
42
+ tmin = max(tmin, min(tz1, tz2))
43
+ tmax = min(tmax, max(tz1, tz2))
44
+
45
+ return tmax >= max(tmin, 0.0);
46
+ end
47
+
48
+ def min(x, y)
49
+ ((x) < (y) ? (x) : (y))
50
+ end
51
+
52
+ def max(x, y)
53
+ ((x) > (y) ? (x) : (y))
54
+ end
55
+ end
56
56
  end
@@ -1,205 +1,262 @@
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 = {}
6
-
7
- def self.add(name, instance)
8
- @@shaders[name] = instance
9
- end
10
-
11
- def self.use(name, &block)
12
- shader = @@shaders.dig(name)
13
- if shader
14
- shader.use(&block)
15
- else
16
- raise ArgumentError, "Shader '#{name}' not found!"
17
- end
18
- end
19
-
20
- def self.available?(name)
21
- @@shaders.dig(name).is_a?(Shader)
22
- end
23
-
24
- def self.get(name)
25
- @@shaders.dig(name)
26
- end
27
-
28
- def self.active_shader
29
- @active_shader
30
- end
31
-
32
- def self.active_shader=(instance)
33
- @active_shader = instance
34
- end
35
-
36
- def self.stop
37
- shader = Shader.active_shader
38
-
39
- if shader
40
- shader.stop
41
- else
42
- raise ArgumentError, "No active shader to stop!"
43
- end
44
- end
45
-
46
- def self.attribute_location(variable)
47
- raise RuntimeError, "No active shader!" unless Shader.active_shader
48
- Shader.active_shader.attribute_location(variable)
49
- end
50
-
51
- def self.set_uniform(variable, value)
52
- raise RuntimeError, "No active shader!" unless Shader.active_shader
53
- Shader.active_shader.set_uniform(variable, value)
54
- end
55
-
56
- attr_reader :name, :program
57
- def initialize(name:, vertex: "shaders/default.vert", fragment:)
58
- @name = name
59
- @vertex_file = vertex
60
- @fragment_file = fragment
61
- @compiled = false
62
-
63
- @program = nil
64
-
65
- @error_buffer_size = 1024
66
- @variable_missing = {}
67
-
68
- raise ArgumentError, "Shader files not found: #{@vertex_file} or #{@fragment_file}" unless shader_files_exist?
69
-
70
- create_shaders
71
- compile_shaders
72
-
73
- # Only add shader if it successfully compiles
74
- if @compiled
75
- Shader.add(@name, self)
76
- else
77
- puts "FAILED to compile shader: #{@name}", ""
78
- end
79
- end
80
-
81
- def shader_files_exist?
82
- File.exist?(@vertex_file) && File.exist?(@fragment_file)
83
- end
84
-
85
- def create_shaders
86
- @vertex = glCreateShader(GL_VERTEX_SHADER)
87
- @fragment = glCreateShader(GL_FRAGMENT_SHADER)
88
-
89
- source = [File.read(@vertex_file)].pack('p')
90
- size = [File.size(@vertex_file)].pack('I')
91
- glShaderSource(@vertex, 1, source, size)
92
-
93
- source = [File.read(@fragment_file)].pack('p')
94
- size = [File.size(@fragment_file)].pack('I')
95
- glShaderSource(@fragment, 1, source, size)
96
- end
97
-
98
- def compile_shaders
99
- return unless shader_files_exist?
100
-
101
- glCompileShader(@vertex)
102
- buffer = ' '
103
- glGetShaderiv(@vertex, GL_COMPILE_STATUS, buffer)
104
- compiled = buffer.unpack('L')[0]
105
-
106
- if compiled == 0
107
- log = ' ' * @error_buffer_size
108
- glGetShaderInfoLog(@vertex, @error_buffer_size, nil, log)
109
- puts "Shader Error: Program \"#{@name}\""
110
- puts " Vectex Shader InfoLog:", " #{log.strip.split("\n").join("\n ")}\n\n"
111
- puts " Shader Compiled status: #{compiled}"
112
- puts " NOTE: assignment of uniforms in shaders is illegal!"
113
- puts
114
- return
115
- end
116
-
117
- glCompileShader(@fragment)
118
- buffer = ' '
119
- glGetShaderiv(@fragment, GL_COMPILE_STATUS, buffer)
120
- compiled = buffer.unpack('L')[0]
121
-
122
- if compiled == 0
123
- log = ' ' * @error_buffer_size
124
- glGetShaderInfoLog(@fragment, @error_buffer_size, nil, log)
125
- puts "Shader Error: Program \"#{@name}\""
126
- puts " Fragment Shader InfoLog:", " #{log.strip.split("\n").join("\n ")}\n\n"
127
- puts " Shader Compiled status: #{compiled}"
128
- puts " NOTE: assignment of uniforms in shader is illegal!"
129
- puts
130
- return
131
- end
132
-
133
- @program = glCreateProgram
134
- glAttachShader(@program, @vertex)
135
- glAttachShader(@program, @fragment)
136
- glLinkProgram(@program)
137
-
138
- buffer = ' '
139
- glGetProgramiv(@program, GL_LINK_STATUS, buffer)
140
- linked = buffer.unpack('L')[0]
141
-
142
- if linked == 0
143
- log = ' ' * @error_buffer_size
144
- glGetProgramInfoLog(@program, @error_buffer_size, nil, log)
145
- puts "Shader Error: Program \"#{@name}\""
146
- puts " Program InfoLog:", " #{log.strip.split("\n").join("\n ")}\n\n"
147
- end
148
-
149
- @compiled = linked == 0 ? false : true
150
- end
151
-
152
- # Returns the location of a uniform variable
153
- def variable(variable)
154
- loc = glGetUniformLocation(@program, variable)
155
- if (loc == -1)
156
- puts "Shader Error: Program \"#{@name}\" has no such uniform named \"#{variable}\"", " Is it used in the shader? GLSL may have optimized it out.", " Is it miss spelled?" unless @variable_missing[variable]
157
- @variable_missing[variable] = true
158
- end
159
- return loc
160
- end
161
-
162
- def use(&block)
163
- return unless compiled?
164
- raise "Another shader is already in use! #{Shader.active_shader.name.inspect}" if Shader.active_shader
165
- Shader.active_shader=self
166
-
167
- glUseProgram(@program)
168
-
169
- if block
170
- block.call(self)
171
- stop
172
- end
173
- end
174
-
175
- def stop
176
- Shader.active_shader = nil if Shader.active_shader == self
177
- glUseProgram(0)
178
- end
179
-
180
- def compiled?
181
- @compiled
182
- end
183
-
184
- def attribute_location(variable)
185
- glGetUniformLocation(@program, variable)
186
- end
187
-
188
- def set_uniform(variable, value, location = nil)
189
- attr_loc = location ? location : attribute_location(variable)
190
-
191
- case value.class.to_s.downcase.to_sym
192
- when :integer
193
- glUniform1i(attr_loc, value)
194
- when :float
195
- glUniform1f(attr_loc, value)
196
- when :string
197
- when :array
198
- else
199
- raise NotImplementedError, "Shader support for #{value.class.inspect} not implemented."
200
- end
201
-
202
- Window.handle_gl_error
203
- end
204
- end
205
- 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 = {}
6
+ PREPROCESSOR_CHARACTER = "@"
7
+
8
+ def self.add(name, instance)
9
+ @@shaders[name] = instance
10
+ end
11
+
12
+ def self.use(name, &block)
13
+ shader = @@shaders.dig(name)
14
+ if shader
15
+ shader.use(&block)
16
+ else
17
+ raise ArgumentError, "Shader '#{name}' not found!"
18
+ end
19
+ end
20
+
21
+ def self.available?(name)
22
+ @@shaders.dig(name).is_a?(Shader)
23
+ end
24
+
25
+ def self.get(name)
26
+ @@shaders.dig(name)
27
+ end
28
+
29
+ def self.active_shader
30
+ @active_shader
31
+ end
32
+
33
+ def self.active_shader=(instance)
34
+ @active_shader = instance
35
+ end
36
+
37
+ def self.stop
38
+ shader = Shader.active_shader
39
+
40
+ if shader
41
+ shader.stop
42
+ else
43
+ raise ArgumentError, "No active shader to stop!"
44
+ end
45
+ end
46
+
47
+ def self.attribute_location(variable)
48
+ raise RuntimeError, "No active shader!" unless Shader.active_shader
49
+ Shader.active_shader.attribute_location(variable)
50
+ end
51
+
52
+ def self.set_uniform(variable, value)
53
+ raise RuntimeError, "No active shader!" unless Shader.active_shader
54
+ Shader.active_shader.set_uniform(variable, value)
55
+ end
56
+
57
+ attr_reader :name, :program
58
+ def initialize(name:, includes_dir: nil, vertex: "shaders/default.vert", fragment:)
59
+ raise "Shader name can not be blank" if name.length == 0
60
+
61
+ @name = name
62
+ @includes_dir = includes_dir
63
+ @compiled = false
64
+
65
+ @program = nil
66
+
67
+ @error_buffer_size = 1024
68
+ @variable_missing = {}
69
+
70
+ @data = {shaders: {}}
71
+
72
+ unless shader_files_exist?(vertex: vertex, fragment: fragment)
73
+ raise ArgumentError, "Shader files not found: #{vertex} or #{fragment}"
74
+ end
75
+
76
+ create_shader(type: :vertex, source: File.read(vertex))
77
+ create_shader(type: :fragment, source: File.read(fragment))
78
+
79
+ compile_shader(type: :vertex)
80
+ compile_shader(type: :fragment)
81
+ link_shaders
82
+
83
+ # Only add shader if it successfully compiles
84
+ if @compiled
85
+ puts "compiled!"
86
+ puts "Compiled shader: #{@name}"
87
+ Shader.add(@name, self)
88
+ else
89
+ warn "FAILED to compile shader: #{@name}", ""
90
+ end
91
+ end
92
+
93
+ def shader_files_exist?(vertex:, fragment:)
94
+ File.exist?(vertex) && File.exist?(fragment)
95
+ end
96
+
97
+ def create_shader(type:, source:)
98
+ _shader = nil
99
+
100
+ case type
101
+ when :vertex
102
+ _shader = glCreateShader(GL_VERTEX_SHADER)
103
+ when :fragment
104
+ _shader = glCreateShader(GL_FRAGMENT_SHADER)
105
+ else
106
+ warn "Unsupported shader type: #{type.inspect}"
107
+ end
108
+
109
+ processed_source = preprocess_source(source: source)
110
+
111
+ _source = [processed_source].pack("p")
112
+ _size = [processed_source.length].pack("I")
113
+ glShaderSource(_shader, 1, _source, _size)
114
+
115
+ @data[:shaders][type] =_shader
116
+ end
117
+
118
+ def preprocess_source(source:)
119
+ lines = source.lines
120
+
121
+ lines.each_with_index do |line, i|
122
+ if line.start_with?(PREPROCESSOR_CHARACTER)
123
+ preprocessor = line.strip.split(" ")
124
+ lines.delete(line)
125
+
126
+ case preprocessor.first
127
+ when "@include"
128
+ raise ArgumentError, "Shader preprocessor include directory was not given for shader #{@name}" unless @includes_dir
129
+
130
+ preprocessor[1..preprocessor.length - 1].join.scan(/"([^"]*)"/).flatten.each do |file|
131
+ source = File.read("#{@includes_dir}/#{file}.glsl")
132
+
133
+ lines.insert(i, source)
134
+ end
135
+ else
136
+ warn "Unsupported preprocessor #{preprocessor.first} for #{@name}"
137
+ end
138
+ end
139
+ end
140
+
141
+ lines.join
142
+ end
143
+
144
+ def compile_shader(type:)
145
+ _compiled = false
146
+ _shader = @data[:shaders][type]
147
+ raise ArgumentError, "No shader for #{type.inspect}" unless _shader
148
+
149
+ glCompileShader(_shader)
150
+ buffer = ' '
151
+ glGetShaderiv(_shader, GL_COMPILE_STATUS, buffer)
152
+ compiled = buffer.unpack('L')[0]
153
+
154
+ if compiled == 0
155
+ log = ' ' * @error_buffer_size
156
+ glGetShaderInfoLog(_shader, @error_buffer_size, nil, log)
157
+ puts "Shader Error: Program \"#{@name}\""
158
+ puts " #{type.to_s.capitalize} Shader InfoLog:", " #{log.strip.split("\n").join("\n ")}\n\n"
159
+ puts " Shader Compiled status: #{compiled}"
160
+ puts " NOTE: assignment of uniforms in shaders is illegal!"
161
+ puts
162
+ else
163
+ _compiled = true
164
+ end
165
+
166
+ return _compiled
167
+ end
168
+
169
+ def link_shaders
170
+ @program = glCreateProgram
171
+ @data[:shaders].values.each do |_shader|
172
+ glAttachShader(@program, _shader)
173
+ end
174
+ glLinkProgram(@program)
175
+
176
+ buffer = ' '
177
+ glGetProgramiv(@program, GL_LINK_STATUS, buffer)
178
+ linked = buffer.unpack('L')[0]
179
+
180
+ if linked == 0
181
+ log = ' ' * @error_buffer_size
182
+ glGetProgramInfoLog(@program, @error_buffer_size, nil, log)
183
+ puts "Shader Error: Program \"#{@name}\""
184
+ puts " Program InfoLog:", " #{log.strip.split("\n").join("\n ")}\n\n"
185
+ end
186
+
187
+ @compiled = linked == 0 ? false : true
188
+ end
189
+
190
+ # Returns the location of a uniform variable
191
+ def variable(variable)
192
+ loc = glGetUniformLocation(@program, variable)
193
+ if (loc == -1)
194
+ puts "Shader Error: Program \"#{@name}\" has no such uniform named \"#{variable}\"", " Is it used in the shader? GLSL may have optimized it out.", " Is it miss spelled?" unless @variable_missing[variable]
195
+ @variable_missing[variable] = true
196
+ end
197
+ return loc
198
+ end
199
+
200
+ def use(&block)
201
+ return unless compiled?
202
+ raise "Another shader is already in use! #{Shader.active_shader.name.inspect}" if Shader.active_shader
203
+ Shader.active_shader=self
204
+
205
+ glUseProgram(@program)
206
+
207
+ if block
208
+ block.call(self)
209
+ stop
210
+ end
211
+ end
212
+
213
+ def stop
214
+ Shader.active_shader = nil if Shader.active_shader == self
215
+ glUseProgram(0)
216
+ end
217
+
218
+ def compiled?
219
+ @compiled
220
+ end
221
+
222
+ def attribute_location(variable)
223
+ glGetUniformLocation(@program, variable)
224
+ end
225
+
226
+ def uniform_transform(variable, value, location = nil)
227
+ attr_loc = location ? location : attribute_location(variable)
228
+
229
+ glUniformMatrix4fv(attr_loc, 1, GL_FALSE, value.to_gl.pack("F16"))
230
+ end
231
+
232
+ def uniform_boolean(variable, value, location = nil)
233
+ attr_loc = location ? location : attribute_location(variable)
234
+
235
+ glUniform1i(attr_loc, value ? 1 : 0)
236
+ end
237
+
238
+ def uniform_integer(variable, value, location = nil)
239
+ attr_loc = location ? location : attribute_location(variable)
240
+
241
+ glUniform1i(attr_loc, value)
242
+ end
243
+
244
+ def uniform_float(variable, value, location = nil)
245
+ attr_loc = location ? location : attribute_location(variable)
246
+
247
+ glUniform1f(attr_loc, value)
248
+ end
249
+
250
+ def uniform_vec3(variable, value, location = nil)
251
+ attr_loc = location ? location : attribute_location(variable)
252
+
253
+ glUniform3f(attr_loc, *value.to_a[0..2])
254
+ end
255
+
256
+ def uniform_vec4(variable, value, location = nil)
257
+ attr_loc = location ? location : attribute_location(variable)
258
+
259
+ glUniform4f(attr_loc, *value.to_a)
260
+ end
261
+ end
262
+ end