rbgl 1.0.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 +7 -1
- 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 +49 -8
- data/lib/rbgl/engine/clip_space_clipper.rb +23 -3
- data/lib/rbgl/engine/context.rb +23 -6
- data/lib/rbgl/engine/framebuffer.rb +137 -41
- data/lib/rbgl/engine/immutable_color.rb +32 -0
- data/lib/rbgl/engine/rasterizer/attribute_interpolator.rb +100 -10
- data/lib/rbgl/engine/rasterizer/line_renderer.rb +30 -14
- data/lib/rbgl/engine/rasterizer/point_renderer.rb +3 -3
- data/lib/rbgl/engine/rasterizer/triangle_renderer.rb +72 -23
- data/lib/rbgl/engine/rasterizer.rb +3 -20
- data/lib/rbgl/engine/shader/base_shader.rb +3 -7
- data/lib/rbgl/engine/shader/builtins.rb +13 -10
- data/lib/rbgl/engine/shader/dynamic_data.rb +18 -10
- data/lib/rbgl/engine/texture.rb +74 -50
- data/lib/rbgl/engine.rb +3 -1
- data/lib/rbgl/gui/backend.rb +12 -7
- data/lib/rbgl/gui/backend_factory.rb +7 -3
- data/lib/rbgl/gui/cocoa/backend.rb +50 -17
- 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 +2 -2
- data/lib/rbgl/gui/file_backend/frame_writer.rb +7 -0
- data/lib/rbgl/gui/file_backend/png_writer.rb +18 -0
- data/lib/rbgl/gui/file_backend/ppm_writer.rb +1 -0
- data/lib/rbgl/gui/file_backend.rb +24 -6
- data/lib/rbgl/gui/wayland/backend.rb +127 -55
- data/lib/rbgl/gui/wayland/codec.rb +7 -11
- data/lib/rbgl/gui/wayland/connection.rb +137 -20
- data/lib/rbgl/gui/wayland/event_dispatcher.rb +103 -12
- data/lib/rbgl/gui/wayland/global_binder.rb +6 -4
- data/lib/rbgl/gui/wayland/input_mapper.rb +61 -0
- data/lib/rbgl/gui/wayland/protocol_objects/arguments.rb +14 -2
- data/lib/rbgl/gui/wayland/protocol_objects/seat.rb +115 -0
- data/lib/rbgl/gui/wayland/protocol_objects/shm.rb +23 -12
- data/lib/rbgl/gui/wayland/protocol_objects/xdg_shell.rb +15 -0
- data/lib/rbgl/gui/wayland/protocol_objects.rb +1 -0
- data/lib/rbgl/gui/window/render_loop.rb +71 -18
- data/lib/rbgl/gui/window.rb +39 -8
- data/lib/rbgl/gui/x11/atom_cache.rb +4 -1
- data/lib/rbgl/gui/x11/backend.rb +66 -28
- data/lib/rbgl/gui/x11/connection.rb +216 -86
- data/lib/rbgl/gui/x11/event_parser.rb +13 -2
- 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 +29 -28
- data/lib/rbgl/gui/x11/setup_parser.rb +138 -0
- data/lib/rbgl/gui/x11/transport.rb +52 -2
- data/lib/rbgl/gui/x11/x_authority.rb +107 -0
- data/lib/rbgl/gui.rb +2 -5
- data/lib/rbgl/version.rb +1 -1
- metadata +36 -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
data/examples/plasma.rb
CHANGED
|
@@ -60,7 +60,7 @@ while running && !window.should_close?
|
|
|
60
60
|
|
|
61
61
|
events = window.poll_events_raw
|
|
62
62
|
events.each do |e|
|
|
63
|
-
if e[:type] == :key_press &&
|
|
63
|
+
if e[:type] == :key_press && %i[escape q].include?(e[:key])
|
|
64
64
|
running = false
|
|
65
65
|
end
|
|
66
66
|
end
|
data/examples/sphere_raymarch.rb
CHANGED
|
@@ -108,7 +108,7 @@ while running && !window.should_close?
|
|
|
108
108
|
|
|
109
109
|
events = window.poll_events_raw
|
|
110
110
|
events.each do |e|
|
|
111
|
-
if e[:type] == :key_press &&
|
|
111
|
+
if e[:type] == :key_press && %i[escape q].include?(e[:key])
|
|
112
112
|
running = false
|
|
113
113
|
end
|
|
114
114
|
end
|
data/examples/triangle_window.rb
CHANGED
data/exe/rbgl
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
require "optparse"
|
|
6
|
+
|
|
7
|
+
lib_path = File.expand_path("../lib", __dir__)
|
|
8
|
+
$LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
|
|
9
|
+
|
|
10
|
+
require "rbgl"
|
|
11
|
+
require "rbgl/cli/doctor"
|
|
12
|
+
|
|
13
|
+
module RBGLCLI
|
|
14
|
+
def self.run(argv = ARGV, stdout: $stdout, stderr: $stderr)
|
|
15
|
+
options = { json: false }
|
|
16
|
+
|
|
17
|
+
parser = OptionParser.new do |parser|
|
|
18
|
+
parser.banner = "Usage: rbgl <command>\n\nCommands:\n doctor [--json]\n"
|
|
19
|
+
parser.on("--json", "Output JSON") { options[:json] = true }
|
|
20
|
+
parser.on("-h", "--help", "Show this help") do
|
|
21
|
+
stdout.puts parser
|
|
22
|
+
return 0
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
parser.parse!(argv)
|
|
27
|
+
|
|
28
|
+
command = argv.shift
|
|
29
|
+
unless command == "doctor"
|
|
30
|
+
stderr.puts parser
|
|
31
|
+
return 1
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
RBGL::CLI::Doctor.new(json: options[:json], io: stdout).run
|
|
35
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument => error
|
|
36
|
+
stderr.puts error.message
|
|
37
|
+
stderr.puts parser
|
|
38
|
+
1
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
exit RBGLCLI.run
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "json"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
|
|
7
|
+
module RBGL
|
|
8
|
+
module CLI
|
|
9
|
+
class Doctor
|
|
10
|
+
class << self
|
|
11
|
+
def default_checks
|
|
12
|
+
{
|
|
13
|
+
file_backend: ->(doctor, env, platform) { doctor.__send__(:check_file_backend, env, platform) },
|
|
14
|
+
wayland: ->(doctor, env, platform) { doctor.__send__(:check_wayland, env, platform) },
|
|
15
|
+
x11: ->(doctor, env, platform) { doctor.__send__(:check_x11, env, platform) },
|
|
16
|
+
cocoa: ->(doctor, env, platform) { doctor.__send__(:check_cocoa, env, platform) },
|
|
17
|
+
rlsl: ->(doctor, env, platform) { doctor.__send__(:check_rlsl, env, platform) }
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(json: false, io: $stdout, env: ENV, platform: RUBY_PLATFORM, ruby_version: RUBY_VERSION, checks: {})
|
|
23
|
+
@json = json
|
|
24
|
+
@io = io
|
|
25
|
+
@env = env
|
|
26
|
+
@platform = platform
|
|
27
|
+
@ruby_version = ruby_version
|
|
28
|
+
@checks = self.class.default_checks.merge(checks)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def run
|
|
32
|
+
report = collect
|
|
33
|
+
|
|
34
|
+
if @json
|
|
35
|
+
output_json(report)
|
|
36
|
+
else
|
|
37
|
+
output_text(report)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
0
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def collect
|
|
44
|
+
{
|
|
45
|
+
rbgl_version: RBGL::VERSION,
|
|
46
|
+
ruby_version: @ruby_version,
|
|
47
|
+
platform: @platform,
|
|
48
|
+
file_backend: collect_component(:file_backend),
|
|
49
|
+
wayland: collect_component(:wayland),
|
|
50
|
+
x11: collect_component(:x11),
|
|
51
|
+
cocoa: collect_component(:cocoa),
|
|
52
|
+
rlsl: collect_component(:rlsl)
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def collect_component(key)
|
|
59
|
+
@checks.fetch(key).call(self, @env, @platform)
|
|
60
|
+
rescue StandardError => e
|
|
61
|
+
{ available: false, error: "#{e.class}: #{e.message}" }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def output_text(report)
|
|
65
|
+
@io.puts("RBGL version: #{report[:rbgl_version]}")
|
|
66
|
+
@io.puts("Ruby version: #{report[:ruby_version]}")
|
|
67
|
+
@io.puts("RUBY_PLATFORM: #{report[:platform]}")
|
|
68
|
+
@io.puts
|
|
69
|
+
dump_section("File backend", report[:file_backend])
|
|
70
|
+
dump_section("Wayland", report[:wayland])
|
|
71
|
+
dump_section("X11", report[:x11])
|
|
72
|
+
dump_section("Cocoa", report[:cocoa])
|
|
73
|
+
dump_section("RLSL", report[:rlsl])
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def output_json(report)
|
|
77
|
+
@io.puts(JSON.pretty_generate(report))
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def dump_section(name, values)
|
|
81
|
+
@io.puts("#{name}:")
|
|
82
|
+
values.each do |key, value|
|
|
83
|
+
@io.puts(" #{key}: #{format_value(value)}")
|
|
84
|
+
end
|
|
85
|
+
@io.puts
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def format_value(value)
|
|
89
|
+
return value.is_a?(TrueClass) || value.is_a?(FalseClass) ? (value ? "yes" : "no") : value.inspect
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def check_file_backend(_env, _platform)
|
|
93
|
+
output_dir = Dir.mktmpdir("rbgl-doctor")
|
|
94
|
+
backend = RBGL::GUI::FileBackend.new(1, 1, "rbgl-doctor", output_dir: output_dir)
|
|
95
|
+
backend.present(RBGL::Engine::Framebuffer.new(1, 1))
|
|
96
|
+
|
|
97
|
+
{ available: true, output_dir: output_dir }
|
|
98
|
+
ensure
|
|
99
|
+
backend&.close
|
|
100
|
+
FileUtils.rm_rf(output_dir) if output_dir
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def check_wayland(env, _platform)
|
|
104
|
+
require_ok, require_error = safe_require("rbgl/gui/wayland/connection")
|
|
105
|
+
{
|
|
106
|
+
display_env: env["WAYLAND_DISPLAY"],
|
|
107
|
+
display_available: !env["WAYLAND_DISPLAY"].nil?,
|
|
108
|
+
require_ok: require_ok,
|
|
109
|
+
require_error: require_error
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def check_x11(env, _platform)
|
|
114
|
+
require_ok, require_error = safe_require("rbgl/gui/x11/connection")
|
|
115
|
+
{
|
|
116
|
+
display_env: env["DISPLAY"],
|
|
117
|
+
display_available: !env["DISPLAY"].nil?,
|
|
118
|
+
require_ok: require_ok,
|
|
119
|
+
require_error: require_error
|
|
120
|
+
}
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def check_cocoa(_env, platform)
|
|
124
|
+
darwin = platform.include?("darwin")
|
|
125
|
+
return { platform: platform, supported: false } unless darwin
|
|
126
|
+
|
|
127
|
+
require_ok, require_error = safe_require("metaco")
|
|
128
|
+
{
|
|
129
|
+
platform: platform,
|
|
130
|
+
supported: true,
|
|
131
|
+
require_ok: require_ok,
|
|
132
|
+
require_error: require_error
|
|
133
|
+
}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def check_rlsl(_env, _platform)
|
|
137
|
+
begin
|
|
138
|
+
require "rlsl"
|
|
139
|
+
version = Gem.loaded_specs["rlsl"]&.version&.to_s
|
|
140
|
+
|
|
141
|
+
{ require_ok: true, version: version }
|
|
142
|
+
rescue LoadError => e
|
|
143
|
+
{ require_ok: false, error: "#{e.class}: #{e.message}" }
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def safe_require(file)
|
|
148
|
+
require file
|
|
149
|
+
[true, nil]
|
|
150
|
+
rescue LoadError => e
|
|
151
|
+
[false, "#{e.class}: #{e.message}"]
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
data/lib/rbgl/engine/buffer.rb
CHANGED
|
@@ -6,6 +6,10 @@ module RBGL
|
|
|
6
6
|
attr_reader :name, :size, :offset, :kind
|
|
7
7
|
|
|
8
8
|
def initialize(name, size, offset = 0, kind: nil)
|
|
9
|
+
unless size.is_a?(Integer) && size.positive?
|
|
10
|
+
raise ArgumentError, "Vertex attribute size must be a positive Integer"
|
|
11
|
+
end
|
|
12
|
+
|
|
9
13
|
@name = name.to_sym
|
|
10
14
|
@size = size
|
|
11
15
|
@offset = offset
|
|
@@ -96,18 +100,35 @@ module RBGL
|
|
|
96
100
|
end
|
|
97
101
|
|
|
98
102
|
class VertexLayout
|
|
99
|
-
attr_reader :
|
|
103
|
+
attr_reader :stride
|
|
100
104
|
|
|
101
105
|
def initialize(&block)
|
|
102
106
|
@attributes = {}
|
|
103
107
|
@offset = 0
|
|
108
|
+
@stride = 0
|
|
109
|
+
@finalized = false
|
|
104
110
|
instance_eval(&block) if block_given?
|
|
105
|
-
@stride = @offset
|
|
106
111
|
end
|
|
107
112
|
|
|
108
113
|
def attribute(name, size, kind: nil)
|
|
109
|
-
|
|
114
|
+
raise ArgumentError, "Vertex layout is finalized" if @finalized
|
|
115
|
+
|
|
116
|
+
key = name.to_sym
|
|
117
|
+
raise ArgumentError, "Duplicate vertex attribute: #{key}" if @attributes.key?(key)
|
|
118
|
+
|
|
119
|
+
@attributes[key] = VertexAttribute.new(key, size, @offset, kind: kind)
|
|
110
120
|
@offset += size
|
|
121
|
+
@stride = @offset
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def attributes
|
|
125
|
+
@finalized ? @attributes : @attributes.dup.freeze
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def finalize!
|
|
129
|
+
@finalized = true
|
|
130
|
+
@attributes.freeze
|
|
131
|
+
self
|
|
111
132
|
end
|
|
112
133
|
|
|
113
134
|
def self.position_only
|
|
@@ -143,7 +164,11 @@ module RBGL
|
|
|
143
164
|
attr_reader :data, :layout, :vertex_count
|
|
144
165
|
|
|
145
166
|
def initialize(layout)
|
|
146
|
-
|
|
167
|
+
unless layout.is_a?(VertexLayout)
|
|
168
|
+
raise ArgumentError, "VertexBuffer layout must be an RBGL::Engine::VertexLayout"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
@layout = layout.finalize!
|
|
147
172
|
@data = []
|
|
148
173
|
@vertex_count = 0
|
|
149
174
|
end
|
|
@@ -199,14 +224,16 @@ module RBGL
|
|
|
199
224
|
end
|
|
200
225
|
|
|
201
226
|
class IndexBuffer
|
|
202
|
-
attr_reader :indices
|
|
203
|
-
|
|
204
227
|
def initialize(indices = [])
|
|
205
|
-
@indices = indices
|
|
228
|
+
@indices = validate_indices!(indices)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def indices
|
|
232
|
+
@indices.dup.freeze
|
|
206
233
|
end
|
|
207
234
|
|
|
208
235
|
def add(*idx)
|
|
209
|
-
@indices.concat(idx.flatten
|
|
236
|
+
@indices.concat(validate_indices!(idx.flatten))
|
|
210
237
|
self
|
|
211
238
|
end
|
|
212
239
|
|
|
@@ -215,6 +242,8 @@ module RBGL
|
|
|
215
242
|
end
|
|
216
243
|
|
|
217
244
|
def get_triangle(index)
|
|
245
|
+
return nil unless index.is_a?(Integer) && index >= 0
|
|
246
|
+
|
|
218
247
|
start = index * 3
|
|
219
248
|
@indices[start, 3]
|
|
220
249
|
end
|
|
@@ -224,6 +253,18 @@ module RBGL
|
|
|
224
253
|
|
|
225
254
|
(0...triangle_count).each { |i| yield get_triangle(i) }
|
|
226
255
|
end
|
|
256
|
+
|
|
257
|
+
private
|
|
258
|
+
|
|
259
|
+
def validate_indices!(indices)
|
|
260
|
+
indices.map do |index|
|
|
261
|
+
unless index.is_a?(Integer) && index >= 0
|
|
262
|
+
raise ArgumentError, "Index must be a non-negative Integer: #{index.inspect}"
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
index
|
|
266
|
+
end
|
|
267
|
+
end
|
|
227
268
|
end
|
|
228
269
|
end
|
|
229
270
|
end
|
|
@@ -13,7 +13,10 @@ module RBGL
|
|
|
13
13
|
].freeze
|
|
14
14
|
|
|
15
15
|
def clip(v0, v1, v2)
|
|
16
|
-
polygon = [v0, v1, v2]
|
|
16
|
+
polygon = [v0, v1, v2].map { |vertex| normalize_vertex(vertex) }
|
|
17
|
+
outcodes = polygon.map { |vertex| outcode(vertex) }
|
|
18
|
+
return [polygon] if outcodes.reduce(:|).zero?
|
|
19
|
+
return [] unless outcodes.reduce(:&).zero?
|
|
17
20
|
|
|
18
21
|
CLIP_PLANES.each do |plane|
|
|
19
22
|
polygon = clip_against_plane(polygon, plane)
|
|
@@ -38,6 +41,11 @@ module RBGL
|
|
|
38
41
|
CLIP_PLANES.all? { |plane| signed_distance(vertex, plane) >= 0 }
|
|
39
42
|
end
|
|
40
43
|
|
|
44
|
+
def clip_point(vertex)
|
|
45
|
+
point = normalize_vertex(vertex)
|
|
46
|
+
visible_point?(point) ? point : nil
|
|
47
|
+
end
|
|
48
|
+
|
|
41
49
|
private
|
|
42
50
|
|
|
43
51
|
def clip_against_plane(vertices, plane)
|
|
@@ -102,7 +110,13 @@ module RBGL
|
|
|
102
110
|
end
|
|
103
111
|
|
|
104
112
|
def duplicate_vertex(vertex)
|
|
105
|
-
ShaderIO.new(vertex.to_h)
|
|
113
|
+
ShaderIO.new(vertex.to_h).tap do |copy|
|
|
114
|
+
copy[:position] = clip_position(vertex) if vertex[:position].is_a?(Larb::Vec2)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def normalize_vertex(vertex)
|
|
119
|
+
vertex[:position].is_a?(Larb::Vec2) ? duplicate_vertex(vertex) : vertex
|
|
106
120
|
end
|
|
107
121
|
|
|
108
122
|
def interpolated_keys(start_vertex, finish_vertex)
|
|
@@ -115,7 +129,7 @@ module RBGL
|
|
|
115
129
|
|
|
116
130
|
case start_value
|
|
117
131
|
when Numeric
|
|
118
|
-
start_value + (finish_value - start_value) * t
|
|
132
|
+
start_value + ((finish_value - start_value) * t)
|
|
119
133
|
when Larb::Vec2, Larb::Vec3, Larb::Vec4, Larb::Color
|
|
120
134
|
start_value.lerp(finish_value, t)
|
|
121
135
|
else
|
|
@@ -127,6 +141,12 @@ module RBGL
|
|
|
127
141
|
plane.call(clip_position(vertex))
|
|
128
142
|
end
|
|
129
143
|
|
|
144
|
+
def outcode(vertex)
|
|
145
|
+
CLIP_PLANES.each_with_index.reduce(0) do |code, (plane, index)|
|
|
146
|
+
signed_distance(vertex, plane).negative? ? code | (1 << index) : code
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
130
150
|
def clip_position(vertex)
|
|
131
151
|
position = vertex[:position]
|
|
132
152
|
|
data/lib/rbgl/engine/context.rb
CHANGED
|
@@ -46,12 +46,18 @@ module RBGL
|
|
|
46
46
|
|
|
47
47
|
def draw_arrays(mode, first, count)
|
|
48
48
|
validate_draw_state!
|
|
49
|
-
|
|
49
|
+
validate_draw_range!(first, count, @vertex_buffer.vertex_count)
|
|
50
|
+
draw_vertices(mode, first...(first + count))
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
def draw_elements(mode, count, offset = 0)
|
|
53
54
|
validate_draw_state!(indexed: true)
|
|
54
|
-
|
|
55
|
+
all_indices = @index_buffer.indices
|
|
56
|
+
validate_draw_range!(offset, count, all_indices.size)
|
|
57
|
+
indices = all_indices[offset, count]
|
|
58
|
+
invalid_index = indices.find { |index| index >= @vertex_buffer.vertex_count }
|
|
59
|
+
raise ArgumentError, "Vertex index out of bounds: #{invalid_index}" if invalid_index
|
|
60
|
+
|
|
55
61
|
draw_vertices(mode, indices, vertex_cache: {})
|
|
56
62
|
end
|
|
57
63
|
|
|
@@ -75,6 +81,15 @@ module RBGL
|
|
|
75
81
|
raise "No index buffer bound" if indexed && !@index_buffer
|
|
76
82
|
end
|
|
77
83
|
|
|
84
|
+
def validate_draw_range!(offset, count, limit)
|
|
85
|
+
unless offset.is_a?(Integer) && count.is_a?(Integer) && offset >= 0 && count >= 0
|
|
86
|
+
raise ArgumentError, "Draw offset and count must be non-negative Integers"
|
|
87
|
+
end
|
|
88
|
+
return if offset + count <= limit
|
|
89
|
+
|
|
90
|
+
raise ArgumentError, "Draw range exceeds available data"
|
|
91
|
+
end
|
|
92
|
+
|
|
78
93
|
def draw_vertices(mode, indices, vertex_cache: nil)
|
|
79
94
|
each_primitive(mode, indices) do |primitive_indices|
|
|
80
95
|
primitive = primitive_indices.map { |index| fetch_vertex(index, vertex_cache) }
|
|
@@ -95,7 +110,7 @@ module RBGL
|
|
|
95
110
|
when :points
|
|
96
111
|
vertices.each { |vertex| yield [vertex] }
|
|
97
112
|
when :triangle_strip
|
|
98
|
-
(0...vertices.size - 2).each do |index|
|
|
113
|
+
(0...(vertices.size - 2)).each do |index|
|
|
99
114
|
if index.even?
|
|
100
115
|
yield [vertices[index], vertices[index + 1], vertices[index + 2]]
|
|
101
116
|
else
|
|
@@ -103,7 +118,7 @@ module RBGL
|
|
|
103
118
|
end
|
|
104
119
|
end
|
|
105
120
|
when :triangle_fan
|
|
106
|
-
(1...vertices.size - 1).each do |index|
|
|
121
|
+
(1...(vertices.size - 1)).each do |index|
|
|
107
122
|
yield [vertices[0], vertices[index], vertices[index + 1]]
|
|
108
123
|
end
|
|
109
124
|
else
|
|
@@ -169,10 +184,12 @@ module RBGL
|
|
|
169
184
|
end
|
|
170
185
|
|
|
171
186
|
def draw_point(v)
|
|
172
|
-
return unless v
|
|
187
|
+
return unless v
|
|
188
|
+
clipped = @clipper.clip_point(v)
|
|
189
|
+
return unless clipped
|
|
173
190
|
|
|
174
191
|
@rasterizer.rasterize_point(
|
|
175
|
-
|
|
192
|
+
clipped,
|
|
176
193
|
@pipeline.fragment_shader,
|
|
177
194
|
@uniforms,
|
|
178
195
|
depth_test: @pipeline.depth_test,
|