cyberarm_engine 0.12.1 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) 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 +25 -7
  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 +86 -50
  43. data/lib/cyberarm_engine/ui/element.rb +60 -21
  44. data/lib/cyberarm_engine/ui/elements/button.rb +121 -28
  45. data/lib/cyberarm_engine/ui/elements/check_box.rb +25 -33
  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 +32 -12
  51. data/lib/cyberarm_engine/ui/elements/label.rb +122 -16
  52. data/lib/cyberarm_engine/ui/elements/list_box.rb +82 -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/text_block.rb +156 -0
  58. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +40 -31
  59. data/lib/cyberarm_engine/ui/event.rb +8 -7
  60. data/lib/cyberarm_engine/ui/gui_state.rb +94 -6
  61. data/lib/cyberarm_engine/ui/style.rb +10 -9
  62. data/lib/cyberarm_engine/ui/theme.rb +85 -22
  63. data/lib/cyberarm_engine/vector.rb +132 -38
  64. data/lib/cyberarm_engine/version.rb +2 -2
  65. data/lib/cyberarm_engine/{engine.rb → window.rb} +32 -19
  66. metadata +88 -17
  67. data/lib/cyberarm_engine/gosu_ext/circle.rb +0 -9
  68. data/lib/cyberarm_engine/shader.rb +0 -205
@@ -0,0 +1,69 @@
1
+ module CyberarmEngine
2
+ class Texture
3
+ DEFAULT_TEXTURE = "#{CYBERARM_ENGINE_ROOT_PATH}/assets/textures/default.png".freeze
4
+
5
+ CACHE = {}
6
+
7
+ def self.release_textures
8
+ CACHE.values.each do |id|
9
+ glDeleteTextures(id)
10
+ end
11
+ end
12
+
13
+ def self.from_cache(path, retro)
14
+ CACHE.dig("#{path}?retro=#{retro}")
15
+ end
16
+
17
+ attr_reader :id
18
+
19
+ def initialize(path: nil, image: nil, retro: false)
20
+ raise "keyword :path or :image must be provided!" if path.nil? && image.nil?
21
+
22
+ @retro = retro
23
+ @path = path
24
+
25
+ if @path
26
+ unless File.exist?(@path)
27
+ warn "Missing texture at: #{@path}"
28
+ @retro = true # override retro setting
29
+ @path = DEFAULT_TEXTURE
30
+ end
31
+
32
+ if texture = Texture.from_cache(@path, @retro)
33
+ @id = texture.id
34
+ return
35
+ end
36
+
37
+ image = load_image(@path)
38
+ @id = create_from_image(image)
39
+ else
40
+ @id = create_from_image(image)
41
+ end
42
+ end
43
+
44
+ def load_image(path)
45
+ CACHE["#{path}?retro=#{@retro}"] = self
46
+ Gosu::Image.new(path, retro: @retro)
47
+ end
48
+
49
+ def create_from_image(image)
50
+ array_of_pixels = image.to_blob
51
+
52
+ tex_names_buf = " " * 4
53
+ glGenTextures(1, tex_names_buf)
54
+ texture_id = tex_names_buf.unpack1("L2")
55
+
56
+ glBindTexture(GL_TEXTURE_2D, texture_id)
57
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, array_of_pixels)
58
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
59
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
60
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) if @retro
61
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) unless @retro
62
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
63
+ glGenerateMipmap(GL_TEXTURE_2D)
64
+ gl_error?
65
+
66
+ texture_id
67
+ end
68
+ end
69
+ end
@@ -4,7 +4,7 @@ module CyberarmEngine
4
4
  raise "Origin must be a Vector!" unless origin.is_a?(Vector)
5
5
  raise "Direction must be a Vector!" unless direction.is_a?(Vector)
6
6
 
7
- @origin = origin
7
+ @origin = origin
8
8
  @direction = direction
9
9
  @range = range
10
10
 
@@ -42,15 +42,15 @@ module CyberarmEngine
42
42
  tmin = max(tmin, min(tz1, tz2))
43
43
  tmax = min(tmax, max(tz1, tz2))
44
44
 
45
- return tmax >= max(tmin, 0.0);
45
+ tmax >= max(tmin, 0.0)
46
46
  end
47
47
 
48
48
  def min(x, y)
49
- ((x) < (y) ? (x) : (y))
49
+ ((x) < (y) ? x : y)
50
50
  end
51
51
 
52
52
  def max(x, y)
53
- ((x) > (y) ? (x) : (y))
53
+ ((x) > (y) ? x : y)
54
54
  end
55
55
  end
56
- end
56
+ end
@@ -0,0 +1,21 @@
1
+ module CyberarmEngine
2
+ class Stats
3
+ @@hash = {
4
+ gui_recalculations_last_frame: 0
5
+ }
6
+
7
+ def self.get(key)
8
+ @@hash.dig(key)
9
+ end
10
+
11
+ def self.increment(key, n)
12
+ @@hash[key] += n
13
+ end
14
+
15
+ def self.clear
16
+ @@hash.each do |key, _value|
17
+ @@hash[key] = 0
18
+ end
19
+ end
20
+ end
21
+ end
@@ -5,39 +5,40 @@ module CyberarmEngine
5
5
  attr_accessor :x, :y, :z, :size, :options
6
6
  attr_reader :text, :textobject, :factor_x, :factor_y, :color, :shadow, :shadow_size, :shadow_alpha, :shadow_color
7
7
 
8
- def initialize(text, options={})
8
+ def initialize(text, options = {})
9
9
  @text = text.to_s || ""
10
10
  @options = options
11
11
  @size = options[:size] || 18
12
- @font = options[:font] || "sans-serif"#Gosu.default_font_name
12
+ @font = options[:font] || Gosu.default_font_name
13
13
  @x = options[:x] || 0
14
14
  @y = options[:y] || 0
15
15
  @z = options[:z] || 1025
16
16
  @factor_x = options[:factor_x] || 1
17
17
  @factor_y = options[:factor_y] || 1
18
18
  @color = options[:color] || Gosu::Color::WHITE
19
- @alignment= options[:alignment] || nil
19
+ @mode = options[:mode] || :default
20
+ @alignment = options[:alignment] || nil
20
21
  @shadow = true if options[:shadow] == true
21
22
  @shadow = false if options[:shadow] == false
22
- @shadow = true if options[:shadow] == nil
23
- @shadow_size = options[:shadow_size] ? options[:shadow_size] : 1
24
- @shadow_alpha= options[:shadow_alpha] ? options[:shadow_alpha] : 30
25
- @shadow_alpha= options[:shadow_alpha] ? options[:shadow_alpha] : 30
26
- @shadow_color= options[:shadow_color]
23
+ @shadow = true if options[:shadow].nil?
24
+ @shadow_size = options[:shadow_size] || 1
25
+ @shadow_alpha = options[:shadow_alpha] || 30
26
+ @shadow_alpha = options[:shadow_alpha] || 30
27
+ @shadow_color = options[:shadow_color]
27
28
  @textobject = check_cache(@size, @font)
28
29
 
29
30
  if @alignment
30
31
  case @alignment
31
32
  when :left
32
- @x = 0+BUTTON_PADDING
33
+ @x = 0 + BUTTON_PADDING
33
34
  when :center
34
- @x = ($window.width/2)-(@textobject.text_width(@text)/2)
35
+ @x = ($window.width / 2) - (@textobject.text_width(@text) / 2)
35
36
  when :right
36
- @x = $window.width-BUTTON_PADDING-@textobject.text_width(@text)
37
+ @x = $window.width - BUTTON_PADDING - @textobject.text_width(@text)
37
38
  end
38
39
  end
39
40
 
40
- return self
41
+ self
41
42
  end
42
43
 
43
44
  def check_cache(size, font_name)
@@ -61,7 +62,7 @@ module CyberarmEngine
61
62
  CACHE[@size][@font] = font
62
63
  end
63
64
 
64
- return font
65
+ font
65
66
  end
66
67
 
67
68
  def text=(string)
@@ -73,64 +74,76 @@ module CyberarmEngine
73
74
  @rendered_shadow = nil
74
75
  @factor_x = n
75
76
  end
77
+
76
78
  def factor_y=(n)
77
79
  @rendered_shadow = nil
78
80
  @factor_y = n
79
81
  end
82
+
80
83
  def color=(color)
81
84
  @rendered_shadow = nil
82
85
  @color = color
83
86
  end
87
+
84
88
  def shadow=(boolean)
85
89
  @rendered_shadow = nil
86
90
  @shadow = boolean
87
91
  end
92
+
88
93
  def shadow_size=(n)
89
94
  @rendered_shadow = nil
90
95
  @shadow_size = n
91
96
  end
97
+
92
98
  def shadow_alpha=(n)
93
99
  @rendered_shadow = nil
94
100
  @shadow_alpha = n
95
101
  end
102
+
96
103
  def shadow_color=(n)
97
104
  @rendered_shadow = nil
98
105
  @shadow_color = n
99
106
  end
100
107
 
101
- def width
102
- textobject.text_width(@text)
108
+ def width(text = @text)
109
+ textobject.text_width(text)
110
+ end
111
+
112
+ def markup_width(text = @text)
113
+ textobject.markup_width(text)
103
114
  end
104
115
 
105
- def height
106
- @text.lines.count > 0 ? (@text.lines.count) * textobject.height : @textobject.height
116
+ def height(text = @text)
117
+ text.lines.count > 0 ? text.lines.count * textobject.height : @textobject.height
107
118
  end
108
119
 
109
- def draw
120
+ def draw(method = :draw_markup)
110
121
  if @shadow && !ARGV.join.include?("--no-shadow")
111
122
  shadow_alpha = @color.alpha <= 30 ? @color.alpha : @shadow_alpha
112
- shadow_color = @shadow_color ? @shadow_color : Gosu::Color.rgba(@color.red, @color.green, @color.blue, shadow_alpha)
123
+ shadow_color = @shadow_color || Gosu::Color.rgba(@color.red, @color.green, @color.blue,
124
+ shadow_alpha)
125
+ white = Gosu::Color::WHITE
113
126
 
114
127
  _x = @shadow_size
115
128
  _y = @shadow_size
116
129
 
117
- @rendered_shadow ||= Gosu.render((self.width+(shadow_size*2)).ceil, (self.height+(@shadow_size*2)).ceil) do
118
- @textobject.draw_markup(@text, _x-@shadow_size, _y, @z)
119
- @textobject.draw_markup(@text, _x-@shadow_size, _y-@shadow_size, @z)
130
+ @rendered_shadow ||= Gosu.render((width + (shadow_size * 2)).ceil, (height + (@shadow_size * 2)).ceil) do
131
+ @textobject.send(method, @text, _x - @shadow_size, _y, @z, @factor_x, @factor_y, white, :add)
132
+ @textobject.send(method, @text, _x - @shadow_size, _y - @shadow_size, @z, @factor_x, @factor_y, white, :add)
120
133
 
121
- @textobject.draw_markup(@text, _x, _y-@shadow_size, @z, @factor_x)
122
- @textobject.draw_markup(@text, _x+@shadow_size, _y-@shadow_size, @z)
134
+ @textobject.send(method, @text, _x, _y - @shadow_size, @z, @factor_x, @factor_y, white, :add)
135
+ @textobject.send(method, @text, _x + @shadow_size, _y - @shadow_size, @z, @factor_x, @factor_y, white, :add)
123
136
 
124
- @textobject.draw_markup(@text, _x, _y+@shadow_size, @z)
125
- @textobject.draw_markup(@text, _x-@shadow_size, _y+@shadow_size, @z)
137
+ @textobject.send(method, @text, _x, _y + @shadow_size, @z, @factor_x, @factor_y, white, :add)
138
+ @textobject.send(method, @text, _x - @shadow_size, _y + @shadow_size, @z, @factor_x, @factor_y, white, :add)
126
139
 
127
- @textobject.draw_markup(@text, _x+@shadow_size, _y, @z)
128
- @textobject.draw_markup(@text, _x+@shadow_size, _y+@shadow_size, @z)
140
+ @textobject.send(method, @text, _x + @shadow_size, _y, @z, @factor_x, @factor_y, white, :add)
141
+ @textobject.send(method, @text, _x + @shadow_size, _y + @shadow_size, @z, @factor_x, @factor_y, white, :add)
129
142
  end
130
- @rendered_shadow.draw(@x-@shadow_size, @y-@shadow_size, @z, @factor_x, @factor_y, shadow_color)
143
+ @rendered_shadow.draw(@x - @shadow_size, @y - @shadow_size, @z, @factor_x, @factor_y, shadow_color)
131
144
  end
132
145
 
133
- @textobject.draw_markup(@text, @x, @y, @z, @factor_x, @factor_y, @color)
146
+ @textobject.send(method, @text, @x, @y, @z, @factor_x, @factor_y, @color, @mode)
134
147
  end
135
148
 
136
149
  def alpha=(n)
@@ -141,6 +154,7 @@ module CyberarmEngine
141
154
  @color.alpha
142
155
  end
143
156
 
144
- def update; end
157
+ def update
158
+ end
145
159
  end
146
160
  end
@@ -20,4 +20,4 @@ module CyberarmEngine
20
20
  end
21
21
  end
22
22
  end
23
- end
23
+ end
@@ -1,20 +1,37 @@
1
1
  module CyberarmEngine
2
+ # Basic 4x4 matrix operations
2
3
  class Transform
3
4
  attr_reader :elements
5
+
4
6
  def initialize(matrix)
5
7
  @elements = matrix
6
8
 
7
9
  raise "Transform is wrong size! Got #{@elements.size}, expected 16" if 16 != @elements.size
10
+ raise "Invalid value for matrix, must all be numeric!" if @elements.any? { |e| e.nil? || !e.is_a?(Numeric) }
11
+ end
12
+
13
+ def self.identity
14
+ Transform.new(
15
+ [
16
+ 1, 0, 0, 0,
17
+ 0, 1, 0, 0,
18
+ 0, 0, 1, 0,
19
+ 0, 0, 0, 1
20
+ ]
21
+ )
8
22
  end
9
23
 
24
+ ### 2D Operations meant for interacting with Gosu ###
25
+
26
+ # 2d rotate operation, replicates Gosu's Gosu.rotate function
10
27
  def self.rotate(angle, rotate_around = nil)
11
28
  double c = Math.cos(angle).degrees_to_radians
12
29
  double s = Math.sin(angle).degrees_to_radians
13
30
  matrix = [
14
- +c, +s, 0, 0,
15
- -s, +c, 0, 0,
16
- 0, 0, 1, 0,
17
- 0, 0, 0, 1,
31
+ +c, +s, 0, 0,
32
+ -s, +c, 0, 0,
33
+ 0, 0, 1, 0,
34
+ 0, 0, 0, 1
18
35
  ]
19
36
 
20
37
  rotate_matrix = Transform.new(matrix, rows: 4, columns: 4)
@@ -28,28 +45,30 @@ module CyberarmEngine
28
45
  )
29
46
  end
30
47
 
31
- return rotate_matrix
48
+ rotate_matrix
32
49
  end
33
50
 
51
+ # 2d translate operation, replicates Gosu's Gosu.translate function
34
52
  def self.translate(vector)
35
53
  x, y, z = vector.to_a[0..2]
36
54
  matrix = [
37
55
  1, 0, 0, 0,
38
56
  0, 1, 0, 0,
39
57
  0, 0, 1, 0,
40
- x, y, z, 1,
58
+ x, y, z, 1
41
59
  ]
42
60
 
43
61
  Transform.new(matrix)
44
62
  end
45
63
 
64
+ # 2d scale operation, replicates Gosu's Gosu.rotate function
46
65
  def self.scale(vector, center_around = nil)
47
66
  scale_x, scale_y, scale_z = vector.to_a[0..2]
48
67
  matrix = [
49
68
  scale_x, 0, 0, 0,
50
69
  0, scale_y, 0, 0,
51
70
  0, 0, scale_z, 0,
52
- 0, 0, 0, 1,
71
+ 0, 0, 0, 1
53
72
  ]
54
73
 
55
74
  scale_matrix = Transform.new(matrix)
@@ -63,7 +82,7 @@ module CyberarmEngine
63
82
  )
64
83
  end
65
84
 
66
- return scale_matrix
85
+ scale_matrix
67
86
  end
68
87
 
69
88
  def self.concat(left, right)
@@ -80,5 +99,198 @@ module CyberarmEngine
80
99
 
81
100
  Transform.new(matrix)
82
101
  end
102
+
103
+ #### 3D Operations meant for OpenGL ###
104
+
105
+ def self.translate_3d(vector)
106
+ x, y, z = vector.to_a[0..2]
107
+ matrix = [
108
+ 1, 0, 0, x,
109
+ 0, 1, 0, y,
110
+ 0, 0, 1, z,
111
+ 0, 0, 0, 1
112
+ ]
113
+
114
+ Transform.new(matrix)
115
+ end
116
+
117
+ def self.rotate_3d(vector, _order = "zyx")
118
+ x, y, z = vector.to_a[0..2].map { |axis| axis * Math::PI / 180.0 }
119
+
120
+ rotation_x = Transform.new(
121
+ [
122
+ 1, 0, 0, 0,
123
+ 0, Math.cos(x), -Math.sin(x), 0,
124
+ 0, Math.sin(x), Math.cos(x), 0,
125
+ 0, 0, 0, 1
126
+ ]
127
+ )
128
+
129
+ rotation_y = Transform.new(
130
+ [
131
+ Math.cos(y), 0, Math.sin(y), 0,
132
+ 0, 1, 0, 0,
133
+ -Math.sin(y), 0, Math.cos(y), 0,
134
+ 0, 0, 0, 1
135
+ ]
136
+ )
137
+
138
+ rotation_z = Transform.new(
139
+ [
140
+ Math.cos(z), -Math.sin(z), 0, 0,
141
+ Math.sin(z), Math.cos(z), 0, 0,
142
+ 0, 0, 1, 0,
143
+ 0, 0, 0, 1
144
+ ]
145
+ )
146
+
147
+ rotation_z * rotation_y * rotation_x
148
+ end
149
+
150
+ # Implements glRotatef
151
+ # https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glRotate.xml
152
+ def self.rotate_gl(angle, axis)
153
+ radians = angle * Math::PI / 180.0
154
+ s = Math.sin(radians)
155
+ c = Math.cos(radians)
156
+
157
+ axis = axis.normalized
158
+ x, y, z = axis.to_a[0..2]
159
+
160
+ n = (1.0 - c)
161
+
162
+ Transform.new(
163
+ [
164
+ x * x * n + c, x * y * n - z * s, x * z * n + y * s, 0,
165
+ y * x * n + z * s, y * y * n + c, y * z * n - x * s, 0,
166
+ x * z * n - y * s, y * z * n + x * s, z * z * n + c, 0,
167
+ 0, 0, 0, 1.0
168
+ ]
169
+ )
170
+ end
171
+
172
+ def self.scale_3d(vector)
173
+ x, y, z = vector.to_a[0..2]
174
+
175
+ Transform.new(
176
+ [
177
+ x, 0, 0, 0,
178
+ 0, y, 0, 0,
179
+ 0, 0, z, 0,
180
+ 0, 0, 0, 1
181
+ ]
182
+ )
183
+ end
184
+
185
+ def self.perspective(fov_y, aspect_ratio, near, far)
186
+ f = 1.0 / Math.tan(fov_y.degrees_to_radians / 2.0) # cotangent
187
+ zn = (far + near.to_f) / (near - far.to_f)
188
+ zf = (2.0 * far * near.to_f) / (near - far.to_f)
189
+
190
+ Transform.new(
191
+ [
192
+ f / aspect_ratio, 0.0, 0.0, 0.0,
193
+ 0.0, f, 0.0, 0.0,
194
+ 0.0, 0.0, zn, zf,
195
+ 0.0, 0.0, -1.0, 0.0
196
+ ]
197
+ )
198
+ end
199
+
200
+ def self.orthographic(left, right, bottom, top, near, far)
201
+ s = Vector.new(
202
+ 2 / (right - left.to_f),
203
+ 2 / (top - bottom.to_f),
204
+ -2 / (far - near.to_f)
205
+ )
206
+
207
+ t = Vector.new(
208
+ (right + left.to_f) / (right - left.to_f),
209
+ (top + bottom.to_f) / (top - bottom.to_f),
210
+ (far + near.to_f) / (far - near.to_f)
211
+ )
212
+
213
+ Transform.new(
214
+ [
215
+ s.x, 0.0, 0.0, t.x,
216
+ 0.0, s.y, 0.0, t.y,
217
+ 0.0, 0.0, s.z, t.z,
218
+ 0.0, 0.0, 0.0, 1.0
219
+ ]
220
+ )
221
+ end
222
+
223
+ def self.view(eye, orientation)
224
+ # https://www.3dgep.com/understanding-the-view-matrix/#The_View_Matrix
225
+ cosPitch = Math.cos(orientation.z * Math::PI / 180.0)
226
+ sinPitch = Math.sin(orientation.z * Math::PI / 180.0)
227
+ cosYaw = Math.cos(orientation.y * Math::PI / 180.0)
228
+ sinYaw = Math.sin(orientation.y * Math::PI / 180.0)
229
+
230
+ x_axis = Vector.new(cosYaw, 0, -sinYaw)
231
+ y_axis = Vector.new(sinYaw * sinPitch, cosPitch, cosYaw * sinPitch)
232
+ z_axis = Vector.new(sinYaw * cosPitch, -sinPitch, cosPitch * cosYaw)
233
+
234
+ Transform.new(
235
+ [
236
+ x_axis.x, y_axis.y, z_axis.z, 0,
237
+ x_axis.x, y_axis.y, z_axis.z, 0,
238
+ x_axis.x, y_axis.y, z_axis.z, 0,
239
+ -x_axis.dot(eye), -y_axis.dot(eye), -z_axis.dot(eye), 1
240
+ ]
241
+ )
242
+ end
243
+
244
+ def *(other)
245
+ case other
246
+ when CyberarmEngine::Vector
247
+ matrix = @elements.clone
248
+ list = other.to_a
249
+
250
+ @elements.each_with_index do |e, i|
251
+ matrix[i] = e + list[i % 4]
252
+ end
253
+
254
+ Transform.new(matrix)
255
+
256
+ when CyberarmEngine::Transform
257
+ multiply_matrices(other)
258
+ else
259
+ p other.class
260
+ raise TypeError, "Expected CyberarmEngine::Vector or CyberarmEngine::Transform got #{other.class}"
261
+ end
262
+ end
263
+
264
+ def get(x, y)
265
+ width = 4
266
+
267
+ # puts "Transform|#{self.object_id} -> #{@elements[width * y + x].inspect} (index: #{width * y + x})"
268
+ @elements[width * y + x]
269
+ end
270
+
271
+ def multiply_matrices(other)
272
+ matrix = Array.new(16, 0)
273
+
274
+ 4.times do |x|
275
+ 4.times do |y|
276
+ 4.times do |k|
277
+ matrix[4 * y + x] += get(x, k) * other.get(k, y)
278
+ end
279
+ end
280
+ end
281
+
282
+ Transform.new(matrix)
283
+ end
284
+
285
+ # arranges Matrix in column major form
286
+ def to_gl
287
+ e = @elements
288
+ [
289
+ e[0], e[4], e[8], e[12],
290
+ e[1], e[5], e[9], e[13],
291
+ e[2], e[6], e[10], e[14],
292
+ e[3], e[7], e[11], e[15]
293
+ ]
294
+ end
83
295
  end
84
- end
296
+ end