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,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunrb
4
+ class ConeGeometry < CylinderGeometry
5
+ def initialize(radius: 1, height: 1, radial_segments: 32, height_segments: 1,
6
+ open_ended: false, theta_start: 0, theta_length: Math::PI * 2)
7
+ super(
8
+ radius_top: 0,
9
+ radius_bottom: radius,
10
+ height: height,
11
+ radial_segments: radial_segments,
12
+ height_segments: height_segments,
13
+ open_ended: open_ended,
14
+ theta_start: theta_start,
15
+ theta_length: theta_length
16
+ )
17
+
18
+ @parameters = {
19
+ radius: radius,
20
+ height: height,
21
+ radial_segments: radial_segments,
22
+ height_segments: height_segments,
23
+ open_ended: open_ended,
24
+ theta_start: theta_start,
25
+ theta_length: theta_length
26
+ }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunrb
4
+ class CylinderGeometry < BufferGeometry
5
+ attr_reader :parameters
6
+
7
+ def initialize(radius_top: 1, radius_bottom: 1, height: 1,
8
+ radial_segments: 32, height_segments: 1,
9
+ open_ended: false, theta_start: 0, theta_length: Math::PI * 2)
10
+ super()
11
+
12
+ @parameters = {
13
+ radius_top: radius_top,
14
+ radius_bottom: radius_bottom,
15
+ height: height,
16
+ radial_segments: radial_segments,
17
+ height_segments: height_segments,
18
+ open_ended: open_ended,
19
+ theta_start: theta_start,
20
+ theta_length: theta_length
21
+ }
22
+
23
+ @indices = []
24
+ @vertices = []
25
+ @normals = []
26
+ @uvs = []
27
+ @index = 0
28
+
29
+ build_geometry
30
+ end
31
+
32
+ private
33
+
34
+ def build_geometry
35
+ radial_segments = [3, @parameters[:radial_segments].to_i].max
36
+ height_segments = [1, @parameters[:height_segments].to_i].max
37
+
38
+ generate_torso(radial_segments, height_segments)
39
+
40
+ unless @parameters[:open_ended]
41
+ generate_cap(true, radial_segments) if @parameters[:radius_top] > 0
42
+ generate_cap(false, radial_segments) if @parameters[:radius_bottom] > 0
43
+ end
44
+
45
+ set_index(@indices)
46
+ set_attribute(:position, Float32BufferAttribute.new(@vertices, 3))
47
+ set_attribute(:normal, Float32BufferAttribute.new(@normals, 3))
48
+ set_attribute(:uv, Float32BufferAttribute.new(@uvs, 2))
49
+ end
50
+
51
+ def generate_torso(radial_segments, height_segments)
52
+ radius_top = @parameters[:radius_top]
53
+ radius_bottom = @parameters[:radius_bottom]
54
+ height = @parameters[:height]
55
+ theta_start = @parameters[:theta_start]
56
+ theta_length = @parameters[:theta_length]
57
+
58
+ half_height = height / 2.0
59
+ slope = (radius_bottom - radius_top) / height
60
+
61
+ index_array = []
62
+
63
+ (0..height_segments).each do |y|
64
+ index_row = []
65
+ v = y.to_f / height_segments
66
+ radius = v * (radius_bottom - radius_top) + radius_top
67
+
68
+ (0..radial_segments).each do |x|
69
+ u = x.to_f / radial_segments
70
+ theta = u * theta_length + theta_start
71
+
72
+ sin_theta = Math.sin(theta)
73
+ cos_theta = Math.cos(theta)
74
+
75
+ @vertices.push(radius * sin_theta, -v * height + half_height, radius * cos_theta)
76
+
77
+ normal = Vector3.new(sin_theta, slope, cos_theta).normalize!
78
+ @normals.push(normal.x, normal.y, normal.z)
79
+
80
+ @uvs.push(u, 1 - v)
81
+
82
+ index_row.push(@index)
83
+ @index += 1
84
+ end
85
+
86
+ index_array.push(index_row)
87
+ end
88
+
89
+ (0...height_segments).each do |y|
90
+ (0...radial_segments).each do |x|
91
+ a = index_array[y][x]
92
+ b = index_array[y + 1][x]
93
+ c = index_array[y + 1][x + 1]
94
+ d = index_array[y][x + 1]
95
+
96
+ @indices.push(a, b, d)
97
+ @indices.push(b, c, d)
98
+ end
99
+ end
100
+ end
101
+
102
+ def generate_cap(top, radial_segments)
103
+ radius_top = @parameters[:radius_top]
104
+ radius_bottom = @parameters[:radius_bottom]
105
+ height = @parameters[:height]
106
+ theta_start = @parameters[:theta_start]
107
+ theta_length = @parameters[:theta_length]
108
+
109
+ half_height = height / 2.0
110
+ radius = top ? radius_top : radius_bottom
111
+ sign = top ? 1 : -1
112
+
113
+ center_index_start = @index
114
+
115
+ (0..radial_segments).each do |x|
116
+ @vertices.push(0, half_height * sign, 0)
117
+ @normals.push(0, sign, 0)
118
+ @uvs.push(0.5, 0.5)
119
+ @index += 1
120
+ end
121
+
122
+ center_index_end = @index
123
+
124
+ (0..radial_segments).each do |x|
125
+ u = x.to_f / radial_segments
126
+ theta = u * theta_length + theta_start
127
+
128
+ cos_theta = Math.cos(theta)
129
+ sin_theta = Math.sin(theta)
130
+
131
+ @vertices.push(radius * sin_theta, half_height * sign, radius * cos_theta)
132
+ @normals.push(0, sign, 0)
133
+ @uvs.push((cos_theta * 0.5) + 0.5, (sin_theta * 0.5 * sign) + 0.5)
134
+ @index += 1
135
+ end
136
+
137
+ (0...radial_segments).each do |x|
138
+ c = center_index_start + x
139
+ i = center_index_end + x
140
+
141
+ if top
142
+ @indices.push(i, i + 1, c)
143
+ else
144
+ @indices.push(i + 1, i, c)
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunrb
4
+ class PlaneGeometry < BufferGeometry
5
+ attr_reader :parameters
6
+
7
+ def initialize(width: 1, height: 1, width_segments: 1, height_segments: 1)
8
+ super()
9
+
10
+ @parameters = {
11
+ width: width,
12
+ height: height,
13
+ width_segments: width_segments,
14
+ height_segments: height_segments
15
+ }
16
+
17
+ build_geometry
18
+ end
19
+
20
+ private
21
+
22
+ def build_geometry
23
+ width = @parameters[:width]
24
+ height = @parameters[:height]
25
+ width_segments = @parameters[:width_segments].to_i
26
+ height_segments = @parameters[:height_segments].to_i
27
+
28
+ width_half = width / 2.0
29
+ height_half = height / 2.0
30
+
31
+ grid_x = width_segments
32
+ grid_y = height_segments
33
+
34
+ grid_x1 = grid_x + 1
35
+ grid_y1 = grid_y + 1
36
+
37
+ segment_width = width / grid_x
38
+ segment_height = height / grid_y
39
+
40
+ indices = []
41
+ vertices = []
42
+ normals = []
43
+ uvs = []
44
+
45
+ (0...grid_y1).each do |iy|
46
+ y = iy * segment_height - height_half
47
+
48
+ (0...grid_x1).each do |ix|
49
+ x = ix * segment_width - width_half
50
+
51
+ vertices.push(x, -y, 0)
52
+ normals.push(0, 0, 1)
53
+ uvs.push(ix.to_f / grid_x, 1 - (iy.to_f / grid_y))
54
+ end
55
+ end
56
+
57
+ (0...grid_y).each do |iy|
58
+ (0...grid_x).each do |ix|
59
+ a = ix + grid_x1 * iy
60
+ b = ix + grid_x1 * (iy + 1)
61
+ c = (ix + 1) + grid_x1 * (iy + 1)
62
+ d = (ix + 1) + grid_x1 * iy
63
+
64
+ indices.push(a, b, d)
65
+ indices.push(b, c, d)
66
+ end
67
+ end
68
+
69
+ set_index(indices)
70
+ set_attribute(:position, Float32BufferAttribute.new(vertices, 3))
71
+ set_attribute(:normal, Float32BufferAttribute.new(normals, 3))
72
+ set_attribute(:uv, Float32BufferAttribute.new(uvs, 2))
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunrb
4
+ class SphereGeometry < BufferGeometry
5
+ attr_reader :parameters
6
+
7
+ def initialize(radius: 1, width_segments: 32, height_segments: 16,
8
+ phi_start: 0, phi_length: Math::PI * 2,
9
+ theta_start: 0, theta_length: Math::PI)
10
+ super()
11
+
12
+ @parameters = {
13
+ radius: radius,
14
+ width_segments: width_segments,
15
+ height_segments: height_segments,
16
+ phi_start: phi_start,
17
+ phi_length: phi_length,
18
+ theta_start: theta_start,
19
+ theta_length: theta_length
20
+ }
21
+
22
+ build_geometry
23
+ end
24
+
25
+ private
26
+
27
+ def build_geometry
28
+ radius = @parameters[:radius]
29
+ width_segments = [3, @parameters[:width_segments].to_i].max
30
+ height_segments = [2, @parameters[:height_segments].to_i].max
31
+ phi_start = @parameters[:phi_start]
32
+ phi_length = @parameters[:phi_length]
33
+ theta_start = @parameters[:theta_start]
34
+ theta_length = @parameters[:theta_length]
35
+
36
+ theta_end = [theta_start + theta_length, Math::PI].min
37
+
38
+ indices = []
39
+ vertices = []
40
+ normals = []
41
+ uvs = []
42
+
43
+ index = 0
44
+ grid = []
45
+
46
+ (0..height_segments).each do |iy|
47
+ vertices_row = []
48
+ v = iy.to_f / height_segments
49
+
50
+ u_offset = 0
51
+ u_offset = 0.5 / width_segments if iy.zero? && theta_start.zero?
52
+ u_offset = -0.5 / width_segments if iy == height_segments && theta_end >= Math::PI
53
+
54
+ (0..width_segments).each do |ix|
55
+ u = ix.to_f / width_segments
56
+
57
+ x = -radius * Math.cos(phi_start + u * phi_length) * Math.sin(theta_start + v * theta_length)
58
+ y = radius * Math.cos(theta_start + v * theta_length)
59
+ z = radius * Math.sin(phi_start + u * phi_length) * Math.sin(theta_start + v * theta_length)
60
+
61
+ vertices.push(x, y, z)
62
+
63
+ normal = Vector3.new(x, y, z).normalize!
64
+ normals.push(normal.x, normal.y, normal.z)
65
+
66
+ uvs.push(u + u_offset, 1 - v)
67
+
68
+ vertices_row.push(index)
69
+ index += 1
70
+ end
71
+
72
+ grid.push(vertices_row)
73
+ end
74
+
75
+ (0...height_segments).each do |iy|
76
+ (0...width_segments).each do |ix|
77
+ a = grid[iy][ix + 1]
78
+ b = grid[iy][ix]
79
+ c = grid[iy + 1][ix]
80
+ d = grid[iy + 1][ix + 1]
81
+
82
+ indices.push(a, b, d) if iy != 0 || theta_start > 0
83
+ indices.push(b, c, d) if iy != height_segments - 1 || theta_end < Math::PI
84
+ end
85
+ end
86
+
87
+ set_index(indices)
88
+ set_attribute(:position, Float32BufferAttribute.new(vertices, 3))
89
+ set_attribute(:normal, Float32BufferAttribute.new(normals, 3))
90
+ set_attribute(:uv, Float32BufferAttribute.new(uvs, 2))
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunrb
4
+ class TorusGeometry < BufferGeometry
5
+ attr_reader :parameters
6
+
7
+ def initialize(radius: 1, tube: 0.4, radial_segments: 12, tubular_segments: 48, arc: Math::PI * 2)
8
+ super()
9
+
10
+ @parameters = {
11
+ radius: radius,
12
+ tube: tube,
13
+ radial_segments: radial_segments,
14
+ tubular_segments: tubular_segments,
15
+ arc: arc
16
+ }
17
+
18
+ build_geometry
19
+ end
20
+
21
+ private
22
+
23
+ def build_geometry
24
+ radius = @parameters[:radius]
25
+ tube = @parameters[:tube]
26
+ radial_segments = [3, @parameters[:radial_segments].to_i].max
27
+ tubular_segments = [3, @parameters[:tubular_segments].to_i].max
28
+ arc = @parameters[:arc]
29
+
30
+ indices = []
31
+ vertices = []
32
+ normals = []
33
+ uvs = []
34
+
35
+ center = Vector3.new
36
+
37
+ (0..radial_segments).each do |j|
38
+ (0..tubular_segments).each do |i|
39
+ u = i.to_f / tubular_segments * arc
40
+ v = j.to_f / radial_segments * Math::PI * 2
41
+
42
+ x = (radius + tube * Math.cos(v)) * Math.cos(u)
43
+ y = (radius + tube * Math.cos(v)) * Math.sin(u)
44
+ z = tube * Math.sin(v)
45
+
46
+ vertices.push(x, y, z)
47
+
48
+ center.x = radius * Math.cos(u)
49
+ center.y = radius * Math.sin(u)
50
+
51
+ normal = Vector3.new(x - center.x, y - center.y, z).normalize!
52
+ normals.push(normal.x, normal.y, normal.z)
53
+
54
+ uvs.push(i.to_f / tubular_segments)
55
+ uvs.push(j.to_f / radial_segments)
56
+ end
57
+ end
58
+
59
+ (1..radial_segments).each do |j|
60
+ (1..tubular_segments).each do |i|
61
+ a = (tubular_segments + 1) * j + i - 1
62
+ b = (tubular_segments + 1) * (j - 1) + i - 1
63
+ c = (tubular_segments + 1) * (j - 1) + i
64
+ d = (tubular_segments + 1) * j + i
65
+
66
+ indices.push(a, b, d)
67
+ indices.push(b, c, d)
68
+ end
69
+ end
70
+
71
+ set_index(indices)
72
+ set_attribute(:position, Float32BufferAttribute.new(vertices, 3))
73
+ set_attribute(:normal, Float32BufferAttribute.new(normals, 3))
74
+ set_attribute(:uv, Float32BufferAttribute.new(uvs, 2))
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunrb
4
+ class AmbientLight < Light
5
+ def initialize(color: 0xffffff, intensity: 1.0)
6
+ super(color: color, intensity: intensity)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunrb
4
+ class DirectionalLight < Light
5
+ attr_accessor :target
6
+ attr_accessor :shadow
7
+
8
+ def initialize(color: 0xffffff, intensity: 1.0)
9
+ super(color: color, intensity: intensity)
10
+ @target = Object3D.new
11
+ @shadow = DirectionalLightShadow.new
12
+ end
13
+
14
+ def copy(source, recursive = true)
15
+ super
16
+ @target = source.target.clone
17
+ @shadow = source.shadow.clone
18
+ self
19
+ end
20
+
21
+ def dispose
22
+ @shadow&.dispose
23
+ end
24
+
25
+ def to_h
26
+ super.merge(
27
+ target: @target.position.to_a
28
+ )
29
+ end
30
+ end
31
+
32
+ class DirectionalLightShadow
33
+ attr_accessor :camera, :bias, :normal_bias, :radius
34
+ attr_accessor :map_size
35
+
36
+ def initialize
37
+ @camera = OrthographicCamera.new(left: -5, right: 5, top: 5, bottom: -5, near: 0.5, far: 500)
38
+ @bias = 0
39
+ @normal_bias = 0
40
+ @radius = 1
41
+ @map_size = Vector2.new(512, 512)
42
+ end
43
+
44
+ def clone
45
+ shadow = DirectionalLightShadow.new
46
+ shadow.camera = @camera.clone
47
+ shadow.bias = @bias
48
+ shadow.normal_bias = @normal_bias
49
+ shadow.radius = @radius
50
+ shadow.map_size = @map_size.clone
51
+ shadow
52
+ end
53
+
54
+ def dispose
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunrb
4
+ class HemisphereLight < Light
5
+ attr_accessor :ground_color
6
+
7
+ def initialize(sky_color: 0xffffff, ground_color: 0xffffff, intensity: 1.0)
8
+ super(color: sky_color, intensity: intensity)
9
+ @ground_color = ground_color.is_a?(Color) ? ground_color : Color.new(ground_color)
10
+ @position.copy(Object3D.new.up)
11
+ update_matrix
12
+ end
13
+
14
+ def copy(source, recursive = true)
15
+ super
16
+ @ground_color = source.ground_color.clone
17
+ self
18
+ end
19
+
20
+ def to_h
21
+ super.merge(
22
+ ground_color: @ground_color.get_hex
23
+ )
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunrb
4
+ class Light < Object3D
5
+ attr_accessor :color, :intensity
6
+
7
+ def initialize(color: 0xffffff, intensity: 1.0)
8
+ super()
9
+ @color = color.is_a?(Color) ? color : Color.new(color)
10
+ @intensity = intensity.to_f
11
+ end
12
+
13
+ def copy(source, recursive = true)
14
+ super
15
+ @color = source.color.clone
16
+ @intensity = source.intensity
17
+ self
18
+ end
19
+
20
+ def to_h
21
+ super.merge(
22
+ color: @color.get_hex,
23
+ intensity: @intensity
24
+ )
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunrb
4
+ class PointLight < Light
5
+ attr_accessor :distance, :decay
6
+ attr_accessor :shadow
7
+
8
+ def initialize(color: 0xffffff, intensity: 1.0, distance: 0, decay: 2)
9
+ super(color: color, intensity: intensity)
10
+ @distance = distance.to_f
11
+ @decay = decay.to_f
12
+ @shadow = PointLightShadow.new
13
+ end
14
+
15
+ def power
16
+ @intensity * 4 * Math::PI
17
+ end
18
+
19
+ def power=(value)
20
+ @intensity = value / (4 * Math::PI)
21
+ end
22
+
23
+ def copy(source, recursive = true)
24
+ super
25
+ @distance = source.distance
26
+ @decay = source.decay
27
+ @shadow = source.shadow.clone
28
+ self
29
+ end
30
+
31
+ def dispose
32
+ @shadow&.dispose
33
+ end
34
+
35
+ def to_h
36
+ super.merge(
37
+ distance: @distance,
38
+ decay: @decay
39
+ )
40
+ end
41
+ end
42
+
43
+ class PointLightShadow
44
+ attr_accessor :camera, :bias, :normal_bias, :radius
45
+ attr_accessor :map_size
46
+
47
+ def initialize
48
+ @camera = PerspectiveCamera.new(fov: 90, aspect: 1, near: 0.5, far: 500)
49
+ @bias = 0
50
+ @normal_bias = 0
51
+ @radius = 1
52
+ @map_size = Vector2.new(512, 512)
53
+ end
54
+
55
+ def clone
56
+ shadow = PointLightShadow.new
57
+ shadow.camera = @camera.clone
58
+ shadow.bias = @bias
59
+ shadow.normal_bias = @normal_bias
60
+ shadow.radius = @radius
61
+ shadow.map_size = @map_size.clone
62
+ shadow
63
+ end
64
+
65
+ def dispose
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sunrb
4
+ class RectAreaLight < Light
5
+ attr_accessor :width, :height
6
+
7
+ def initialize(color: 0xffffff, intensity: 1.0, width: 10, height: 10)
8
+ super(color: color, intensity: intensity)
9
+ @width = width.to_f
10
+ @height = height.to_f
11
+ end
12
+
13
+ def power
14
+ @intensity * @width * @height * Math::PI
15
+ end
16
+
17
+ def power=(value)
18
+ @intensity = value / (@width * @height * Math::PI)
19
+ end
20
+
21
+ def copy(source, recursive = true)
22
+ super
23
+ @width = source.width
24
+ @height = source.height
25
+ self
26
+ end
27
+
28
+ def to_h
29
+ super.merge(
30
+ width: @width,
31
+ height: @height
32
+ )
33
+ end
34
+ end
35
+ end