rbgl 0.1.0 → 1.0.1
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 +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +20 -2
- data/README.md +130 -74
- data/Rakefile +7 -0
- data/bench/renderer_bench.rb +283 -0
- data/docs/.nojekyll +1 -0
- data/docs/index.html +38 -0
- data/examples/array_test.rb +1 -1
- data/examples/color_test.rb +1 -1
- data/examples/cube_spinning.rb +1 -1
- data/examples/gradient.rb +1 -1
- data/examples/multi_return_test.rb +1 -1
- data/examples/plasma.rb +1 -1
- data/examples/sphere_raymarch.rb +1 -1
- data/examples/triangle_window.rb +1 -1
- data/exe/rbgl +42 -0
- data/lib/rbgl/cli/doctor.rb +155 -0
- data/lib/rbgl/engine/buffer.rb +150 -40
- data/lib/rbgl/engine/clip_space_clipper.rb +163 -0
- data/lib/rbgl/engine/context.rb +133 -63
- data/lib/rbgl/engine/framebuffer.rb +153 -48
- data/lib/rbgl/engine/immutable_color.rb +32 -0
- data/lib/rbgl/engine/pipeline.rb +38 -7
- data/lib/rbgl/engine/rasterizer/attribute_interpolator.rb +195 -0
- data/lib/rbgl/engine/rasterizer/line_renderer.rb +88 -0
- data/lib/rbgl/engine/rasterizer/point_renderer.rb +35 -0
- data/lib/rbgl/engine/rasterizer/triangle_renderer.rb +136 -0
- data/lib/rbgl/engine/rasterizer.rb +65 -171
- data/lib/rbgl/engine/shader/base_shader.rb +51 -0
- data/lib/rbgl/engine/shader/builtins.rb +257 -0
- data/lib/rbgl/engine/shader/dynamic_data.rb +73 -0
- data/lib/rbgl/engine/shader.rb +5 -318
- data/lib/rbgl/engine/texture.rb +264 -51
- data/lib/rbgl/engine.rb +4 -1
- data/lib/rbgl/gui/backend.rb +21 -34
- data/lib/rbgl/gui/backend_factory.rb +89 -0
- data/lib/rbgl/gui/cocoa/backend.rb +59 -46
- data/lib/rbgl/gui/cocoa/key_mapper.rb +48 -0
- data/lib/rbgl/gui/event.rb +4 -2
- data/lib/rbgl/gui/file_backend/bmp_writer.rb +63 -0
- data/lib/rbgl/gui/file_backend/frame_writer.rb +35 -0
- data/lib/rbgl/gui/file_backend/png_writer.rb +18 -0
- data/lib/rbgl/gui/file_backend/ppm_writer.rb +26 -0
- data/lib/rbgl/gui/file_backend.rb +50 -55
- data/lib/rbgl/gui/wayland/backend.rb +214 -60
- data/lib/rbgl/gui/wayland/codec.rb +50 -0
- data/lib/rbgl/gui/wayland/connection.rb +188 -231
- data/lib/rbgl/gui/wayland/event_dispatcher.rb +165 -0
- data/lib/rbgl/gui/wayland/global_binder.rb +46 -0
- data/lib/rbgl/gui/wayland/input_mapper.rb +61 -0
- data/lib/rbgl/gui/wayland/protocol_objects/arguments.rb +63 -0
- data/lib/rbgl/gui/wayland/protocol_objects/display.rb +54 -0
- data/lib/rbgl/gui/wayland/protocol_objects/seat.rb +115 -0
- data/lib/rbgl/gui/wayland/protocol_objects/shm.rb +157 -0
- data/lib/rbgl/gui/wayland/protocol_objects/surface.rb +33 -0
- data/lib/rbgl/gui/wayland/protocol_objects/xdg_shell.rb +60 -0
- data/lib/rbgl/gui/wayland/protocol_objects.rb +8 -0
- data/lib/rbgl/gui/window/event_dispatcher.rb +30 -0
- data/lib/rbgl/gui/window/render_loop.rb +111 -0
- data/lib/rbgl/gui/window.rb +74 -84
- data/lib/rbgl/gui/x11/atom_cache.rb +29 -0
- data/lib/rbgl/gui/x11/backend.rb +73 -49
- data/lib/rbgl/gui/x11/connection.rb +284 -219
- data/lib/rbgl/gui/x11/event_parser.rb +71 -0
- data/lib/rbgl/gui/x11/key_mapper.rb +48 -0
- data/lib/rbgl/gui/x11/pixel_encoder.rb +164 -0
- data/lib/rbgl/gui/x11/request_encoder.rb +155 -0
- data/lib/rbgl/gui/x11/setup_parser.rb +138 -0
- data/lib/rbgl/gui/x11/transport.rb +81 -0
- data/lib/rbgl/gui/x11/x_authority.rb +107 -0
- data/lib/rbgl/gui.rb +3 -5
- data/lib/rbgl/version.rb +1 -1
- metadata +63 -24
- data/examples/chemical_heartbeat.rb +0 -166
- data/examples/dark_transit.rb +0 -166
- data/examples/fractured_orb.rb +0 -428
- data/examples/fractured_orb_rb.rb +0 -598
- data/examples/hexagonal_flow.rb +0 -333
- data/examples/teapot.rb +0 -362
- data/examples/teapot_mcu.rb +0 -344
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "rasterizer/attribute_interpolator"
|
|
4
|
+
require_relative "rasterizer/triangle_renderer"
|
|
5
|
+
require_relative "rasterizer/line_renderer"
|
|
6
|
+
require_relative "rasterizer/point_renderer"
|
|
7
|
+
|
|
3
8
|
module RBGL
|
|
4
9
|
module Engine
|
|
5
10
|
class Rasterizer
|
|
@@ -8,112 +13,57 @@ module RBGL
|
|
|
8
13
|
def initialize(framebuffer)
|
|
9
14
|
@framebuffer = framebuffer
|
|
10
15
|
@viewport = { x: 0, y: 0, width: framebuffer.width, height: framebuffer.height }
|
|
16
|
+
@attribute_interpolator = AttributeInterpolator.new
|
|
17
|
+
@triangle_renderer = TriangleRenderer.new(
|
|
18
|
+
framebuffer,
|
|
19
|
+
interpolator: @attribute_interpolator,
|
|
20
|
+
viewport_transform: method(:viewport_transform),
|
|
21
|
+
edge_function: method(:edge_function)
|
|
22
|
+
)
|
|
23
|
+
@line_renderer = LineRenderer.new(
|
|
24
|
+
framebuffer,
|
|
25
|
+
interpolator: @attribute_interpolator,
|
|
26
|
+
viewport_transform: method(:viewport_transform)
|
|
27
|
+
)
|
|
28
|
+
@point_renderer = PointRenderer.new(
|
|
29
|
+
framebuffer,
|
|
30
|
+
viewport_transform: method(:viewport_transform),
|
|
31
|
+
point_pixel_offsets: method(:point_pixel_offsets)
|
|
32
|
+
)
|
|
11
33
|
end
|
|
12
34
|
|
|
13
|
-
def
|
|
14
|
-
|
|
15
|
-
p1 = viewport_transform(v1[:position])
|
|
16
|
-
p2 = viewport_transform(v2[:position])
|
|
17
|
-
|
|
18
|
-
min_x = [p0.x, p1.x, p2.x].min.floor.clamp(0, @framebuffer.width - 1)
|
|
19
|
-
max_x = [p0.x, p1.x, p2.x].max.ceil.clamp(0, @framebuffer.width - 1)
|
|
20
|
-
min_y = [p0.y, p1.y, p2.y].min.floor.clamp(0, @framebuffer.height - 1)
|
|
21
|
-
max_y = [p0.y, p1.y, p2.y].max.ceil.clamp(0, @framebuffer.height - 1)
|
|
22
|
-
|
|
23
|
-
area = edge_function(p0, p1, p2)
|
|
24
|
-
return if area.abs < 1e-10
|
|
25
|
-
|
|
26
|
-
case cull_mode
|
|
27
|
-
when :back
|
|
28
|
-
return if area < 0
|
|
29
|
-
when :front
|
|
30
|
-
return if area > 0
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
(min_y..max_y).each do |y|
|
|
34
|
-
(min_x..max_x).each do |x|
|
|
35
|
-
px = x + 0.5
|
|
36
|
-
py = y + 0.5
|
|
37
|
-
p = Larb::Vec2.new(px, py)
|
|
38
|
-
|
|
39
|
-
w0 = edge_function(p1, p2, p)
|
|
40
|
-
w1 = edge_function(p2, p0, p)
|
|
41
|
-
w2 = edge_function(p0, p1, p)
|
|
42
|
-
|
|
43
|
-
if (w0 >= 0 && w1 >= 0 && w2 >= 0) || (w0 <= 0 && w1 <= 0 && w2 <= 0)
|
|
44
|
-
inv_area = 1.0 / area
|
|
45
|
-
w0 *= inv_area
|
|
46
|
-
w1 *= inv_area
|
|
47
|
-
w2 *= inv_area
|
|
48
|
-
|
|
49
|
-
depth = w0 * p0.z + w1 * p1.z + w2 * p2.z
|
|
50
|
-
|
|
51
|
-
interpolated = interpolate_attributes(v0, v1, v2, w0, w1, w2)
|
|
52
|
-
|
|
53
|
-
frag_output = fragment_shader.process(interpolated, uniforms)
|
|
54
|
-
color = frag_output[:color]
|
|
55
|
-
|
|
56
|
-
@framebuffer.write_pixel(x, y, color, depth)
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
35
|
+
def resize(width, height)
|
|
36
|
+
@viewport = @viewport.merge(width: width, height: height)
|
|
60
37
|
end
|
|
61
38
|
|
|
62
|
-
def
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
dx = (x1 - x0).abs
|
|
72
|
-
dy = -(y1 - y0).abs
|
|
73
|
-
sx = x0 < x1 ? 1 : -1
|
|
74
|
-
sy = y0 < y1 ? 1 : -1
|
|
75
|
-
err = dx + dy
|
|
76
|
-
|
|
77
|
-
total_dist = Math.sqrt((x1 - x0)**2 + (y1 - y0)**2)
|
|
78
|
-
|
|
79
|
-
loop do
|
|
80
|
-
current_dist = Math.sqrt((x0 - p0.x.round)**2 + (y0 - p0.y.round)**2)
|
|
81
|
-
t = total_dist > 0 ? current_dist / total_dist : 0
|
|
82
|
-
|
|
83
|
-
depth = p0.z + (p1.z - p0.z) * t
|
|
84
|
-
|
|
85
|
-
interpolated = interpolate_line_attributes(v0, v1, t)
|
|
86
|
-
|
|
87
|
-
frag_output = fragment_shader.process(interpolated, uniforms)
|
|
88
|
-
@framebuffer.write_pixel(x0, y0, frag_output[:color], depth)
|
|
89
|
-
|
|
90
|
-
break if x0 == x1 && y0 == y1
|
|
91
|
-
|
|
92
|
-
e2 = 2 * err
|
|
93
|
-
if e2 >= dy
|
|
94
|
-
err += dy
|
|
95
|
-
x0 += sx
|
|
96
|
-
end
|
|
97
|
-
if e2 <= dx
|
|
98
|
-
err += dx
|
|
99
|
-
y0 += sy
|
|
100
|
-
end
|
|
101
|
-
end
|
|
39
|
+
def rasterize_triangle(v0, v1, v2, fragment_shader, uniforms, cull_mode: :none,
|
|
40
|
+
depth_test: true, depth_write: true, blend_mode: :none)
|
|
41
|
+
@triangle_renderer.rasterize(v0, v1, v2, fragment_shader, uniforms, state(
|
|
42
|
+
cull_mode: cull_mode,
|
|
43
|
+
depth_test: depth_test,
|
|
44
|
+
depth_write: depth_write,
|
|
45
|
+
blend_mode: blend_mode
|
|
46
|
+
))
|
|
102
47
|
end
|
|
103
48
|
|
|
104
|
-
def
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
49
|
+
def rasterize_line(v0, v1, fragment_shader, uniforms, cull_mode: :none,
|
|
50
|
+
depth_test: true, depth_write: true, blend_mode: :none)
|
|
51
|
+
@line_renderer.rasterize(v0, v1, fragment_shader, uniforms, state(
|
|
52
|
+
cull_mode: cull_mode,
|
|
53
|
+
depth_test: depth_test,
|
|
54
|
+
depth_write: depth_write,
|
|
55
|
+
blend_mode: blend_mode
|
|
56
|
+
))
|
|
57
|
+
end
|
|
109
58
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
59
|
+
def rasterize_point(vertex, fragment_shader, uniforms, size: 1,
|
|
60
|
+
depth_test: true, depth_write: true, blend_mode: :none)
|
|
61
|
+
@point_renderer.rasterize(vertex, fragment_shader, uniforms, {
|
|
62
|
+
size: size,
|
|
63
|
+
depth_test: depth_test,
|
|
64
|
+
depth_write: depth_write,
|
|
65
|
+
blend_mode: blend_mode
|
|
66
|
+
})
|
|
117
67
|
end
|
|
118
68
|
|
|
119
69
|
private
|
|
@@ -126,87 +76,31 @@ module RBGL
|
|
|
126
76
|
end
|
|
127
77
|
|
|
128
78
|
Larb::Vec3.new(
|
|
129
|
-
(ndc.x + 1) * 0.5 * @viewport[:width] + @viewport[:x],
|
|
130
|
-
(1 - ndc.y) * 0.5 * @viewport[:height] + @viewport[:y],
|
|
79
|
+
((ndc.x + 1) * 0.5 * @viewport[:width]) + @viewport[:x],
|
|
80
|
+
((1 - ndc.y) * 0.5 * @viewport[:height]) + @viewport[:y],
|
|
131
81
|
(ndc.z + 1) * 0.5
|
|
132
82
|
)
|
|
133
83
|
end
|
|
134
84
|
|
|
135
85
|
def edge_function(a, b, c)
|
|
136
|
-
(c.x - a.x) * (b.y - a.y) - (c.y - a.y) * (b.x - a.x)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def interpolate_attributes(v0, v1, v2, w0, w1, w2)
|
|
140
|
-
result = ShaderIO.new
|
|
141
|
-
|
|
142
|
-
all_keys = (v0.to_h.keys | v1.to_h.keys | v2.to_h.keys) - [:position]
|
|
143
|
-
|
|
144
|
-
all_keys.each do |key|
|
|
145
|
-
a0 = v0[key]
|
|
146
|
-
a1 = v1[key]
|
|
147
|
-
a2 = v2[key]
|
|
148
|
-
next unless a0 && a1 && a2
|
|
149
|
-
|
|
150
|
-
result[key] = interpolate_value(a0, a1, a2, w0, w1, w2)
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
result
|
|
86
|
+
((c.x - a.x) * (b.y - a.y)) - ((c.y - a.y) * (b.x - a.x))
|
|
154
87
|
end
|
|
155
88
|
|
|
156
|
-
def
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
when Larb::Vec3
|
|
164
|
-
Larb::Vec3.new(
|
|
165
|
-
a.x * w0 + b.x * w1 + c.x * w2,
|
|
166
|
-
a.y * w0 + b.y * w1 + c.y * w2,
|
|
167
|
-
a.z * w0 + b.z * w1 + c.z * w2
|
|
168
|
-
)
|
|
169
|
-
when Larb::Vec4
|
|
170
|
-
Larb::Vec4.new(
|
|
171
|
-
a.x * w0 + b.x * w1 + c.x * w2,
|
|
172
|
-
a.y * w0 + b.y * w1 + c.y * w2,
|
|
173
|
-
a.z * w0 + b.z * w1 + c.z * w2,
|
|
174
|
-
a.w * w0 + b.w * w1 + c.w * w2
|
|
175
|
-
)
|
|
176
|
-
when Larb::Color
|
|
177
|
-
Larb::Color.new(
|
|
178
|
-
a.r * w0 + b.r * w1 + c.r * w2,
|
|
179
|
-
a.g * w0 + b.g * w1 + c.g * w2,
|
|
180
|
-
a.b * w0 + b.b * w1 + c.b * w2,
|
|
181
|
-
a.a * w0 + b.a * w1 + c.a * w2
|
|
182
|
-
)
|
|
183
|
-
when Numeric
|
|
184
|
-
a * w0 + b * w1 + c * w2
|
|
185
|
-
else
|
|
186
|
-
a
|
|
187
|
-
end
|
|
89
|
+
def state(cull_mode:, depth_test:, depth_write:, blend_mode:)
|
|
90
|
+
{
|
|
91
|
+
cull_mode: cull_mode,
|
|
92
|
+
depth_test: depth_test,
|
|
93
|
+
depth_write: depth_write,
|
|
94
|
+
blend_mode: blend_mode
|
|
95
|
+
}
|
|
188
96
|
end
|
|
189
97
|
|
|
190
|
-
def
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
a1 = v1[key]
|
|
197
|
-
next unless a0 && a1
|
|
198
|
-
|
|
199
|
-
result[key] = case a0
|
|
200
|
-
when Larb::Vec2, Larb::Vec3, Larb::Vec4, Larb::Color
|
|
201
|
-
a0.lerp(a1, t)
|
|
202
|
-
when Numeric
|
|
203
|
-
a0 + (a1 - a0) * t
|
|
204
|
-
else
|
|
205
|
-
a0
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
result
|
|
98
|
+
def point_pixel_offsets(size)
|
|
99
|
+
pixel_size = [size.to_i, 1].max
|
|
100
|
+
min_offset = -(pixel_size / 2.0).floor
|
|
101
|
+
max_offset = ((pixel_size - 1) / 2.0).floor
|
|
102
|
+
offsets = (min_offset..max_offset).to_a
|
|
103
|
+
[offsets, offsets]
|
|
210
104
|
end
|
|
211
105
|
end
|
|
212
106
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RBGL
|
|
4
|
+
module Engine
|
|
5
|
+
class BaseShader
|
|
6
|
+
include ShaderBuiltins
|
|
7
|
+
|
|
8
|
+
def initialize(&block)
|
|
9
|
+
@process_block = block
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def process(input, uniforms, output: nil)
|
|
13
|
+
output ||= ShaderIO.new
|
|
14
|
+
output.clear
|
|
15
|
+
|
|
16
|
+
instance_exec(input, uniforms, output, &@process_block)
|
|
17
|
+
finalize_output(output)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.create(&block)
|
|
21
|
+
new(&block)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def finalize_output(output)
|
|
27
|
+
output
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class VertexShader < BaseShader
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def finalize_output(output)
|
|
35
|
+
raise "VertexShader must set output.position" unless output[:position]
|
|
36
|
+
|
|
37
|
+
output
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class FragmentShader < BaseShader
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def finalize_output(output)
|
|
45
|
+
output[:color] ||= Larb::Color.white
|
|
46
|
+
|
|
47
|
+
output
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RBGL
|
|
4
|
+
module Engine
|
|
5
|
+
module ShaderBuiltins
|
|
6
|
+
def vec2(x, y = nil)
|
|
7
|
+
y ||= x
|
|
8
|
+
Larb::Vec2.new(x, y)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def vec3(x, y = nil, z = nil)
|
|
12
|
+
if y.nil? && z.nil?
|
|
13
|
+
Larb::Vec3.new(x, x, x)
|
|
14
|
+
elsif z.nil? && y.is_a?(Larb::Vec2)
|
|
15
|
+
Larb::Vec3.new(x, y.x, y.y)
|
|
16
|
+
else
|
|
17
|
+
Larb::Vec3.new(x, y, z)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def vec4(x, y = nil, z = nil, w = nil)
|
|
22
|
+
case
|
|
23
|
+
when y.nil? && z.nil? && w.nil?
|
|
24
|
+
Larb::Vec4.new(x, x, x, x)
|
|
25
|
+
when x.is_a?(Larb::Vec3) && !y.nil?
|
|
26
|
+
Larb::Vec4.new(x.x, x.y, x.z, y)
|
|
27
|
+
when x.is_a?(Larb::Vec2) && y.is_a?(Larb::Vec2)
|
|
28
|
+
Larb::Vec4.new(x.x, x.y, y.x, y.y)
|
|
29
|
+
else
|
|
30
|
+
Larb::Vec4.new(x, y, z, w)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def dot(a, b)
|
|
35
|
+
a.dot(b)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def cross(a, b)
|
|
39
|
+
a.cross(b)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def normalize(v)
|
|
43
|
+
v.normalize
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def length(v)
|
|
47
|
+
v.length
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def reflect(v, n)
|
|
51
|
+
v.reflect(n)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def refract(v, n, eta)
|
|
55
|
+
cos_i = -dot(n, v)
|
|
56
|
+
sin_t2 = eta * eta * (1.0 - (cos_i * cos_i))
|
|
57
|
+
return v * 0 if sin_t2 > 1.0
|
|
58
|
+
|
|
59
|
+
cos_t = Math.sqrt(1.0 - sin_t2)
|
|
60
|
+
(v * eta) + (n * ((eta * cos_i) - cos_t))
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def mix(a, b, t)
|
|
64
|
+
case a
|
|
65
|
+
when Numeric then a + ((b - a) * t)
|
|
66
|
+
when Larb::Vec2, Larb::Vec3, Larb::Vec4, Larb::Color then a.lerp(b, t)
|
|
67
|
+
else
|
|
68
|
+
raise ArgumentError, "Unsupported mix operand: #{a.class}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def clamp(v, min_val, max_val)
|
|
73
|
+
case v
|
|
74
|
+
when Numeric then v.clamp(min_val, max_val)
|
|
75
|
+
when Larb::Color then Larb::Color.new(*v.to_a.map { |component| component.clamp(min_val, max_val) })
|
|
76
|
+
else
|
|
77
|
+
component_map(v) { |component| component.clamp(min_val, max_val) }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def saturate(v)
|
|
82
|
+
clamp(v, 0.0, 1.0)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def smoothstep(edge0, edge1, x)
|
|
86
|
+
component_ternary_map(edge0, edge1, x) do |e0, e1, value|
|
|
87
|
+
next value < e0 ? 0.0 : 1.0 if e0 == e1
|
|
88
|
+
|
|
89
|
+
t = (value - e0).fdiv(e1 - e0).clamp(0.0, 1.0)
|
|
90
|
+
t * t * (3.0 - (2.0 * t))
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def step(edge, x)
|
|
95
|
+
component_binary_map(edge, x) { |limit, value| value < limit ? 0.0 : 1.0 }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def fract(x)
|
|
99
|
+
component_map(x) { |value| value - value.floor }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def mod(x, y)
|
|
103
|
+
component_binary_map(x, y) { |left, right| left - (right * (left / right).floor) }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def abs(x)
|
|
107
|
+
component_map(x, &:abs)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def sign(x)
|
|
111
|
+
component_map(x) { |value| value <=> 0 }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def floor(x)
|
|
115
|
+
component_map(x, &:floor)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def ceil(x)
|
|
119
|
+
component_map(x, &:ceil)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def pow(x, y)
|
|
123
|
+
component_binary_map(x, y) { |left, right| left**right }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def sqrt(x)
|
|
127
|
+
component_map(x) { |value| Math.sqrt(value) }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def sin(x)
|
|
131
|
+
component_map(x) { |value| Math.sin(value) }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def cos(x)
|
|
135
|
+
component_map(x) { |value| Math.cos(value) }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def tan(x)
|
|
139
|
+
component_map(x) { |value| Math.tan(value) }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def asin(x)
|
|
143
|
+
component_map(x) { |value| Math.asin(value) }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def acos(x)
|
|
147
|
+
component_map(x) { |value| Math.acos(value) }
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def atan(y, x = nil)
|
|
151
|
+
if x
|
|
152
|
+
component_binary_map(y, x) { |left, right| Math.atan2(left, right) }
|
|
153
|
+
else
|
|
154
|
+
component_map(y) { |value| Math.atan(value) }
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def min(*args)
|
|
159
|
+
values = args.flatten
|
|
160
|
+
return values.min unless values.any? { |value| vector_value?(value) }
|
|
161
|
+
|
|
162
|
+
values.reduce { |current, value| component_binary_map(current, value) { |left, right| [left, right].min } }
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def max(*args)
|
|
166
|
+
values = args.flatten
|
|
167
|
+
return values.max unless values.any? { |value| vector_value?(value) }
|
|
168
|
+
|
|
169
|
+
values.reduce { |current, value| component_binary_map(current, value) { |left, right| [left, right].max } }
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def texture(tex, uv)
|
|
173
|
+
tex.sample(uv.x, uv.y)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def texture_lod(tex, uv, lod)
|
|
177
|
+
tex.sample(uv.x, uv.y, lod: lod)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def rgb(r, g, b)
|
|
181
|
+
Larb::Color.rgb(r, g, b)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def rgba(r, g, b, a)
|
|
185
|
+
Larb::Color.rgba(r, g, b, a)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def color_from_vec3(v)
|
|
189
|
+
Larb::Color.from_vec3(v)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def color_from_vec4(v)
|
|
193
|
+
Larb::Color.from_vec4(v)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
private
|
|
197
|
+
|
|
198
|
+
def component_map(value)
|
|
199
|
+
return yield(value) unless vector_value?(value)
|
|
200
|
+
|
|
201
|
+
rebuild_vector(value, vector_components(value).map { |component| yield(component) })
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def component_binary_map(left, right)
|
|
205
|
+
return yield(left, right) unless vector_value?(left) || vector_value?(right)
|
|
206
|
+
|
|
207
|
+
template = left if vector_value?(left)
|
|
208
|
+
template ||= right
|
|
209
|
+
|
|
210
|
+
rebuilt_components = vector_components(template).each_index.map do |index|
|
|
211
|
+
yield(component_at(left, index), component_at(right, index))
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
rebuild_vector(template, rebuilt_components)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def component_ternary_map(first, second, third)
|
|
218
|
+
return yield(first, second, third) unless [first, second, third].any? { |value| vector_value?(value) }
|
|
219
|
+
|
|
220
|
+
template = [first, second, third].find { |value| vector_value?(value) }
|
|
221
|
+
rebuilt_components = vector_components(template).each_index.map do |index|
|
|
222
|
+
yield(component_at(first, index), component_at(second, index), component_at(third, index))
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
rebuild_vector(template, rebuilt_components)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def vector_value?(value)
|
|
229
|
+
value.is_a?(Larb::Vec2) || value.is_a?(Larb::Vec3) || value.is_a?(Larb::Vec4)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def vector_components(value)
|
|
233
|
+
case value
|
|
234
|
+
when Larb::Vec2 then [value.x, value.y]
|
|
235
|
+
when Larb::Vec3 then [value.x, value.y, value.z]
|
|
236
|
+
when Larb::Vec4 then [value.x, value.y, value.z, value.w]
|
|
237
|
+
else
|
|
238
|
+
[]
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def component_at(value, index)
|
|
243
|
+
return value unless vector_value?(value)
|
|
244
|
+
|
|
245
|
+
vector_components(value)[index]
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def rebuild_vector(template, components)
|
|
249
|
+
case template
|
|
250
|
+
when Larb::Vec2 then Larb::Vec2.new(*components)
|
|
251
|
+
when Larb::Vec3 then Larb::Vec3.new(*components)
|
|
252
|
+
when Larb::Vec4 then Larb::Vec4.new(*components)
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RBGL
|
|
4
|
+
module Engine
|
|
5
|
+
class DynamicData
|
|
6
|
+
def initialize(data = {})
|
|
7
|
+
@data = {}
|
|
8
|
+
@writer_keys = {}
|
|
9
|
+
data.to_h.each { |key, value| self[key] = value }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def method_missing(name, *args)
|
|
13
|
+
key = normalize_key(name)
|
|
14
|
+
if args.empty? && @data.key?(key)
|
|
15
|
+
@data[key]
|
|
16
|
+
elsif (writer = writer_key(name))
|
|
17
|
+
self[writer] = args.first
|
|
18
|
+
else
|
|
19
|
+
super
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def respond_to_missing?(name, include_private = false)
|
|
24
|
+
@data.key?(normalize_key(name)) || !writer_key(name).nil? || super
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def [](key)
|
|
28
|
+
@data[normalize_key(key)]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def []=(key, value)
|
|
32
|
+
@data[normalize_key(key)] = value
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def to_h
|
|
36
|
+
@data.dup
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def keys
|
|
40
|
+
@data.keys
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def clear
|
|
44
|
+
@data.clear
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def normalize_key(key)
|
|
51
|
+
return key if key.is_a?(Symbol)
|
|
52
|
+
|
|
53
|
+
key.to_s.chomp("=").to_sym
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def writer_key(name)
|
|
57
|
+
return @writer_keys[name] if @writer_keys.key?(name)
|
|
58
|
+
|
|
59
|
+
text = name.to_s
|
|
60
|
+
@writer_keys[name] = text.end_with?("=") ? text.chomp("=").to_sym : nil
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class ShaderIO < DynamicData
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class Uniforms < DynamicData
|
|
68
|
+
def merge(other)
|
|
69
|
+
Uniforms.new(@data.merge(other.to_h))
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|