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,337 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunrb
4
+ class Quaternion
5
+ attr_accessor :x, :y, :z, :w
6
+
7
+ def initialize(x = 0, y = 0, z = 0, w = 1)
8
+ @x = x.to_f
9
+ @y = y.to_f
10
+ @z = z.to_f
11
+ @w = w.to_f
12
+ end
13
+
14
+ def set(x, y, z, w)
15
+ @x = x.to_f
16
+ @y = y.to_f
17
+ @z = z.to_f
18
+ @w = w.to_f
19
+ self
20
+ end
21
+
22
+ def copy(q)
23
+ @x = q.x
24
+ @y = q.y
25
+ @z = q.z
26
+ @w = q.w
27
+ self
28
+ end
29
+
30
+ def clone
31
+ Quaternion.new(@x, @y, @z, @w)
32
+ end
33
+
34
+ def identity
35
+ set(0, 0, 0, 1)
36
+ end
37
+
38
+ def *(other)
39
+ clone.multiply!(other)
40
+ end
41
+
42
+ def multiply!(other)
43
+ qax = @x
44
+ qay = @y
45
+ qaz = @z
46
+ qaw = @w
47
+ qbx = other.x
48
+ qby = other.y
49
+ qbz = other.z
50
+ qbw = other.w
51
+
52
+ @x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby
53
+ @y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz
54
+ @z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx
55
+ @w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz
56
+
57
+ self
58
+ end
59
+
60
+ def premultiply!(other)
61
+ tmp = other * self
62
+ copy(tmp)
63
+ end
64
+
65
+ def dot(other)
66
+ @x * other.x + @y * other.y + @z * other.z + @w * other.w
67
+ end
68
+
69
+ def length_sq
70
+ @x * @x + @y * @y + @z * @z + @w * @w
71
+ end
72
+
73
+ def length
74
+ Math.sqrt(length_sq)
75
+ end
76
+
77
+ def normalize
78
+ clone.normalize!
79
+ end
80
+
81
+ def normalize!
82
+ len = length
83
+ return identity if len.zero?
84
+
85
+ len = 1.0 / len
86
+ @x *= len
87
+ @y *= len
88
+ @z *= len
89
+ @w *= len
90
+ self
91
+ end
92
+
93
+ def conjugate
94
+ clone.conjugate!
95
+ end
96
+
97
+ def conjugate!
98
+ @x = -@x
99
+ @y = -@y
100
+ @z = -@z
101
+ self
102
+ end
103
+
104
+ def invert
105
+ clone.invert!
106
+ end
107
+
108
+ def invert!
109
+ conjugate!.normalize!
110
+ end
111
+
112
+ def slerp(other, t)
113
+ return clone if t.zero?
114
+ return other.clone if t == 1
115
+
116
+ x = @x
117
+ y = @y
118
+ z = @z
119
+ w = @w
120
+
121
+ cos_half_theta = w * other.w + x * other.x + y * other.y + z * other.z
122
+
123
+ if cos_half_theta < 0
124
+ @w = -other.w
125
+ @x = -other.x
126
+ @y = -other.y
127
+ @z = -other.z
128
+ cos_half_theta = -cos_half_theta
129
+ else
130
+ copy(other)
131
+ end
132
+
133
+ if cos_half_theta >= 1.0
134
+ @w = w
135
+ @x = x
136
+ @y = y
137
+ @z = z
138
+ return self
139
+ end
140
+
141
+ sqr_sin_half_theta = 1.0 - cos_half_theta * cos_half_theta
142
+
143
+ if sqr_sin_half_theta <= Float::EPSILON
144
+ s = 1 - t
145
+ @w = s * w + t * @w
146
+ @x = s * x + t * @x
147
+ @y = s * y + t * @y
148
+ @z = s * z + t * @z
149
+ normalize!
150
+ return self
151
+ end
152
+
153
+ sin_half_theta = Math.sqrt(sqr_sin_half_theta)
154
+ half_theta = Math.atan2(sin_half_theta, cos_half_theta)
155
+ ratio_a = Math.sin((1 - t) * half_theta) / sin_half_theta
156
+ ratio_b = Math.sin(t * half_theta) / sin_half_theta
157
+
158
+ @w = w * ratio_a + @w * ratio_b
159
+ @x = x * ratio_a + @x * ratio_b
160
+ @y = y * ratio_a + @y * ratio_b
161
+ @z = z * ratio_a + @z * ratio_b
162
+
163
+ self
164
+ end
165
+
166
+ def set_from_axis_angle(axis, angle)
167
+ half_angle = angle / 2.0
168
+ s = Math.sin(half_angle)
169
+
170
+ @x = axis.x * s
171
+ @y = axis.y * s
172
+ @z = axis.z * s
173
+ @w = Math.cos(half_angle)
174
+
175
+ self
176
+ end
177
+
178
+ def set_from_euler(euler, update = true)
179
+ x = euler.x
180
+ y = euler.y
181
+ z = euler.z
182
+ order = euler.order
183
+
184
+ c1 = Math.cos(x / 2)
185
+ c2 = Math.cos(y / 2)
186
+ c3 = Math.cos(z / 2)
187
+
188
+ s1 = Math.sin(x / 2)
189
+ s2 = Math.sin(y / 2)
190
+ s3 = Math.sin(z / 2)
191
+
192
+ case order
193
+ when "XYZ"
194
+ @x = s1 * c2 * c3 + c1 * s2 * s3
195
+ @y = c1 * s2 * c3 - s1 * c2 * s3
196
+ @z = c1 * c2 * s3 + s1 * s2 * c3
197
+ @w = c1 * c2 * c3 - s1 * s2 * s3
198
+ when "YXZ"
199
+ @x = s1 * c2 * c3 + c1 * s2 * s3
200
+ @y = c1 * s2 * c3 - s1 * c2 * s3
201
+ @z = c1 * c2 * s3 - s1 * s2 * c3
202
+ @w = c1 * c2 * c3 + s1 * s2 * s3
203
+ when "ZXY"
204
+ @x = s1 * c2 * c3 - c1 * s2 * s3
205
+ @y = c1 * s2 * c3 + s1 * c2 * s3
206
+ @z = c1 * c2 * s3 + s1 * s2 * c3
207
+ @w = c1 * c2 * c3 - s1 * s2 * s3
208
+ when "ZYX"
209
+ @x = s1 * c2 * c3 - c1 * s2 * s3
210
+ @y = c1 * s2 * c3 + s1 * c2 * s3
211
+ @z = c1 * c2 * s3 - s1 * s2 * c3
212
+ @w = c1 * c2 * c3 + s1 * s2 * s3
213
+ when "YZX"
214
+ @x = s1 * c2 * c3 + c1 * s2 * s3
215
+ @y = c1 * s2 * c3 + s1 * c2 * s3
216
+ @z = c1 * c2 * s3 - s1 * s2 * c3
217
+ @w = c1 * c2 * c3 - s1 * s2 * s3
218
+ when "XZY"
219
+ @x = s1 * c2 * c3 - c1 * s2 * s3
220
+ @y = c1 * s2 * c3 - s1 * c2 * s3
221
+ @z = c1 * c2 * s3 + s1 * s2 * c3
222
+ @w = c1 * c2 * c3 + s1 * s2 * s3
223
+ end
224
+
225
+ self
226
+ end
227
+
228
+ def set_from_rotation_matrix(m)
229
+ e = m.elements
230
+
231
+ m11 = e[0]
232
+ m12 = e[4]
233
+ m13 = e[8]
234
+ m21 = e[1]
235
+ m22 = e[5]
236
+ m23 = e[9]
237
+ m31 = e[2]
238
+ m32 = e[6]
239
+ m33 = e[10]
240
+
241
+ trace = m11 + m22 + m33
242
+
243
+ if trace > 0
244
+ s = 0.5 / Math.sqrt(trace + 1.0)
245
+ @w = 0.25 / s
246
+ @x = (m32 - m23) * s
247
+ @y = (m13 - m31) * s
248
+ @z = (m21 - m12) * s
249
+ elsif m11 > m22 && m11 > m33
250
+ s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33)
251
+ @w = (m32 - m23) / s
252
+ @x = 0.25 * s
253
+ @y = (m12 + m21) / s
254
+ @z = (m13 + m31) / s
255
+ elsif m22 > m33
256
+ s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33)
257
+ @w = (m13 - m31) / s
258
+ @x = (m12 + m21) / s
259
+ @y = 0.25 * s
260
+ @z = (m23 + m32) / s
261
+ else
262
+ s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22)
263
+ @w = (m21 - m12) / s
264
+ @x = (m13 + m31) / s
265
+ @y = (m23 + m32) / s
266
+ @z = 0.25 * s
267
+ end
268
+
269
+ self
270
+ end
271
+
272
+ def set_from_unit_vectors(from, to)
273
+ r = from.dot(to) + 1
274
+
275
+ if r < Float::EPSILON
276
+ r = 0
277
+ if from.x.abs > from.z.abs
278
+ @x = -from.y
279
+ @y = from.x
280
+ @z = 0
281
+ @w = r
282
+ else
283
+ @x = 0
284
+ @y = -from.z
285
+ @z = from.y
286
+ @w = r
287
+ end
288
+ else
289
+ @x = from.y * to.z - from.z * to.y
290
+ @y = from.z * to.x - from.x * to.z
291
+ @z = from.x * to.y - from.y * to.x
292
+ @w = r
293
+ end
294
+
295
+ normalize!
296
+ end
297
+
298
+ def angle_to(q)
299
+ 2 * Math.acos([dot(q).abs, 1].min)
300
+ end
301
+
302
+ def rotate_towards(q, step)
303
+ angle = angle_to(q)
304
+ return self if angle.zero?
305
+
306
+ t = [1, step / angle].min
307
+ slerp(q, t)
308
+ end
309
+
310
+ def ==(other)
311
+ return false unless other.is_a?(Quaternion)
312
+
313
+ (@x - other.x).abs < Float::EPSILON &&
314
+ (@y - other.y).abs < Float::EPSILON &&
315
+ (@z - other.z).abs < Float::EPSILON &&
316
+ (@w - other.w).abs < Float::EPSILON
317
+ end
318
+
319
+ def to_a
320
+ [@x, @y, @z, @w]
321
+ end
322
+
323
+ def to_h
324
+ { x: @x, y: @y, z: @z, w: @w }
325
+ end
326
+
327
+ def inspect
328
+ "#<Sunrb::Quaternion x=#{@x} y=#{@y} z=#{@z} w=#{@w}>"
329
+ end
330
+
331
+ class << self
332
+ def identity
333
+ new(0, 0, 0, 1)
334
+ end
335
+ end
336
+ end
337
+ end
@@ -0,0 +1,216 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunrb
4
+ class Vector2
5
+ attr_accessor :x, :y
6
+
7
+ def initialize(x = 0, y = 0)
8
+ @x = x.to_f
9
+ @y = y.to_f
10
+ end
11
+
12
+ def set(x, y)
13
+ @x = x.to_f
14
+ @y = y.to_f
15
+ self
16
+ end
17
+
18
+ def set_scalar(scalar)
19
+ @x = @y = scalar.to_f
20
+ self
21
+ end
22
+
23
+ def copy(v)
24
+ @x = v.x
25
+ @y = v.y
26
+ self
27
+ end
28
+
29
+ def clone
30
+ Vector2.new(@x, @y)
31
+ end
32
+
33
+ def +(other)
34
+ Vector2.new(@x + other.x, @y + other.y)
35
+ end
36
+
37
+ def -(other)
38
+ Vector2.new(@x - other.x, @y - other.y)
39
+ end
40
+
41
+ def *(scalar)
42
+ Vector2.new(@x * scalar, @y * scalar)
43
+ end
44
+
45
+ def /(scalar)
46
+ Vector2.new(@x / scalar, @y / scalar)
47
+ end
48
+
49
+ def -@
50
+ Vector2.new(-@x, -@y)
51
+ end
52
+
53
+ def add!(other)
54
+ @x += other.x
55
+ @y += other.y
56
+ self
57
+ end
58
+
59
+ def sub!(other)
60
+ @x -= other.x
61
+ @y -= other.y
62
+ self
63
+ end
64
+
65
+ def multiply_scalar!(scalar)
66
+ @x *= scalar
67
+ @y *= scalar
68
+ self
69
+ end
70
+
71
+ def divide_scalar!(scalar)
72
+ @x /= scalar
73
+ @y /= scalar
74
+ self
75
+ end
76
+
77
+ def dot(other)
78
+ @x * other.x + @y * other.y
79
+ end
80
+
81
+ def cross(other)
82
+ @x * other.y - @y * other.x
83
+ end
84
+
85
+ def length_sq
86
+ @x * @x + @y * @y
87
+ end
88
+
89
+ def length
90
+ Math.sqrt(length_sq)
91
+ end
92
+
93
+ def normalize
94
+ clone.normalize!
95
+ end
96
+
97
+ def normalize!
98
+ len = length
99
+ return self if len.zero?
100
+
101
+ divide_scalar!(len)
102
+ end
103
+
104
+ def distance_to(other)
105
+ (self - other).length
106
+ end
107
+
108
+ def distance_to_sq(other)
109
+ (self - other).length_sq
110
+ end
111
+
112
+ def lerp(other, alpha)
113
+ Vector2.new(
114
+ @x + (other.x - @x) * alpha,
115
+ @y + (other.y - @y) * alpha
116
+ )
117
+ end
118
+
119
+ def lerp!(other, alpha)
120
+ @x += (other.x - @x) * alpha
121
+ @y += (other.y - @y) * alpha
122
+ self
123
+ end
124
+
125
+ def negate!
126
+ @x = -@x
127
+ @y = -@y
128
+ self
129
+ end
130
+
131
+ def min(other)
132
+ Vector2.new(
133
+ [@x, other.x].min,
134
+ [@y, other.y].min
135
+ )
136
+ end
137
+
138
+ def max(other)
139
+ Vector2.new(
140
+ [@x, other.x].max,
141
+ [@y, other.y].max
142
+ )
143
+ end
144
+
145
+ def clamp(min_vec, max_vec)
146
+ Vector2.new(
147
+ [[@x, min_vec.x].max, max_vec.x].min,
148
+ [[@y, min_vec.y].max, max_vec.y].min
149
+ )
150
+ end
151
+
152
+ def floor
153
+ Vector2.new(@x.floor, @y.floor)
154
+ end
155
+
156
+ def ceil
157
+ Vector2.new(@x.ceil, @y.ceil)
158
+ end
159
+
160
+ def round
161
+ Vector2.new(@x.round, @y.round)
162
+ end
163
+
164
+ def angle
165
+ Math.atan2(-@y, -@x) + Math::PI
166
+ end
167
+
168
+ def rotate_around(center, angle)
169
+ cos = Math.cos(angle)
170
+ sin = Math.sin(angle)
171
+ x = @x - center.x
172
+ y = @y - center.y
173
+
174
+ Vector2.new(
175
+ x * cos - y * sin + center.x,
176
+ x * sin + y * cos + center.y
177
+ )
178
+ end
179
+
180
+ def ==(other)
181
+ return false unless other.is_a?(Vector2)
182
+
183
+ (@x - other.x).abs < Float::EPSILON &&
184
+ (@y - other.y).abs < Float::EPSILON
185
+ end
186
+
187
+ def equals?(other, tolerance = Float::EPSILON)
188
+ return false unless other.is_a?(Vector2)
189
+
190
+ (@x - other.x).abs <= tolerance &&
191
+ (@y - other.y).abs <= tolerance
192
+ end
193
+
194
+ def to_a
195
+ [@x, @y]
196
+ end
197
+
198
+ def to_h
199
+ { x: @x, y: @y }
200
+ end
201
+
202
+ def inspect
203
+ "#<Sunrb::Vector2 x=#{@x} y=#{@y}>"
204
+ end
205
+
206
+ class << self
207
+ def zero
208
+ new(0, 0)
209
+ end
210
+
211
+ def one
212
+ new(1, 1)
213
+ end
214
+ end
215
+ end
216
+ end