3rb 0.1.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 (100) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +2 -0
  3. data/3rb.gemspec +29 -0
  4. data/CHANGELOG.md +12 -0
  5. data/LICENSE +21 -0
  6. data/README.md +321 -0
  7. data/Rakefile +13 -0
  8. data/examples/01_hello_cube.rb +29 -0
  9. data/examples/02_basic_geometries.rb +56 -0
  10. data/examples/03_materials.rb +61 -0
  11. data/examples/04_lighting.rb +63 -0
  12. data/examples/05_animation.rb +79 -0
  13. data/examples/06_custom_shader.rb +92 -0
  14. data/examples/07_scene_graph.rb +74 -0
  15. data/examples/08_orbit_controls.rb +50 -0
  16. data/examples/09_3d_chart.rb +71 -0
  17. data/examples/10_procedural_terrain.rb +140 -0
  18. data/examples/11_particle_system.rb +68 -0
  19. data/examples/12_model_loader.rb +73 -0
  20. data/examples/13_game_prototype.rb +145 -0
  21. data/examples/14_utah_teapot.rb +291 -0
  22. data/examples/15_stanford_bunny.rb +200 -0
  23. data/examples/16_cornell_box.rb +373 -0
  24. data/examples/17_weird_fractal4.rb +130 -0
  25. data/examples/18_platonic_solids.rb +268 -0
  26. data/lib/3rb/animation/animation_clip.rb +287 -0
  27. data/lib/3rb/animation/animation_mixer.rb +366 -0
  28. data/lib/3rb/cameras/camera.rb +50 -0
  29. data/lib/3rb/cameras/orthographic_camera.rb +92 -0
  30. data/lib/3rb/cameras/perspective_camera.rb +103 -0
  31. data/lib/3rb/controls/orbit_controls.rb +341 -0
  32. data/lib/3rb/core/buffer_attribute.rb +172 -0
  33. data/lib/3rb/core/group.rb +9 -0
  34. data/lib/3rb/core/object3d.rb +298 -0
  35. data/lib/3rb/core/scene.rb +78 -0
  36. data/lib/3rb/dsl/helpers.rb +57 -0
  37. data/lib/3rb/dsl/scene_builder.rb +288 -0
  38. data/lib/3rb/ffi/glfw.rb +61 -0
  39. data/lib/3rb/ffi/opengl.rb +137 -0
  40. data/lib/3rb/ffi/platform.rb +65 -0
  41. data/lib/3rb/geometries/box_geometry.rb +101 -0
  42. data/lib/3rb/geometries/buffer_geometry.rb +345 -0
  43. data/lib/3rb/geometries/cone_geometry.rb +29 -0
  44. data/lib/3rb/geometries/cylinder_geometry.rb +149 -0
  45. data/lib/3rb/geometries/plane_geometry.rb +75 -0
  46. data/lib/3rb/geometries/sphere_geometry.rb +93 -0
  47. data/lib/3rb/geometries/torus_geometry.rb +77 -0
  48. data/lib/3rb/lights/ambient_light.rb +9 -0
  49. data/lib/3rb/lights/directional_light.rb +57 -0
  50. data/lib/3rb/lights/hemisphere_light.rb +26 -0
  51. data/lib/3rb/lights/light.rb +27 -0
  52. data/lib/3rb/lights/point_light.rb +68 -0
  53. data/lib/3rb/lights/rect_area_light.rb +35 -0
  54. data/lib/3rb/lights/spot_light.rb +88 -0
  55. data/lib/3rb/loaders/gltf_loader.rb +304 -0
  56. data/lib/3rb/loaders/loader.rb +94 -0
  57. data/lib/3rb/loaders/obj_loader.rb +186 -0
  58. data/lib/3rb/loaders/texture_loader.rb +55 -0
  59. data/lib/3rb/materials/basic_material.rb +70 -0
  60. data/lib/3rb/materials/lambert_material.rb +102 -0
  61. data/lib/3rb/materials/material.rb +114 -0
  62. data/lib/3rb/materials/phong_material.rb +106 -0
  63. data/lib/3rb/materials/shader_material.rb +104 -0
  64. data/lib/3rb/materials/standard_material.rb +106 -0
  65. data/lib/3rb/math/color.rb +246 -0
  66. data/lib/3rb/math/euler.rb +156 -0
  67. data/lib/3rb/math/math_utils.rb +132 -0
  68. data/lib/3rb/math/matrix3.rb +269 -0
  69. data/lib/3rb/math/matrix4.rb +501 -0
  70. data/lib/3rb/math/quaternion.rb +337 -0
  71. data/lib/3rb/math/vector2.rb +216 -0
  72. data/lib/3rb/math/vector3.rb +366 -0
  73. data/lib/3rb/math/vector4.rb +233 -0
  74. data/lib/3rb/native/gl.rb +382 -0
  75. data/lib/3rb/native/native.rb +55 -0
  76. data/lib/3rb/native/window.rb +111 -0
  77. data/lib/3rb/native.rb +9 -0
  78. data/lib/3rb/objects/line.rb +116 -0
  79. data/lib/3rb/objects/mesh.rb +40 -0
  80. data/lib/3rb/objects/points.rb +71 -0
  81. data/lib/3rb/renderers/opengl_renderer.rb +567 -0
  82. data/lib/3rb/renderers/renderer.rb +60 -0
  83. data/lib/3rb/renderers/shader_lib.rb +100 -0
  84. data/lib/3rb/textures/cube_texture.rb +26 -0
  85. data/lib/3rb/textures/data_texture.rb +35 -0
  86. data/lib/3rb/textures/render_target.rb +125 -0
  87. data/lib/3rb/textures/texture.rb +190 -0
  88. data/lib/3rb/version.rb +5 -0
  89. data/lib/3rb.rb +86 -0
  90. data/shaders/basic.frag +19 -0
  91. data/shaders/basic.vert +15 -0
  92. data/shaders/common/lights.glsl +53 -0
  93. data/shaders/common/uniforms.glsl +9 -0
  94. data/shaders/lambert.frag +37 -0
  95. data/shaders/lambert.vert +22 -0
  96. data/shaders/phong.frag +51 -0
  97. data/shaders/phong.vert +28 -0
  98. data/shaders/standard.frag +92 -0
  99. data/shaders/standard.vert +28 -0
  100. metadata +155 -0
@@ -0,0 +1,501 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunrb
4
+ class Matrix4
5
+ attr_reader :elements
6
+
7
+ def initialize
8
+ @elements = [
9
+ 1.0, 0.0, 0.0, 0.0,
10
+ 0.0, 1.0, 0.0, 0.0,
11
+ 0.0, 0.0, 1.0, 0.0,
12
+ 0.0, 0.0, 0.0, 1.0
13
+ ]
14
+ end
15
+
16
+ def set(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44)
17
+ @elements[0] = n11.to_f
18
+ @elements[4] = n12.to_f
19
+ @elements[8] = n13.to_f
20
+ @elements[12] = n14.to_f
21
+ @elements[1] = n21.to_f
22
+ @elements[5] = n22.to_f
23
+ @elements[9] = n23.to_f
24
+ @elements[13] = n24.to_f
25
+ @elements[2] = n31.to_f
26
+ @elements[6] = n32.to_f
27
+ @elements[10] = n33.to_f
28
+ @elements[14] = n34.to_f
29
+ @elements[3] = n41.to_f
30
+ @elements[7] = n42.to_f
31
+ @elements[11] = n43.to_f
32
+ @elements[15] = n44.to_f
33
+ self
34
+ end
35
+
36
+ def identity
37
+ set(
38
+ 1, 0, 0, 0,
39
+ 0, 1, 0, 0,
40
+ 0, 0, 1, 0,
41
+ 0, 0, 0, 1
42
+ )
43
+ end
44
+
45
+ def copy(m)
46
+ @elements = m.elements.dup
47
+ self
48
+ end
49
+
50
+ def clone
51
+ Matrix4.new.copy(self)
52
+ end
53
+
54
+ def *(other)
55
+ clone.multiply!(other)
56
+ end
57
+
58
+ def multiply!(other)
59
+ ae = @elements
60
+ be = other.elements
61
+
62
+ a11 = ae[0]
63
+ a12 = ae[4]
64
+ a13 = ae[8]
65
+ a14 = ae[12]
66
+ a21 = ae[1]
67
+ a22 = ae[5]
68
+ a23 = ae[9]
69
+ a24 = ae[13]
70
+ a31 = ae[2]
71
+ a32 = ae[6]
72
+ a33 = ae[10]
73
+ a34 = ae[14]
74
+ a41 = ae[3]
75
+ a42 = ae[7]
76
+ a43 = ae[11]
77
+ a44 = ae[15]
78
+
79
+ b11 = be[0]
80
+ b12 = be[4]
81
+ b13 = be[8]
82
+ b14 = be[12]
83
+ b21 = be[1]
84
+ b22 = be[5]
85
+ b23 = be[9]
86
+ b24 = be[13]
87
+ b31 = be[2]
88
+ b32 = be[6]
89
+ b33 = be[10]
90
+ b34 = be[14]
91
+ b41 = be[3]
92
+ b42 = be[7]
93
+ b43 = be[11]
94
+ b44 = be[15]
95
+
96
+ @elements[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41
97
+ @elements[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42
98
+ @elements[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43
99
+ @elements[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44
100
+
101
+ @elements[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41
102
+ @elements[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42
103
+ @elements[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43
104
+ @elements[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44
105
+
106
+ @elements[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41
107
+ @elements[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42
108
+ @elements[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43
109
+ @elements[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44
110
+
111
+ @elements[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41
112
+ @elements[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42
113
+ @elements[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43
114
+ @elements[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44
115
+
116
+ self
117
+ end
118
+
119
+ def premultiply!(other)
120
+ tmp = other * self
121
+ copy(tmp)
122
+ end
123
+
124
+ def determinant
125
+ e = @elements
126
+
127
+ n11 = e[0]
128
+ n12 = e[4]
129
+ n13 = e[8]
130
+ n14 = e[12]
131
+ n21 = e[1]
132
+ n22 = e[5]
133
+ n23 = e[9]
134
+ n24 = e[13]
135
+ n31 = e[2]
136
+ n32 = e[6]
137
+ n33 = e[10]
138
+ n34 = e[14]
139
+ n41 = e[3]
140
+ n42 = e[7]
141
+ n43 = e[11]
142
+ n44 = e[15]
143
+
144
+ n41 * (+n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34) +
145
+ n42 * (+n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31) +
146
+ n43 * (+n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31) +
147
+ n44 * (-n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31)
148
+ end
149
+
150
+ def transpose
151
+ clone.transpose!
152
+ end
153
+
154
+ def transpose!
155
+ e = @elements
156
+ tmp = e[1]
157
+ e[1] = e[4]
158
+ e[4] = tmp
159
+ tmp = e[2]
160
+ e[2] = e[8]
161
+ e[8] = tmp
162
+ tmp = e[6]
163
+ e[6] = e[9]
164
+ e[9] = tmp
165
+ tmp = e[3]
166
+ e[3] = e[12]
167
+ e[12] = tmp
168
+ tmp = e[7]
169
+ e[7] = e[13]
170
+ e[13] = tmp
171
+ tmp = e[11]
172
+ e[11] = e[14]
173
+ e[14] = tmp
174
+ self
175
+ end
176
+
177
+ def invert
178
+ clone.invert!
179
+ end
180
+
181
+ def invert!
182
+ e = @elements
183
+
184
+ n11 = e[0]
185
+ n21 = e[1]
186
+ n31 = e[2]
187
+ n41 = e[3]
188
+ n12 = e[4]
189
+ n22 = e[5]
190
+ n32 = e[6]
191
+ n42 = e[7]
192
+ n13 = e[8]
193
+ n23 = e[9]
194
+ n33 = e[10]
195
+ n43 = e[11]
196
+ n14 = e[12]
197
+ n24 = e[13]
198
+ n34 = e[14]
199
+ n44 = e[15]
200
+
201
+ t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44
202
+ t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44
203
+ t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44
204
+ t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34
205
+
206
+ det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14
207
+
208
+ return identity if det.zero?
209
+
210
+ det_inv = 1.0 / det
211
+
212
+ e[0] = t11 * det_inv
213
+ e[1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * det_inv
214
+ e[2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * det_inv
215
+ e[3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * det_inv
216
+
217
+ e[4] = t12 * det_inv
218
+ e[5] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * det_inv
219
+ e[6] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * det_inv
220
+ e[7] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * det_inv
221
+
222
+ e[8] = t13 * det_inv
223
+ e[9] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * det_inv
224
+ e[10] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * det_inv
225
+ e[11] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * det_inv
226
+
227
+ e[12] = t14 * det_inv
228
+ e[13] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * det_inv
229
+ e[14] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * det_inv
230
+ e[15] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * det_inv
231
+
232
+ self
233
+ end
234
+
235
+ def compose(position, quaternion, scale)
236
+ x = quaternion.x
237
+ y = quaternion.y
238
+ z = quaternion.z
239
+ w = quaternion.w
240
+ x2 = x + x
241
+ y2 = y + y
242
+ z2 = z + z
243
+ xx = x * x2
244
+ xy = x * y2
245
+ xz = x * z2
246
+ yy = y * y2
247
+ yz = y * z2
248
+ zz = z * z2
249
+ wx = w * x2
250
+ wy = w * y2
251
+ wz = w * z2
252
+
253
+ sx = scale.x
254
+ sy = scale.y
255
+ sz = scale.z
256
+
257
+ @elements[0] = (1 - (yy + zz)) * sx
258
+ @elements[1] = (xy + wz) * sx
259
+ @elements[2] = (xz - wy) * sx
260
+ @elements[3] = 0
261
+
262
+ @elements[4] = (xy - wz) * sy
263
+ @elements[5] = (1 - (xx + zz)) * sy
264
+ @elements[6] = (yz + wx) * sy
265
+ @elements[7] = 0
266
+
267
+ @elements[8] = (xz + wy) * sz
268
+ @elements[9] = (yz - wx) * sz
269
+ @elements[10] = (1 - (xx + yy)) * sz
270
+ @elements[11] = 0
271
+
272
+ @elements[12] = position.x
273
+ @elements[13] = position.y
274
+ @elements[14] = position.z
275
+ @elements[15] = 1
276
+
277
+ self
278
+ end
279
+
280
+ def decompose
281
+ sx = Vector3.new(@elements[0], @elements[1], @elements[2]).length
282
+ sy = Vector3.new(@elements[4], @elements[5], @elements[6]).length
283
+ sz = Vector3.new(@elements[8], @elements[9], @elements[10]).length
284
+
285
+ sx = -sx if determinant < 0
286
+
287
+ position = Vector3.new(@elements[12], @elements[13], @elements[14])
288
+
289
+ m = clone
290
+ inv_sx = 1.0 / sx
291
+ inv_sy = 1.0 / sy
292
+ inv_sz = 1.0 / sz
293
+
294
+ m.elements[0] *= inv_sx
295
+ m.elements[1] *= inv_sx
296
+ m.elements[2] *= inv_sx
297
+
298
+ m.elements[4] *= inv_sy
299
+ m.elements[5] *= inv_sy
300
+ m.elements[6] *= inv_sy
301
+
302
+ m.elements[8] *= inv_sz
303
+ m.elements[9] *= inv_sz
304
+ m.elements[10] *= inv_sz
305
+
306
+ quaternion = Quaternion.new.set_from_rotation_matrix(m)
307
+ scale = Vector3.new(sx, sy, sz)
308
+
309
+ [position, quaternion, scale]
310
+ end
311
+
312
+ def make_perspective(fov, aspect, near, far)
313
+ top = near * Math.tan(fov * 0.5 * Math::PI / 180)
314
+ height = 2 * top
315
+ width = aspect * height
316
+ left = -0.5 * width
317
+
318
+ make_perspective_bounds(left, left + width, top, top - height, near, far)
319
+ end
320
+
321
+ def make_perspective_bounds(left, right, top, bottom, near, far)
322
+ x = 2 * near / (right - left)
323
+ y = 2 * near / (top - bottom)
324
+
325
+ a = (right + left) / (right - left)
326
+ b = (top + bottom) / (top - bottom)
327
+ c = -(far + near) / (far - near)
328
+ d = -2 * far * near / (far - near)
329
+
330
+ @elements[0] = x
331
+ @elements[4] = 0
332
+ @elements[8] = a
333
+ @elements[12] = 0
334
+ @elements[1] = 0
335
+ @elements[5] = y
336
+ @elements[9] = b
337
+ @elements[13] = 0
338
+ @elements[2] = 0
339
+ @elements[6] = 0
340
+ @elements[10] = c
341
+ @elements[14] = d
342
+ @elements[3] = 0
343
+ @elements[7] = 0
344
+ @elements[11] = -1
345
+ @elements[15] = 0
346
+
347
+ self
348
+ end
349
+
350
+ def make_orthographic(left, right, top, bottom, near, far)
351
+ w = 1.0 / (right - left)
352
+ h = 1.0 / (top - bottom)
353
+ p = 1.0 / (far - near)
354
+
355
+ x = (right + left) * w
356
+ y = (top + bottom) * h
357
+ z = (far + near) * p
358
+
359
+ @elements[0] = 2 * w
360
+ @elements[4] = 0
361
+ @elements[8] = 0
362
+ @elements[12] = -x
363
+ @elements[1] = 0
364
+ @elements[5] = 2 * h
365
+ @elements[9] = 0
366
+ @elements[13] = -y
367
+ @elements[2] = 0
368
+ @elements[6] = 0
369
+ @elements[10] = -2 * p
370
+ @elements[14] = -z
371
+ @elements[3] = 0
372
+ @elements[7] = 0
373
+ @elements[11] = 0
374
+ @elements[15] = 1
375
+
376
+ self
377
+ end
378
+
379
+ def make_look_at(eye, target, up)
380
+ z = (eye - target).normalize
381
+ z = Vector3.new(0, 0, 1) if z.length_sq.zero?
382
+
383
+ x = up.cross(z).normalize
384
+ x = z.x.abs == 1 ? Vector3.new(0, 0, -z.x) : Vector3.new(1, 0, 0).cross(z).normalize if x.length_sq.zero?
385
+
386
+ y = z.cross(x)
387
+
388
+ @elements[0] = x.x
389
+ @elements[4] = y.x
390
+ @elements[8] = z.x
391
+ @elements[1] = x.y
392
+ @elements[5] = y.y
393
+ @elements[9] = z.y
394
+ @elements[2] = x.z
395
+ @elements[6] = y.z
396
+ @elements[10] = z.z
397
+
398
+ self
399
+ end
400
+
401
+ def make_rotation_from_quaternion(q)
402
+ compose(Vector3.zero, q, Vector3.one)
403
+ end
404
+
405
+ def make_translation(x, y, z)
406
+ set(
407
+ 1, 0, 0, x,
408
+ 0, 1, 0, y,
409
+ 0, 0, 1, z,
410
+ 0, 0, 0, 1
411
+ )
412
+ end
413
+
414
+ def make_rotation_x(theta)
415
+ c = Math.cos(theta)
416
+ s = Math.sin(theta)
417
+
418
+ set(
419
+ 1, 0, 0, 0,
420
+ 0, c, -s, 0,
421
+ 0, s, c, 0,
422
+ 0, 0, 0, 1
423
+ )
424
+ end
425
+
426
+ def make_rotation_y(theta)
427
+ c = Math.cos(theta)
428
+ s = Math.sin(theta)
429
+
430
+ set(
431
+ c, 0, s, 0,
432
+ 0, 1, 0, 0,
433
+ -s, 0, c, 0,
434
+ 0, 0, 0, 1
435
+ )
436
+ end
437
+
438
+ def make_rotation_z(theta)
439
+ c = Math.cos(theta)
440
+ s = Math.sin(theta)
441
+
442
+ set(
443
+ c, -s, 0, 0,
444
+ s, c, 0, 0,
445
+ 0, 0, 1, 0,
446
+ 0, 0, 0, 1
447
+ )
448
+ end
449
+
450
+ def make_scale(x, y, z)
451
+ set(
452
+ x, 0, 0, 0,
453
+ 0, y, 0, 0,
454
+ 0, 0, z, 0,
455
+ 0, 0, 0, 1
456
+ )
457
+ end
458
+
459
+ def get_position
460
+ Vector3.new(@elements[12], @elements[13], @elements[14])
461
+ end
462
+
463
+ def get_scale
464
+ sx = Vector3.new(@elements[0], @elements[1], @elements[2]).length
465
+ sy = Vector3.new(@elements[4], @elements[5], @elements[6]).length
466
+ sz = Vector3.new(@elements[8], @elements[9], @elements[10]).length
467
+ Vector3.new(sx, sy, sz)
468
+ end
469
+
470
+ def get_max_scale_on_axis
471
+ scale = get_scale
472
+ [scale.x, scale.y, scale.z].max
473
+ end
474
+
475
+ def ==(other)
476
+ return false unless other.is_a?(Matrix4)
477
+
478
+ @elements.each_with_index do |e, i|
479
+ return false if (e - other.elements[i]).abs > Float::EPSILON
480
+ end
481
+ true
482
+ end
483
+
484
+ def to_a
485
+ @elements.dup
486
+ end
487
+
488
+ def to_matrix3
489
+ e = @elements
490
+ Matrix3.new.set(
491
+ e[0], e[4], e[8],
492
+ e[1], e[5], e[9],
493
+ e[2], e[6], e[10]
494
+ )
495
+ end
496
+
497
+ def inspect
498
+ "#<Sunrb::Matrix4 #{@elements.inspect}>"
499
+ end
500
+ end
501
+ end