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
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RBGL
|
|
4
|
+
module Engine
|
|
5
|
+
class Rasterizer
|
|
6
|
+
class AttributeInterpolator
|
|
7
|
+
def interpolate(vertices, weights)
|
|
8
|
+
interpolate_prepared(prepare(vertices), weights, result: ShaderIO.new)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def prepare(vertices)
|
|
12
|
+
interpolated_keys(vertices).filter_map do |key|
|
|
13
|
+
values = vertices.map { |vertex| vertex[key] }
|
|
14
|
+
[key, *values] unless values.any?(&:nil?)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def interpolate_prepared(plan, weights, result:)
|
|
19
|
+
result.clear
|
|
20
|
+
plan.each do |entry|
|
|
21
|
+
result[entry[0]] = interpolate_values(entry.drop(1), weights)
|
|
22
|
+
end
|
|
23
|
+
result
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def interpolate_triangle(plan, w0, w1, w2, result:)
|
|
27
|
+
result.clear
|
|
28
|
+
plan.each do |key, a, b, c|
|
|
29
|
+
result[key] = interpolate_three(a, b, c, w0, w1, w2)
|
|
30
|
+
end
|
|
31
|
+
result
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def interpolate_line(plan, w0, w1, result:)
|
|
35
|
+
result.clear
|
|
36
|
+
plan.each do |key, a, b|
|
|
37
|
+
result[key] = interpolate_two(a, b, w0, w1)
|
|
38
|
+
end
|
|
39
|
+
result
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def interpolate_values(values, weights)
|
|
43
|
+
first = values.first
|
|
44
|
+
|
|
45
|
+
case first
|
|
46
|
+
when Larb::Vec2
|
|
47
|
+
vector2(values, weights)
|
|
48
|
+
when Larb::Vec3
|
|
49
|
+
vector3(values, weights)
|
|
50
|
+
when Larb::Vec4
|
|
51
|
+
vector4(values, weights)
|
|
52
|
+
when Larb::Color
|
|
53
|
+
color(values, weights)
|
|
54
|
+
when Array
|
|
55
|
+
numeric_arrays?(*values) ? array(values, weights) : first
|
|
56
|
+
when Numeric
|
|
57
|
+
scalar(values, weights)
|
|
58
|
+
else
|
|
59
|
+
first
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def interpolate_depth(depths, weights)
|
|
64
|
+
scalar(depths, weights)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def perspective_correct_weights(positions, weights)
|
|
68
|
+
corrected = positions.zip(weights).map do |position, weight|
|
|
69
|
+
weight * inverse_clip_w(position)
|
|
70
|
+
end
|
|
71
|
+
total = corrected.sum
|
|
72
|
+
return weights if total.zero?
|
|
73
|
+
|
|
74
|
+
corrected.map { |weight| weight / total }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def inverse_clip_w(position)
|
|
78
|
+
return 1.0 unless position.is_a?(Larb::Vec4)
|
|
79
|
+
return 1.0 if position.w.zero?
|
|
80
|
+
|
|
81
|
+
1.0 / position.w
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def interpolate_three(a, b, c, w0, w1, w2)
|
|
87
|
+
case a
|
|
88
|
+
when Larb::Vec2
|
|
89
|
+
Larb::Vec2.new((a.x * w0) + (b.x * w1) + (c.x * w2), (a.y * w0) + (b.y * w1) + (c.y * w2))
|
|
90
|
+
when Larb::Vec3
|
|
91
|
+
Larb::Vec3.new(
|
|
92
|
+
(a.x * w0) + (b.x * w1) + (c.x * w2),
|
|
93
|
+
(a.y * w0) + (b.y * w1) + (c.y * w2),
|
|
94
|
+
(a.z * w0) + (b.z * w1) + (c.z * w2)
|
|
95
|
+
)
|
|
96
|
+
when Larb::Vec4
|
|
97
|
+
Larb::Vec4.new(
|
|
98
|
+
(a.x * w0) + (b.x * w1) + (c.x * w2),
|
|
99
|
+
(a.y * w0) + (b.y * w1) + (c.y * w2),
|
|
100
|
+
(a.z * w0) + (b.z * w1) + (c.z * w2),
|
|
101
|
+
(a.w * w0) + (b.w * w1) + (c.w * w2)
|
|
102
|
+
)
|
|
103
|
+
when Larb::Color
|
|
104
|
+
Larb::Color.new(
|
|
105
|
+
(a.r * w0) + (b.r * w1) + (c.r * w2),
|
|
106
|
+
(a.g * w0) + (b.g * w1) + (c.g * w2),
|
|
107
|
+
(a.b * w0) + (b.b * w1) + (c.b * w2),
|
|
108
|
+
(a.a * w0) + (b.a * w1) + (c.a * w2)
|
|
109
|
+
)
|
|
110
|
+
when Array then numeric_arrays?(a, b, c) ? array([a, b, c], [w0, w1, w2]) : a
|
|
111
|
+
when Numeric then (a * w0) + (b * w1) + (c * w2)
|
|
112
|
+
else a
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def interpolate_two(a, b, w0, w1)
|
|
117
|
+
case a
|
|
118
|
+
when Larb::Vec2
|
|
119
|
+
Larb::Vec2.new((a.x * w0) + (b.x * w1), (a.y * w0) + (b.y * w1))
|
|
120
|
+
when Larb::Vec3
|
|
121
|
+
Larb::Vec3.new((a.x * w0) + (b.x * w1), (a.y * w0) + (b.y * w1), (a.z * w0) + (b.z * w1))
|
|
122
|
+
when Larb::Vec4
|
|
123
|
+
Larb::Vec4.new(
|
|
124
|
+
(a.x * w0) + (b.x * w1),
|
|
125
|
+
(a.y * w0) + (b.y * w1),
|
|
126
|
+
(a.z * w0) + (b.z * w1),
|
|
127
|
+
(a.w * w0) + (b.w * w1)
|
|
128
|
+
)
|
|
129
|
+
when Larb::Color
|
|
130
|
+
Larb::Color.new(
|
|
131
|
+
(a.r * w0) + (b.r * w1),
|
|
132
|
+
(a.g * w0) + (b.g * w1),
|
|
133
|
+
(a.b * w0) + (b.b * w1),
|
|
134
|
+
(a.a * w0) + (b.a * w1)
|
|
135
|
+
)
|
|
136
|
+
when Array then numeric_arrays?(a, b) ? array([a, b], [w0, w1]) : a
|
|
137
|
+
when Numeric then (a * w0) + (b * w1)
|
|
138
|
+
else a
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def interpolated_keys(vertices)
|
|
143
|
+
vertices.flat_map { |vertex| vertex.to_h.keys }.uniq - [:position]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def scalar(values, weights)
|
|
147
|
+
values.zip(weights).sum { |value, weight| value * weight }
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def array(values, weights)
|
|
151
|
+
values.transpose.map { |components| scalar(components, weights) }
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def numeric_arrays?(*values)
|
|
155
|
+
size = values.first.size
|
|
156
|
+
values.all? { |value| value.is_a?(Array) && value.size == size && value.all?(Numeric) }
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def vector2(values, weights)
|
|
160
|
+
Larb::Vec2.new(
|
|
161
|
+
scalar(values.map(&:x), weights),
|
|
162
|
+
scalar(values.map(&:y), weights)
|
|
163
|
+
)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def vector3(values, weights)
|
|
167
|
+
Larb::Vec3.new(
|
|
168
|
+
scalar(values.map(&:x), weights),
|
|
169
|
+
scalar(values.map(&:y), weights),
|
|
170
|
+
scalar(values.map(&:z), weights)
|
|
171
|
+
)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def vector4(values, weights)
|
|
175
|
+
Larb::Vec4.new(
|
|
176
|
+
scalar(values.map(&:x), weights),
|
|
177
|
+
scalar(values.map(&:y), weights),
|
|
178
|
+
scalar(values.map(&:z), weights),
|
|
179
|
+
scalar(values.map(&:w), weights)
|
|
180
|
+
)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def color(values, weights)
|
|
184
|
+
Larb::Color.new(
|
|
185
|
+
scalar(values.map(&:r), weights),
|
|
186
|
+
scalar(values.map(&:g), weights),
|
|
187
|
+
scalar(values.map(&:b), weights),
|
|
188
|
+
scalar(values.map(&:a), weights)
|
|
189
|
+
)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RBGL
|
|
4
|
+
module Engine
|
|
5
|
+
class Rasterizer
|
|
6
|
+
class LineRenderer
|
|
7
|
+
def initialize(framebuffer, interpolator:, viewport_transform:)
|
|
8
|
+
@framebuffer = framebuffer
|
|
9
|
+
@interpolator = interpolator
|
|
10
|
+
@viewport_transform = viewport_transform
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def rasterize(v0, v1, fragment_shader, uniforms, state)
|
|
14
|
+
p0 = @viewport_transform.call(v0[:position])
|
|
15
|
+
p1 = @viewport_transform.call(v1[:position])
|
|
16
|
+
if ([p0.x, p0.y, p0.z] <=> [p1.x, p1.y, p1.z]).positive?
|
|
17
|
+
p0, p1 = p1, p0
|
|
18
|
+
v0, v1 = v1, v0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
x0 = p0.x.round
|
|
22
|
+
y0 = p0.y.round
|
|
23
|
+
x1 = p1.x.round
|
|
24
|
+
y1 = p1.y.round
|
|
25
|
+
|
|
26
|
+
dx = (x1 - x0).abs
|
|
27
|
+
dy = -(y1 - y0).abs
|
|
28
|
+
sx = x0 < x1 ? 1 : -1
|
|
29
|
+
sy = y0 < y1 ? 1 : -1
|
|
30
|
+
err = dx + dy
|
|
31
|
+
line_dx = p1.x - p0.x
|
|
32
|
+
line_dy = p1.y - p0.y
|
|
33
|
+
line_length_squared = (line_dx * line_dx) + (line_dy * line_dy)
|
|
34
|
+
inverse_w0 = @interpolator.inverse_clip_w(v0[:position])
|
|
35
|
+
inverse_w1 = @interpolator.inverse_clip_w(v1[:position])
|
|
36
|
+
interpolation_plan = @interpolator.prepare([v0, v1])
|
|
37
|
+
attributes = ShaderIO.new
|
|
38
|
+
fragment_output = ShaderIO.new
|
|
39
|
+
|
|
40
|
+
loop do
|
|
41
|
+
t = interpolation_factor(x0, y0, p0, line_dx, line_dy, line_length_squared)
|
|
42
|
+
corrected_w0 = (1.0 - t) * inverse_w0
|
|
43
|
+
corrected_w1 = t * inverse_w1
|
|
44
|
+
total = corrected_w0 + corrected_w1
|
|
45
|
+
unless total.zero?
|
|
46
|
+
corrected_w0 /= total
|
|
47
|
+
corrected_w1 /= total
|
|
48
|
+
end
|
|
49
|
+
depth = (p0.z * (1.0 - t)) + (p1.z * t)
|
|
50
|
+
@interpolator.interpolate_line(interpolation_plan, corrected_w0, corrected_w1, result: attributes)
|
|
51
|
+
|
|
52
|
+
shade_fragment(x0, y0, depth, attributes, fragment_shader, uniforms, state, fragment_output)
|
|
53
|
+
break if x0 == x1 && y0 == y1
|
|
54
|
+
|
|
55
|
+
e2 = 2 * err
|
|
56
|
+
if e2 >= dy
|
|
57
|
+
err += dy
|
|
58
|
+
x0 += sx
|
|
59
|
+
end
|
|
60
|
+
if e2 <= dx
|
|
61
|
+
err += dx
|
|
62
|
+
y0 += sy
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def interpolation_factor(x, y, start_point, line_dx, line_dy, line_length_squared)
|
|
70
|
+
return 0.0 unless line_length_squared.positive?
|
|
71
|
+
|
|
72
|
+
(((x - start_point.x) * line_dx) + ((y - start_point.y) * line_dy))
|
|
73
|
+
.fdiv(line_length_squared).clamp(0.0, 1.0)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def shade_fragment(x, y, depth, attributes, fragment_shader, uniforms, state, fragment_output)
|
|
77
|
+
frag_output = fragment_shader.process(attributes, uniforms, output: fragment_output)
|
|
78
|
+
@framebuffer.write_pixel(
|
|
79
|
+
x, y, frag_output[:color], depth,
|
|
80
|
+
depth_test: state[:depth_test],
|
|
81
|
+
depth_write: state[:depth_write],
|
|
82
|
+
blend_mode: state[:blend_mode]
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RBGL
|
|
4
|
+
module Engine
|
|
5
|
+
class Rasterizer
|
|
6
|
+
class PointRenderer
|
|
7
|
+
def initialize(framebuffer, viewport_transform:, point_pixel_offsets:)
|
|
8
|
+
@framebuffer = framebuffer
|
|
9
|
+
@viewport_transform = viewport_transform
|
|
10
|
+
@point_pixel_offsets = point_pixel_offsets
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def rasterize(vertex, fragment_shader, uniforms, state)
|
|
14
|
+
point = @viewport_transform.call(vertex[:position])
|
|
15
|
+
x = point.x.floor
|
|
16
|
+
y = point.y.floor
|
|
17
|
+
depth = point.z
|
|
18
|
+
x_offsets, y_offsets = @point_pixel_offsets.call(state[:size])
|
|
19
|
+
frag_output = fragment_shader.process(vertex, uniforms)
|
|
20
|
+
|
|
21
|
+
y_offsets.each do |dy|
|
|
22
|
+
x_offsets.each do |dx|
|
|
23
|
+
@framebuffer.write_pixel(
|
|
24
|
+
x + dx, y + dy, frag_output[:color], depth,
|
|
25
|
+
depth_test: state[:depth_test],
|
|
26
|
+
depth_write: state[:depth_write],
|
|
27
|
+
blend_mode: state[:blend_mode]
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RBGL
|
|
4
|
+
module Engine
|
|
5
|
+
class Rasterizer
|
|
6
|
+
class TriangleRenderer
|
|
7
|
+
def initialize(framebuffer, interpolator:, viewport_transform:, edge_function:)
|
|
8
|
+
@framebuffer = framebuffer
|
|
9
|
+
@interpolator = interpolator
|
|
10
|
+
@viewport_transform = viewport_transform
|
|
11
|
+
@edge_function = edge_function
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def rasterize(v0, v1, v2, fragment_shader, uniforms, state)
|
|
15
|
+
p0 = @viewport_transform.call(v0[:position])
|
|
16
|
+
p1 = @viewport_transform.call(v1[:position])
|
|
17
|
+
p2 = @viewport_transform.call(v2[:position])
|
|
18
|
+
|
|
19
|
+
area = @edge_function.call(p0, p1, p2)
|
|
20
|
+
return if area.abs < 1e-10
|
|
21
|
+
return if culled?(area, state[:cull_mode])
|
|
22
|
+
|
|
23
|
+
min_x, max_x, min_y, max_y = bounds(p0, p1, p2)
|
|
24
|
+
inv_area = 1.0 / area
|
|
25
|
+
inverse_w0 = @interpolator.inverse_clip_w(v0[:position])
|
|
26
|
+
inverse_w1 = @interpolator.inverse_clip_w(v1[:position])
|
|
27
|
+
inverse_w2 = @interpolator.inverse_clip_w(v2[:position])
|
|
28
|
+
interpolation_plan = @interpolator.prepare([v0, v1, v2])
|
|
29
|
+
attributes = ShaderIO.new
|
|
30
|
+
fragment_output = ShaderIO.new
|
|
31
|
+
|
|
32
|
+
x = min_x + 0.5
|
|
33
|
+
y = min_y + 0.5
|
|
34
|
+
row_w0 = edge_value(p1, p2, x, y)
|
|
35
|
+
row_w1 = edge_value(p2, p0, x, y)
|
|
36
|
+
row_w2 = edge_value(p0, p1, x, y)
|
|
37
|
+
x_steps = [p2.y - p1.y, p0.y - p2.y, p1.y - p0.y]
|
|
38
|
+
y_steps = [p1.x - p2.x, p2.x - p0.x, p0.x - p1.x]
|
|
39
|
+
positive_area = area.positive?
|
|
40
|
+
inclusive_edges = [
|
|
41
|
+
inclusive_edge?(p1, p2, positive_area),
|
|
42
|
+
inclusive_edge?(p2, p0, positive_area),
|
|
43
|
+
inclusive_edge?(p0, p1, positive_area)
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
(min_y..max_y).each do |pixel_y|
|
|
47
|
+
w0 = row_w0
|
|
48
|
+
w1 = row_w1
|
|
49
|
+
w2 = row_w2
|
|
50
|
+
(min_x..max_x).each do |pixel_x|
|
|
51
|
+
if inside_triangle?(w0, w1, w2, positive_area, inclusive_edges)
|
|
52
|
+
screen_w0 = w0 * inv_area
|
|
53
|
+
screen_w1 = w1 * inv_area
|
|
54
|
+
screen_w2 = w2 * inv_area
|
|
55
|
+
corrected_w0 = screen_w0 * inverse_w0
|
|
56
|
+
corrected_w1 = screen_w1 * inverse_w1
|
|
57
|
+
corrected_w2 = screen_w2 * inverse_w2
|
|
58
|
+
total = corrected_w0 + corrected_w1 + corrected_w2
|
|
59
|
+
unless total.zero?
|
|
60
|
+
corrected_w0 /= total
|
|
61
|
+
corrected_w1 /= total
|
|
62
|
+
corrected_w2 /= total
|
|
63
|
+
end
|
|
64
|
+
depth = (p0.z * screen_w0) + (p1.z * screen_w1) + (p2.z * screen_w2)
|
|
65
|
+
@interpolator.interpolate_triangle(
|
|
66
|
+
interpolation_plan, corrected_w0, corrected_w1, corrected_w2, result: attributes
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
shade_fragment(
|
|
70
|
+
pixel_x, pixel_y, depth, attributes, fragment_shader, uniforms, state, fragment_output
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
w0 += x_steps[0]
|
|
74
|
+
w1 += x_steps[1]
|
|
75
|
+
w2 += x_steps[2]
|
|
76
|
+
end
|
|
77
|
+
row_w0 += y_steps[0]
|
|
78
|
+
row_w1 += y_steps[1]
|
|
79
|
+
row_w2 += y_steps[2]
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def bounds(p0, p1, p2)
|
|
86
|
+
min_x = [p0.x, p1.x, p2.x].min.floor.clamp(0, @framebuffer.width - 1)
|
|
87
|
+
max_x = [p0.x, p1.x, p2.x].max.ceil.clamp(0, @framebuffer.width - 1)
|
|
88
|
+
min_y = [p0.y, p1.y, p2.y].min.floor.clamp(0, @framebuffer.height - 1)
|
|
89
|
+
max_y = [p0.y, p1.y, p2.y].max.ceil.clamp(0, @framebuffer.height - 1)
|
|
90
|
+
[min_x, max_x, min_y, max_y]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def edge_value(a, b, x, y)
|
|
94
|
+
((x - a.x) * (b.y - a.y)) - ((y - a.y) * (b.x - a.x))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def inside_triangle?(w0, w1, w2, positive_area, inclusive_edges)
|
|
98
|
+
inside_edge?(w0, positive_area, inclusive_edges[0]) &&
|
|
99
|
+
inside_edge?(w1, positive_area, inclusive_edges[1]) &&
|
|
100
|
+
inside_edge?(w2, positive_area, inclusive_edges[2])
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def inside_edge?(weight, positive_area, inclusive)
|
|
104
|
+
positive_area ? weight.positive? || (weight.zero? && inclusive) :
|
|
105
|
+
weight.negative? || (weight.zero? && inclusive)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def inclusive_edge?(start_point, end_point, positive_area)
|
|
109
|
+
dx = end_point.x - start_point.x
|
|
110
|
+
dy = end_point.y - start_point.y
|
|
111
|
+
dx = -dx if positive_area
|
|
112
|
+
dy = -dy if positive_area
|
|
113
|
+
dy.positive? || (dy.zero? && dx.positive?)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def culled?(area, cull_mode)
|
|
117
|
+
case cull_mode
|
|
118
|
+
when :back then area < 0
|
|
119
|
+
when :front then area > 0
|
|
120
|
+
else false
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def shade_fragment(x, y, depth, attributes, fragment_shader, uniforms, state, fragment_output)
|
|
125
|
+
frag_output = fragment_shader.process(attributes, uniforms, output: fragment_output)
|
|
126
|
+
@framebuffer.write_pixel(
|
|
127
|
+
x, y, frag_output[:color], depth,
|
|
128
|
+
depth_test: state[:depth_test],
|
|
129
|
+
depth_write: state[:depth_write],
|
|
130
|
+
blend_mode: state[:blend_mode]
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|