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.
- checksums.yaml +7 -0
- data/.rspec +2 -0
- data/3rb.gemspec +29 -0
- data/CHANGELOG.md +12 -0
- data/LICENSE +21 -0
- data/README.md +321 -0
- data/Rakefile +13 -0
- data/examples/01_hello_cube.rb +29 -0
- data/examples/02_basic_geometries.rb +56 -0
- data/examples/03_materials.rb +61 -0
- data/examples/04_lighting.rb +63 -0
- data/examples/05_animation.rb +79 -0
- data/examples/06_custom_shader.rb +92 -0
- data/examples/07_scene_graph.rb +74 -0
- data/examples/08_orbit_controls.rb +50 -0
- data/examples/09_3d_chart.rb +71 -0
- data/examples/10_procedural_terrain.rb +140 -0
- data/examples/11_particle_system.rb +68 -0
- data/examples/12_model_loader.rb +73 -0
- data/examples/13_game_prototype.rb +145 -0
- data/examples/14_utah_teapot.rb +291 -0
- data/examples/15_stanford_bunny.rb +200 -0
- data/examples/16_cornell_box.rb +373 -0
- data/examples/17_weird_fractal4.rb +130 -0
- data/examples/18_platonic_solids.rb +268 -0
- data/lib/3rb/animation/animation_clip.rb +287 -0
- data/lib/3rb/animation/animation_mixer.rb +366 -0
- data/lib/3rb/cameras/camera.rb +50 -0
- data/lib/3rb/cameras/orthographic_camera.rb +92 -0
- data/lib/3rb/cameras/perspective_camera.rb +103 -0
- data/lib/3rb/controls/orbit_controls.rb +341 -0
- data/lib/3rb/core/buffer_attribute.rb +172 -0
- data/lib/3rb/core/group.rb +9 -0
- data/lib/3rb/core/object3d.rb +298 -0
- data/lib/3rb/core/scene.rb +78 -0
- data/lib/3rb/dsl/helpers.rb +57 -0
- data/lib/3rb/dsl/scene_builder.rb +288 -0
- data/lib/3rb/ffi/glfw.rb +61 -0
- data/lib/3rb/ffi/opengl.rb +137 -0
- data/lib/3rb/ffi/platform.rb +65 -0
- data/lib/3rb/geometries/box_geometry.rb +101 -0
- data/lib/3rb/geometries/buffer_geometry.rb +345 -0
- data/lib/3rb/geometries/cone_geometry.rb +29 -0
- data/lib/3rb/geometries/cylinder_geometry.rb +149 -0
- data/lib/3rb/geometries/plane_geometry.rb +75 -0
- data/lib/3rb/geometries/sphere_geometry.rb +93 -0
- data/lib/3rb/geometries/torus_geometry.rb +77 -0
- data/lib/3rb/lights/ambient_light.rb +9 -0
- data/lib/3rb/lights/directional_light.rb +57 -0
- data/lib/3rb/lights/hemisphere_light.rb +26 -0
- data/lib/3rb/lights/light.rb +27 -0
- data/lib/3rb/lights/point_light.rb +68 -0
- data/lib/3rb/lights/rect_area_light.rb +35 -0
- data/lib/3rb/lights/spot_light.rb +88 -0
- data/lib/3rb/loaders/gltf_loader.rb +304 -0
- data/lib/3rb/loaders/loader.rb +94 -0
- data/lib/3rb/loaders/obj_loader.rb +186 -0
- data/lib/3rb/loaders/texture_loader.rb +55 -0
- data/lib/3rb/materials/basic_material.rb +70 -0
- data/lib/3rb/materials/lambert_material.rb +102 -0
- data/lib/3rb/materials/material.rb +114 -0
- data/lib/3rb/materials/phong_material.rb +106 -0
- data/lib/3rb/materials/shader_material.rb +104 -0
- data/lib/3rb/materials/standard_material.rb +106 -0
- data/lib/3rb/math/color.rb +246 -0
- data/lib/3rb/math/euler.rb +156 -0
- data/lib/3rb/math/math_utils.rb +132 -0
- data/lib/3rb/math/matrix3.rb +269 -0
- data/lib/3rb/math/matrix4.rb +501 -0
- data/lib/3rb/math/quaternion.rb +337 -0
- data/lib/3rb/math/vector2.rb +216 -0
- data/lib/3rb/math/vector3.rb +366 -0
- data/lib/3rb/math/vector4.rb +233 -0
- data/lib/3rb/native/gl.rb +382 -0
- data/lib/3rb/native/native.rb +55 -0
- data/lib/3rb/native/window.rb +111 -0
- data/lib/3rb/native.rb +9 -0
- data/lib/3rb/objects/line.rb +116 -0
- data/lib/3rb/objects/mesh.rb +40 -0
- data/lib/3rb/objects/points.rb +71 -0
- data/lib/3rb/renderers/opengl_renderer.rb +567 -0
- data/lib/3rb/renderers/renderer.rb +60 -0
- data/lib/3rb/renderers/shader_lib.rb +100 -0
- data/lib/3rb/textures/cube_texture.rb +26 -0
- data/lib/3rb/textures/data_texture.rb +35 -0
- data/lib/3rb/textures/render_target.rb +125 -0
- data/lib/3rb/textures/texture.rb +190 -0
- data/lib/3rb/version.rb +5 -0
- data/lib/3rb.rb +86 -0
- data/shaders/basic.frag +19 -0
- data/shaders/basic.vert +15 -0
- data/shaders/common/lights.glsl +53 -0
- data/shaders/common/uniforms.glsl +9 -0
- data/shaders/lambert.frag +37 -0
- data/shaders/lambert.vert +22 -0
- data/shaders/phong.frag +51 -0
- data/shaders/phong.vert +28 -0
- data/shaders/standard.frag +92 -0
- data/shaders/standard.vert +28 -0
- 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,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
|