three-rb 0.1.0 → 0.2.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/CHANGELOG.md +33 -1
- data/README.md +66 -3
- data/docs/browser-runtime.md +92 -24
- data/docs/loaded-assets-design.md +1 -1
- data/docs/next-work.md +9 -5
- data/docs/publishing.md +119 -23
- data/docs/release-readiness.md +5 -3
- data/docs/standalone-browser-app.md +106 -0
- data/examples/browser/README.md +8 -0
- data/examples/browser/composition/main.rb +44 -64
- data/examples/browser/cube/main.rb +4 -34
- data/examples/browser/cubemap/assets/checker.svg +11 -0
- data/examples/browser/cubemap/main.rb +17 -40
- data/examples/browser/gltf/main.rb +30 -50
- data/examples/browser/picking/main.rb +27 -53
- data/examples/browser/postprocessing/main.rb +23 -42
- data/examples/browser/primitives/assets/checker.svg +11 -0
- data/examples/browser/primitives/main.rb +19 -42
- data/examples/browser/ruby/README.md +24 -0
- data/examples/browser/ruby/boot.mjs +6 -0
- data/examples/browser/ruby/index.html +142 -0
- data/examples/browser/ruby/main.rb +313 -0
- data/examples/browser/ruby/smoke_test.mjs +126 -0
- data/examples/browser/serialization/assets/checker.svg +11 -0
- data/examples/browser/serialization/main.rb +20 -42
- data/examples/browser/shared/boot.mjs +37 -5
- data/examples/browser/textures/assets/checker.svg +11 -0
- data/examples/browser/textures/assets/studio.hdr +5 -0
- data/examples/browser/textures/main.rb +23 -41
- data/exe/three-rb +56 -0
- data/lib/three/backends/threejs/materialization.rb +6 -0
- data/lib/three/backends/threejs/parameters.rb +17 -0
- data/lib/three/backends/threejs/ruby_wasm_adapter.rb +166 -59
- data/lib/three/backends/threejs/synchronization.rb +38 -4
- data/lib/three/backends/threejs.rb +24 -0
- data/lib/three/browser.rb +389 -0
- data/lib/three/constants.rb +6 -0
- data/lib/three/core/buffer_attribute.rb +5 -1
- data/lib/three/core/buffer_geometry.rb +29 -1
- data/lib/three/core/object3d.rb +39 -1
- data/lib/three/exporters/three_json_exporter.rb +3 -0
- data/lib/three/generators/browser_example.rb +396 -0
- data/lib/three/geometries/text_geometry.rb +41 -0
- data/lib/three/loaders/font_loader.rb +29 -0
- data/lib/three/loaders/three_json_loader.rb +92 -46
- data/lib/three/materials/material.rb +2 -1
- data/lib/three/math/matrix4.rb +27 -0
- data/lib/three/renderers/threejs_renderer.rb +19 -0
- data/lib/three/scenes/fog.rb +86 -0
- data/lib/three/scenes/scene.rb +19 -1
- data/lib/three/textures/texture.rb +2 -1
- data/lib/three/version.rb +1 -1
- data/lib/three.rb +4 -0
- data/package.json +2 -1
- metadata +26 -8
- /data/examples/browser/{assets → composition/assets}/checker.svg +0 -0
- /data/examples/browser/{assets → gltf/assets}/animated_triangle.gltf +0 -0
- /data/examples/browser/{assets → gltf/assets}/compressed_triangle.gltf +0 -0
- /data/examples/browser/{assets → gltf/assets}/triangle.gltf +0 -0
- /data/examples/browser/{assets → ruby/assets}/studio.hdr +0 -0
|
@@ -1,24 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
begin
|
|
6
|
-
JS.global[:__threeReady].await
|
|
7
|
-
|
|
8
|
-
require_relative "../../../lib/three"
|
|
9
|
-
|
|
10
|
-
document = JS.global[:document]
|
|
11
|
-
window = JS.global[:window]
|
|
12
|
-
viewport = document.call(:querySelector, "#viewport")
|
|
13
|
-
status = document.call(:querySelector, "#status")
|
|
14
|
-
status_dot = document.call(:querySelector, "#status-dot")
|
|
15
|
-
status[:textContent] = "Starting Ruby scene"
|
|
3
|
+
require_relative "../../../lib/three"
|
|
16
4
|
|
|
5
|
+
Three::Browser.run(starting: "Starting Ruby scene") do |app|
|
|
17
6
|
scene = Three::Scene.new
|
|
18
7
|
camera = Three::OrthographicCamera.new(-2.5, 2.5, 1.6, -1.6, near: 0.1, far: 100)
|
|
19
8
|
camera.position.z = 5
|
|
20
9
|
|
|
21
|
-
environment_texture = Three::Loaders::RGBELoader.new.load("/examples/browser/assets/studio.hdr")
|
|
10
|
+
environment_texture = Three::Loaders::RGBELoader.new.load("/examples/browser/textures/assets/studio.hdr")
|
|
22
11
|
scene.environment = environment_texture
|
|
23
12
|
|
|
24
13
|
scene.add(Three::AmbientLight.new(0xffffff, 0.45))
|
|
@@ -27,7 +16,7 @@ begin
|
|
|
27
16
|
key_light.position.set(2.5, 3.0, 4.0)
|
|
28
17
|
scene.add(key_light)
|
|
29
18
|
|
|
30
|
-
texture = Three::Loaders::TextureLoader.new.load("/examples/browser/assets/checker.svg")
|
|
19
|
+
texture = Three::Loaders::TextureLoader.new.load("/examples/browser/textures/assets/checker.svg")
|
|
31
20
|
texture.wrap_s = Three::RepeatWrapping
|
|
32
21
|
texture.wrap_t = Three::RepeatWrapping
|
|
33
22
|
texture.mag_filter = Three::NearestFilter
|
|
@@ -91,9 +80,7 @@ begin
|
|
|
91
80
|
)
|
|
92
81
|
renderer.set_clear_color(0x11161a, 1)
|
|
93
82
|
|
|
94
|
-
|
|
95
|
-
width = [viewport[:clientWidth].to_i, 1].max
|
|
96
|
-
height = [viewport[:clientHeight].to_i, 1].max
|
|
83
|
+
app.resize_renderer(renderer, camera) do |width, height, _aspect|
|
|
97
84
|
view_height = 3.4
|
|
98
85
|
view_width = view_height * width.to_f / height
|
|
99
86
|
|
|
@@ -102,25 +89,26 @@ begin
|
|
|
102
89
|
camera.top = view_height / 2
|
|
103
90
|
camera.bottom = -view_height / 2
|
|
104
91
|
camera.update_projection_matrix
|
|
105
|
-
renderer.set_size(width, height)
|
|
106
92
|
end
|
|
107
|
-
|
|
108
|
-
resize.call
|
|
109
|
-
window.call(:addEventListener, "resize", resize)
|
|
110
93
|
renderer.render(scene, camera)
|
|
111
94
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
95
|
+
app.expose(
|
|
96
|
+
{
|
|
97
|
+
renderer: renderer,
|
|
98
|
+
scene: scene,
|
|
99
|
+
camera: camera,
|
|
100
|
+
textured_mesh: mesh,
|
|
101
|
+
texture_material: material,
|
|
102
|
+
matcap_mesh: matcap_mesh,
|
|
103
|
+
matcap_material: matcap_material,
|
|
104
|
+
toon_mesh: toon_mesh,
|
|
105
|
+
toon_material: toon_material,
|
|
106
|
+
texture_example_texture: texture,
|
|
107
|
+
texture_example_environment: environment_texture,
|
|
108
|
+
texture_example_frame: 0
|
|
109
|
+
},
|
|
110
|
+
renderer: renderer
|
|
111
|
+
)
|
|
124
112
|
|
|
125
113
|
frame = 0
|
|
126
114
|
renderer.animation_loop do
|
|
@@ -130,13 +118,7 @@ begin
|
|
|
130
118
|
matcap_mesh.rotation.x += 0.005
|
|
131
119
|
matcap_mesh.rotation.y -= 0.009
|
|
132
120
|
toon_mesh.rotation.y += 0.012
|
|
133
|
-
|
|
121
|
+
app.set(:texture_example_frame, frame)
|
|
134
122
|
renderer.render(scene, camera)
|
|
135
123
|
end
|
|
136
|
-
|
|
137
|
-
status[:textContent] = "Running"
|
|
138
|
-
status_dot[:dataset][:state] = "running"
|
|
139
|
-
rescue StandardError => error
|
|
140
|
-
JS.global.call(:__threeRbBootFailed, error.message) if JS.global[:__threeRbBootFailed]
|
|
141
|
-
raise
|
|
142
124
|
end
|
data/exe/three-rb
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "optparse"
|
|
5
|
+
require "three/generators/browser_example"
|
|
6
|
+
|
|
7
|
+
def usage
|
|
8
|
+
<<~TEXT
|
|
9
|
+
Usage:
|
|
10
|
+
three-rb browser PATH [--force]
|
|
11
|
+
|
|
12
|
+
Commands:
|
|
13
|
+
browser PATH Generate a Ruby-only browser example at PATH.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
three-rb browser examples/browser/quickstart
|
|
17
|
+
three-rb browser examples/browser/ruby
|
|
18
|
+
TEXT
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
command = ARGV.shift
|
|
22
|
+
|
|
23
|
+
case command
|
|
24
|
+
when "browser", "browser-example"
|
|
25
|
+
options = { force: false }
|
|
26
|
+
parser = OptionParser.new do |opts|
|
|
27
|
+
opts.banner = "Usage: three-rb #{command} PATH [--force]"
|
|
28
|
+
opts.on("--force", "Overwrite generated example files") { options[:force] = true }
|
|
29
|
+
opts.on("-h", "--help", "Show help") do
|
|
30
|
+
puts opts
|
|
31
|
+
exit 0
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
begin
|
|
35
|
+
parser.parse!(ARGV)
|
|
36
|
+
rescue OptionParser::ParseError => error
|
|
37
|
+
warn error.message
|
|
38
|
+
warn parser
|
|
39
|
+
exit 1
|
|
40
|
+
end
|
|
41
|
+
target = ARGV.shift
|
|
42
|
+
|
|
43
|
+
unless target
|
|
44
|
+
warn parser
|
|
45
|
+
exit 1
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
Three::Generators::BrowserExample.new(target: target, force: options[:force]).call
|
|
49
|
+
when "-h", "--help", nil
|
|
50
|
+
puts usage
|
|
51
|
+
exit(command ? 0 : 1)
|
|
52
|
+
else
|
|
53
|
+
warn "Unknown command: #{command}"
|
|
54
|
+
warn usage
|
|
55
|
+
exit 1
|
|
56
|
+
end
|
|
@@ -30,6 +30,10 @@ module Three
|
|
|
30
30
|
@adapter.load_rgbe_texture(object.source, texture_parameters(object))
|
|
31
31
|
when Texture
|
|
32
32
|
@adapter.load_texture(object.source, texture_parameters(object))
|
|
33
|
+
when FogExp2
|
|
34
|
+
@adapter.new_fog_exp2(object.color.hex, object.density)
|
|
35
|
+
when Fog
|
|
36
|
+
@adapter.new_fog(object.color.hex, object.near, object.far)
|
|
33
37
|
when AmbientLight
|
|
34
38
|
@adapter.new_ambient_light(object.color.hex, object.intensity)
|
|
35
39
|
when DirectionalLight
|
|
@@ -67,6 +71,8 @@ module Three
|
|
|
67
71
|
parameters[:theta_start],
|
|
68
72
|
parameters[:theta_length]
|
|
69
73
|
)
|
|
74
|
+
when TextGeometry
|
|
75
|
+
@adapter.new_text_geometry(object.text, text_geometry_parameters(object))
|
|
70
76
|
when BufferGeometry
|
|
71
77
|
build_buffer_geometry(object)
|
|
72
78
|
when LineBasicMaterial
|
|
@@ -89,6 +89,23 @@ module Three
|
|
|
89
89
|
matrix: texture.matrix.to_a
|
|
90
90
|
}
|
|
91
91
|
end
|
|
92
|
+
|
|
93
|
+
def text_geometry_parameters(geometry)
|
|
94
|
+
parameters = geometry.parameters
|
|
95
|
+
{
|
|
96
|
+
font: parameters[:font].handle,
|
|
97
|
+
size: parameters[:size],
|
|
98
|
+
depth: parameters[:depth],
|
|
99
|
+
curveSegments: parameters[:curve_segments],
|
|
100
|
+
steps: parameters[:steps],
|
|
101
|
+
bevelEnabled: parameters[:bevel_enabled],
|
|
102
|
+
bevelThickness: parameters[:bevel_thickness],
|
|
103
|
+
bevelSize: parameters[:bevel_size],
|
|
104
|
+
bevelOffset: parameters[:bevel_offset],
|
|
105
|
+
bevelSegments: parameters[:bevel_segments],
|
|
106
|
+
direction: parameters[:direction]
|
|
107
|
+
}
|
|
108
|
+
end
|
|
92
109
|
end
|
|
93
110
|
|
|
94
111
|
include Parameters
|
|
@@ -33,6 +33,75 @@ module Three
|
|
|
33
33
|
specularIntensityMap
|
|
34
34
|
].freeze
|
|
35
35
|
|
|
36
|
+
ADDON_CONSTRUCTORS = {
|
|
37
|
+
orbit_controls: {
|
|
38
|
+
owner: "Three::Controls::OrbitControls",
|
|
39
|
+
global: :THREE_ORBIT_CONTROLS,
|
|
40
|
+
import: 'import { OrbitControls } from "three/addons/controls/OrbitControls.js";',
|
|
41
|
+
assignment: "globalThis.THREE_ORBIT_CONTROLS = OrbitControls;"
|
|
42
|
+
},
|
|
43
|
+
effect_composer: {
|
|
44
|
+
owner: "Three::Postprocessing::EffectComposer",
|
|
45
|
+
global: :THREE_EFFECT_COMPOSER,
|
|
46
|
+
import: 'import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";',
|
|
47
|
+
assignment: "globalThis.THREE_EFFECT_COMPOSER = EffectComposer;"
|
|
48
|
+
},
|
|
49
|
+
render_pass: {
|
|
50
|
+
owner: "Three::Postprocessing::RenderPass",
|
|
51
|
+
global: :THREE_RENDER_PASS,
|
|
52
|
+
import: 'import { RenderPass } from "three/addons/postprocessing/RenderPass.js";',
|
|
53
|
+
assignment: "globalThis.THREE_RENDER_PASS = RenderPass;"
|
|
54
|
+
},
|
|
55
|
+
unreal_bloom_pass: {
|
|
56
|
+
owner: "Three::Postprocessing::UnrealBloomPass",
|
|
57
|
+
global: :THREE_UNREAL_BLOOM_PASS,
|
|
58
|
+
import: 'import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js";',
|
|
59
|
+
assignment: "globalThis.THREE_UNREAL_BLOOM_PASS = UnrealBloomPass;"
|
|
60
|
+
},
|
|
61
|
+
dot_screen_pass: {
|
|
62
|
+
owner: "Three::Postprocessing::DotScreenPass",
|
|
63
|
+
global: :THREE_DOT_SCREEN_PASS,
|
|
64
|
+
import: 'import { DotScreenPass } from "three/addons/postprocessing/DotScreenPass.js";',
|
|
65
|
+
assignment: "globalThis.THREE_DOT_SCREEN_PASS = DotScreenPass;"
|
|
66
|
+
},
|
|
67
|
+
output_pass: {
|
|
68
|
+
owner: "Three::Postprocessing::OutputPass",
|
|
69
|
+
global: :THREE_OUTPUT_PASS,
|
|
70
|
+
import: 'import { OutputPass } from "three/addons/postprocessing/OutputPass.js";',
|
|
71
|
+
assignment: "globalThis.THREE_OUTPUT_PASS = OutputPass;"
|
|
72
|
+
},
|
|
73
|
+
gltf_loader: {
|
|
74
|
+
owner: "Three::Loaders::GLTFLoader",
|
|
75
|
+
global: :THREE_GLTF_LOADER,
|
|
76
|
+
import: 'import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";',
|
|
77
|
+
assignment: "globalThis.THREE_GLTF_LOADER = GLTFLoader;"
|
|
78
|
+
},
|
|
79
|
+
draco_loader: {
|
|
80
|
+
owner: "Three::Loaders::GLTFLoader with DRACO",
|
|
81
|
+
global: :THREE_DRACO_LOADER,
|
|
82
|
+
import: 'import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";',
|
|
83
|
+
assignment: "globalThis.THREE_DRACO_LOADER = DRACOLoader;"
|
|
84
|
+
},
|
|
85
|
+
rgbe_loader: {
|
|
86
|
+
owner: "Three::Loaders::RGBELoader",
|
|
87
|
+
global: :THREE_RGBE_LOADER,
|
|
88
|
+
import: 'import { HDRLoader } from "three/addons/loaders/HDRLoader.js";',
|
|
89
|
+
assignment: "globalThis.THREE_RGBE_LOADER = HDRLoader;"
|
|
90
|
+
},
|
|
91
|
+
font_loader: {
|
|
92
|
+
owner: "Three::Loaders::FontLoader",
|
|
93
|
+
global: :THREE_FONT_LOADER,
|
|
94
|
+
import: 'import { FontLoader } from "three/addons/loaders/FontLoader.js";',
|
|
95
|
+
assignment: "globalThis.THREE_FONT_LOADER = FontLoader;"
|
|
96
|
+
},
|
|
97
|
+
text_geometry: {
|
|
98
|
+
owner: "Three::TextGeometry",
|
|
99
|
+
global: :THREE_TEXT_GEOMETRY,
|
|
100
|
+
import: 'import { TextGeometry } from "three/addons/geometries/TextGeometry.js";',
|
|
101
|
+
assignment: "globalThis.THREE_TEXT_GEOMETRY = TextGeometry;"
|
|
102
|
+
}
|
|
103
|
+
}.freeze
|
|
104
|
+
|
|
36
105
|
def initialize(three: nil)
|
|
37
106
|
@three = three || default_three
|
|
38
107
|
end
|
|
@@ -55,6 +124,14 @@ module Three
|
|
|
55
124
|
renderer.call(:setClearColor, color, alpha)
|
|
56
125
|
end
|
|
57
126
|
|
|
127
|
+
def set_renderer_tone_mapping(renderer, value)
|
|
128
|
+
renderer[:toneMapping] = value
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def set_renderer_tone_mapping_exposure(renderer, value)
|
|
132
|
+
renderer[:toneMappingExposure] = value
|
|
133
|
+
end
|
|
134
|
+
|
|
58
135
|
def set_renderer_shadow_map(renderer, enabled: nil, type: nil, auto_update: nil)
|
|
59
136
|
shadow_map = renderer[:shadowMap]
|
|
60
137
|
shadow_map[:enabled] = enabled unless enabled.nil?
|
|
@@ -78,6 +155,10 @@ module Three
|
|
|
78
155
|
end
|
|
79
156
|
end
|
|
80
157
|
|
|
158
|
+
def add_event_listener(handle, type, callback)
|
|
159
|
+
handle.call(:addEventListener, type.to_s, callback)
|
|
160
|
+
end
|
|
161
|
+
|
|
81
162
|
def new_effect_composer(renderer)
|
|
82
163
|
effect_composer_constructor.new(renderer)
|
|
83
164
|
end
|
|
@@ -172,6 +253,18 @@ module Three
|
|
|
172
253
|
texture
|
|
173
254
|
end
|
|
174
255
|
|
|
256
|
+
def new_fog(color, near, far)
|
|
257
|
+
@three[:Fog].new(color, near, far)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def new_fog_exp2(color, density)
|
|
261
|
+
@three[:FogExp2].new(color, density)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def load_font(source)
|
|
265
|
+
font_loader_constructor.new.call(:loadAsync, source)
|
|
266
|
+
end
|
|
267
|
+
|
|
175
268
|
def load_gltf(source, draco_decoder_path: nil, draco_decoder_config: nil)
|
|
176
269
|
loader = gltf_loader_constructor.new
|
|
177
270
|
configure_draco_loader(loader, draco_decoder_path, draco_decoder_config) if draco_decoder_path
|
|
@@ -395,6 +488,10 @@ module Three
|
|
|
395
488
|
@three[:SphereGeometry].new(radius, width_segments, height_segments, phi_start, phi_length, theta_start, theta_length)
|
|
396
489
|
end
|
|
397
490
|
|
|
491
|
+
def new_text_geometry(text, parameters)
|
|
492
|
+
text_geometry_constructor.new(text, stringify_keys(parameters))
|
|
493
|
+
end
|
|
494
|
+
|
|
398
495
|
def new_buffer_geometry
|
|
399
496
|
@three[:BufferGeometry].new
|
|
400
497
|
end
|
|
@@ -428,6 +525,10 @@ module Three
|
|
|
428
525
|
geometry.call(:setDrawRange, start, count)
|
|
429
526
|
end
|
|
430
527
|
|
|
528
|
+
def center_geometry(geometry)
|
|
529
|
+
geometry.call(:center)
|
|
530
|
+
end
|
|
531
|
+
|
|
431
532
|
def new_mesh_basic_material(parameters)
|
|
432
533
|
@three[:MeshBasicMaterial].new(stringify_keys(parameters))
|
|
433
534
|
end
|
|
@@ -497,6 +598,15 @@ module Three
|
|
|
497
598
|
object[:scale].call(:set, *scale)
|
|
498
599
|
end
|
|
499
600
|
|
|
601
|
+
def set_object_matrix_auto_update(object, value)
|
|
602
|
+
object[:matrixAutoUpdate] = value
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def set_object_matrix(object, elements)
|
|
606
|
+
object[:matrix].call(:fromArray, js_array(elements))
|
|
607
|
+
object[:matrixWorldNeedsUpdate] = true
|
|
608
|
+
end
|
|
609
|
+
|
|
500
610
|
def update_perspective_camera(camera, fov, aspect, near, far, zoom)
|
|
501
611
|
camera[:fov] = fov
|
|
502
612
|
camera[:aspect] = aspect
|
|
@@ -580,6 +690,25 @@ module Three
|
|
|
580
690
|
scene[:environment] = environment
|
|
581
691
|
end
|
|
582
692
|
|
|
693
|
+
def set_scene_fog(scene, fog)
|
|
694
|
+
scene[:fog] = fog
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
def set_scene_override_material(scene, material)
|
|
698
|
+
scene[:overrideMaterial] = material
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
def update_fog(fog, color, near, far)
|
|
702
|
+
fog[:color].call(:setHex, color)
|
|
703
|
+
fog[:near] = near
|
|
704
|
+
fog[:far] = far
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
def update_fog_exp2(fog, color, density)
|
|
708
|
+
fog[:color].call(:setHex, color)
|
|
709
|
+
fog[:density] = density
|
|
710
|
+
end
|
|
711
|
+
|
|
583
712
|
def dispose(handle)
|
|
584
713
|
handle.call(:dispose) if handle.respond_to?(:call)
|
|
585
714
|
end
|
|
@@ -675,83 +804,35 @@ module Three
|
|
|
675
804
|
end
|
|
676
805
|
|
|
677
806
|
def orbit_controls_constructor
|
|
678
|
-
|
|
679
|
-
constructor = JS.global[:THREE_ORBIT_CONTROLS]
|
|
680
|
-
raise RuntimeError, "Three::Controls::OrbitControls requires globalThis.THREE_ORBIT_CONTROLS" if constructor.typeof == "undefined"
|
|
681
|
-
|
|
682
|
-
constructor
|
|
683
|
-
rescue LoadError
|
|
684
|
-
raise RuntimeError, "Three::Controls::OrbitControls requires ruby.wasm's js gem or an injected adapter"
|
|
807
|
+
addon_constructor(:orbit_controls)
|
|
685
808
|
end
|
|
686
809
|
|
|
687
810
|
def effect_composer_constructor
|
|
688
|
-
|
|
689
|
-
constructor = JS.global[:THREE_EFFECT_COMPOSER]
|
|
690
|
-
raise RuntimeError, "Three::Postprocessing::EffectComposer requires globalThis.THREE_EFFECT_COMPOSER" if constructor.typeof == "undefined"
|
|
691
|
-
|
|
692
|
-
constructor
|
|
693
|
-
rescue LoadError
|
|
694
|
-
raise RuntimeError, "Three::Postprocessing::EffectComposer requires ruby.wasm's js gem or an injected adapter"
|
|
811
|
+
addon_constructor(:effect_composer)
|
|
695
812
|
end
|
|
696
813
|
|
|
697
814
|
def render_pass_constructor
|
|
698
|
-
|
|
699
|
-
constructor = JS.global[:THREE_RENDER_PASS]
|
|
700
|
-
raise RuntimeError, "Three::Postprocessing::RenderPass requires globalThis.THREE_RENDER_PASS" if constructor.typeof == "undefined"
|
|
701
|
-
|
|
702
|
-
constructor
|
|
703
|
-
rescue LoadError
|
|
704
|
-
raise RuntimeError, "Three::Postprocessing::RenderPass requires ruby.wasm's js gem or an injected adapter"
|
|
815
|
+
addon_constructor(:render_pass)
|
|
705
816
|
end
|
|
706
817
|
|
|
707
818
|
def unreal_bloom_pass_constructor
|
|
708
|
-
|
|
709
|
-
constructor = JS.global[:THREE_UNREAL_BLOOM_PASS]
|
|
710
|
-
raise RuntimeError, "Three::Postprocessing::UnrealBloomPass requires globalThis.THREE_UNREAL_BLOOM_PASS" if constructor.typeof == "undefined"
|
|
711
|
-
|
|
712
|
-
constructor
|
|
713
|
-
rescue LoadError
|
|
714
|
-
raise RuntimeError, "Three::Postprocessing::UnrealBloomPass requires ruby.wasm's js gem or an injected adapter"
|
|
819
|
+
addon_constructor(:unreal_bloom_pass)
|
|
715
820
|
end
|
|
716
821
|
|
|
717
822
|
def dot_screen_pass_constructor
|
|
718
|
-
|
|
719
|
-
constructor = JS.global[:THREE_DOT_SCREEN_PASS]
|
|
720
|
-
raise RuntimeError, "Three::Postprocessing::DotScreenPass requires globalThis.THREE_DOT_SCREEN_PASS" if constructor.typeof == "undefined"
|
|
721
|
-
|
|
722
|
-
constructor
|
|
723
|
-
rescue LoadError
|
|
724
|
-
raise RuntimeError, "Three::Postprocessing::DotScreenPass requires ruby.wasm's js gem or an injected adapter"
|
|
823
|
+
addon_constructor(:dot_screen_pass)
|
|
725
824
|
end
|
|
726
825
|
|
|
727
826
|
def output_pass_constructor
|
|
728
|
-
|
|
729
|
-
constructor = JS.global[:THREE_OUTPUT_PASS]
|
|
730
|
-
raise RuntimeError, "Three::Postprocessing::OutputPass requires globalThis.THREE_OUTPUT_PASS" if constructor.typeof == "undefined"
|
|
731
|
-
|
|
732
|
-
constructor
|
|
733
|
-
rescue LoadError
|
|
734
|
-
raise RuntimeError, "Three::Postprocessing::OutputPass requires ruby.wasm's js gem or an injected adapter"
|
|
827
|
+
addon_constructor(:output_pass)
|
|
735
828
|
end
|
|
736
829
|
|
|
737
830
|
def gltf_loader_constructor
|
|
738
|
-
|
|
739
|
-
constructor = JS.global[:THREE_GLTF_LOADER]
|
|
740
|
-
raise RuntimeError, "Three::Loaders::GLTFLoader requires globalThis.THREE_GLTF_LOADER" if constructor.typeof == "undefined"
|
|
741
|
-
|
|
742
|
-
constructor
|
|
743
|
-
rescue LoadError
|
|
744
|
-
raise RuntimeError, "Three::Loaders::GLTFLoader requires ruby.wasm's js gem or an injected adapter"
|
|
831
|
+
addon_constructor(:gltf_loader)
|
|
745
832
|
end
|
|
746
833
|
|
|
747
834
|
def draco_loader_constructor
|
|
748
|
-
|
|
749
|
-
constructor = JS.global[:THREE_DRACO_LOADER]
|
|
750
|
-
raise RuntimeError, "Three::Loaders::GLTFLoader with DRACO requires globalThis.THREE_DRACO_LOADER" if constructor.typeof == "undefined"
|
|
751
|
-
|
|
752
|
-
constructor
|
|
753
|
-
rescue LoadError
|
|
754
|
-
raise RuntimeError, "Three::Loaders::GLTFLoader with DRACO requires ruby.wasm's js gem or an injected adapter"
|
|
835
|
+
addon_constructor(:draco_loader)
|
|
755
836
|
end
|
|
756
837
|
|
|
757
838
|
def configure_draco_loader(loader, decoder_path, decoder_config)
|
|
@@ -762,13 +843,39 @@ module Three
|
|
|
762
843
|
end
|
|
763
844
|
|
|
764
845
|
def rgbe_loader_constructor
|
|
846
|
+
addon_constructor(:rgbe_loader)
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
def font_loader_constructor
|
|
850
|
+
addon_constructor(:font_loader)
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
def text_geometry_constructor
|
|
854
|
+
addon_constructor(:text_geometry)
|
|
855
|
+
end
|
|
856
|
+
|
|
857
|
+
def addon_constructor(name)
|
|
858
|
+
addon = ADDON_CONSTRUCTORS.fetch(name)
|
|
765
859
|
require "js"
|
|
766
|
-
constructor = JS.global[:
|
|
767
|
-
raise RuntimeError,
|
|
860
|
+
constructor = JS.global[addon.fetch(:global)]
|
|
861
|
+
raise RuntimeError, missing_addon_constructor_message(addon) if js_undefined?(constructor)
|
|
768
862
|
|
|
769
863
|
constructor
|
|
770
864
|
rescue LoadError
|
|
771
|
-
raise RuntimeError, "
|
|
865
|
+
raise RuntimeError, "#{addon.fetch(:owner)} requires ruby.wasm's js gem or an injected adapter"
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
def js_undefined?(value)
|
|
869
|
+
value.respond_to?(:typeof) && value.typeof == "undefined"
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
def missing_addon_constructor_message(addon)
|
|
873
|
+
<<~MESSAGE.chomp
|
|
874
|
+
#{addon.fetch(:owner)} requires globalThis.#{addon.fetch(:global)}.
|
|
875
|
+
Add this to boot.mjs before loading Ruby:
|
|
876
|
+
#{addon.fetch(:import)}
|
|
877
|
+
#{addon.fetch(:assignment)}
|
|
878
|
+
MESSAGE
|
|
772
879
|
end
|
|
773
880
|
|
|
774
881
|
def resolve_canvas(canvas)
|
|
@@ -17,7 +17,12 @@ module Three
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
if object.dirty_field?(:transform)
|
|
20
|
-
@adapter.
|
|
20
|
+
@adapter.set_object_matrix_auto_update(handle, object.matrix_auto_update)
|
|
21
|
+
if object.matrix_auto_update
|
|
22
|
+
@adapter.set_object_transform(handle, object.position.to_a, object.quaternion.to_a, object.scale.to_a)
|
|
23
|
+
else
|
|
24
|
+
@adapter.set_object_matrix(handle, object.matrix.to_a)
|
|
25
|
+
end
|
|
21
26
|
end
|
|
22
27
|
|
|
23
28
|
if object.dirty_field?(:camera)
|
|
@@ -117,14 +122,28 @@ module Three
|
|
|
117
122
|
def sync_scene(scene, handle)
|
|
118
123
|
@adapter.set_scene_background(handle, scene.background ? sync(scene.background) : nil)
|
|
119
124
|
@adapter.set_scene_environment(handle, scene.environment ? sync(scene.environment) : nil)
|
|
125
|
+
@adapter.set_scene_fog(handle, scene.fog ? sync(scene.fog) : nil)
|
|
126
|
+
@adapter.set_scene_override_material(handle, scene.override_material ? sync(scene.override_material) : nil)
|
|
120
127
|
end
|
|
121
128
|
|
|
122
129
|
def scene_resource_dirty?(scene)
|
|
123
|
-
[scene.background, scene.environment].compact.any? do |resource|
|
|
130
|
+
[scene.background, scene.environment, scene.fog, scene.override_material].compact.any? do |resource|
|
|
124
131
|
resource.respond_to?(:dirty?) && resource.dirty?
|
|
125
132
|
end
|
|
126
133
|
end
|
|
127
134
|
|
|
135
|
+
def sync_fog(fog, handle)
|
|
136
|
+
return handle unless fog.dirty?
|
|
137
|
+
|
|
138
|
+
if fog.is_a?(FogExp2)
|
|
139
|
+
@adapter.update_fog_exp2(handle, fog.color.hex, fog.density)
|
|
140
|
+
else
|
|
141
|
+
@adapter.update_fog(handle, fog.color.hex, fog.near, fog.far)
|
|
142
|
+
end
|
|
143
|
+
fog.mark_clean!
|
|
144
|
+
handle
|
|
145
|
+
end
|
|
146
|
+
|
|
128
147
|
def sync_camera(object, handle)
|
|
129
148
|
case object
|
|
130
149
|
when OrthographicCamera
|
|
@@ -172,7 +191,10 @@ module Three
|
|
|
172
191
|
end
|
|
173
192
|
|
|
174
193
|
def sync_geometry(geometry, handle)
|
|
175
|
-
|
|
194
|
+
if built_in_geometry?(geometry)
|
|
195
|
+
sync_geometry_operations(geometry, handle)
|
|
196
|
+
return handle
|
|
197
|
+
end
|
|
176
198
|
return handle unless geometry_dirty?(geometry)
|
|
177
199
|
|
|
178
200
|
if geometry.dirty_field?(:all) || geometry.dirty_field?(:index) || geometry.index&.dirty?
|
|
@@ -203,10 +225,19 @@ module Three
|
|
|
203
225
|
@adapter.set_geometry_draw_range(handle, geometry.draw_range[:start], geometry.draw_range[:count])
|
|
204
226
|
end
|
|
205
227
|
|
|
228
|
+
sync_geometry_operations(geometry, handle)
|
|
206
229
|
geometry.mark_clean!
|
|
207
230
|
handle
|
|
208
231
|
end
|
|
209
232
|
|
|
233
|
+
def sync_geometry_operations(geometry, handle)
|
|
234
|
+
return unless geometry.respond_to?(:centered?) && geometry.centered?
|
|
235
|
+
return unless geometry.dirty_field?(:geometry_operations)
|
|
236
|
+
|
|
237
|
+
@adapter.center_geometry(handle)
|
|
238
|
+
geometry.mark_clean!(:geometry_operations)
|
|
239
|
+
end
|
|
240
|
+
|
|
210
241
|
def geometry_dirty?(geometry)
|
|
211
242
|
geometry.dirty? ||
|
|
212
243
|
geometry.index&.dirty? ||
|
|
@@ -214,7 +245,10 @@ module Three
|
|
|
214
245
|
end
|
|
215
246
|
|
|
216
247
|
def built_in_geometry?(geometry)
|
|
217
|
-
geometry.is_a?(BoxGeometry) ||
|
|
248
|
+
geometry.is_a?(BoxGeometry) ||
|
|
249
|
+
geometry.is_a?(PlaneGeometry) ||
|
|
250
|
+
geometry.is_a?(SphereGeometry) ||
|
|
251
|
+
geometry.is_a?(TextGeometry)
|
|
218
252
|
end
|
|
219
253
|
end
|
|
220
254
|
|
|
@@ -6,6 +6,7 @@ require_relative "../core/buffer_geometry"
|
|
|
6
6
|
require_relative "../geometries/box_geometry"
|
|
7
7
|
require_relative "../geometries/plane_geometry"
|
|
8
8
|
require_relative "../geometries/sphere_geometry"
|
|
9
|
+
require_relative "../geometries/text_geometry"
|
|
9
10
|
require_relative "../lights/ambient_light"
|
|
10
11
|
require_relative "../lights/directional_light"
|
|
11
12
|
require_relative "../lights/hemisphere_light"
|
|
@@ -28,6 +29,7 @@ require_relative "../objects/line"
|
|
|
28
29
|
require_relative "../objects/mesh"
|
|
29
30
|
require_relative "../objects/points"
|
|
30
31
|
require_relative "../objects/sprite"
|
|
32
|
+
require_relative "../scenes/fog"
|
|
31
33
|
require_relative "../scenes/scene"
|
|
32
34
|
require_relative "../textures/cube_texture"
|
|
33
35
|
require_relative "../textures/rgbe_texture"
|
|
@@ -93,6 +95,14 @@ module Three
|
|
|
93
95
|
@adapter.set_clear_color(renderer_handle, color, alpha)
|
|
94
96
|
end
|
|
95
97
|
|
|
98
|
+
def set_renderer_tone_mapping(renderer_handle, value)
|
|
99
|
+
@adapter.set_renderer_tone_mapping(renderer_handle, value)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def set_renderer_tone_mapping_exposure(renderer_handle, value)
|
|
103
|
+
@adapter.set_renderer_tone_mapping_exposure(renderer_handle, value)
|
|
104
|
+
end
|
|
105
|
+
|
|
96
106
|
def set_renderer_shadow_map(renderer_handle, enabled: nil, type: nil, auto_update: nil)
|
|
97
107
|
@adapter.set_renderer_shadow_map(renderer_handle, enabled: enabled, type: type, auto_update: auto_update)
|
|
98
108
|
end
|
|
@@ -247,6 +257,7 @@ module Three
|
|
|
247
257
|
return @handles[key] if key && @handles.key?(key)
|
|
248
258
|
|
|
249
259
|
handle = build_handle(object)
|
|
260
|
+
sync_geometry_operations(object, handle) if object.is_a?(BufferGeometry)
|
|
250
261
|
@handles[key] = handle if key
|
|
251
262
|
register_object_handle(object, handle)
|
|
252
263
|
mark_clean_after_materialize(object)
|
|
@@ -259,6 +270,8 @@ module Three
|
|
|
259
270
|
case object
|
|
260
271
|
when Object3D
|
|
261
272
|
sync_object3d(object, handle)
|
|
273
|
+
when Fog
|
|
274
|
+
sync_fog(object, handle)
|
|
262
275
|
when BufferGeometry
|
|
263
276
|
sync_geometry(object, handle)
|
|
264
277
|
when Texture
|
|
@@ -281,6 +294,17 @@ module Three
|
|
|
281
294
|
handle
|
|
282
295
|
end
|
|
283
296
|
|
|
297
|
+
def add_event_listener(object, type, callback)
|
|
298
|
+
raise ArgumentError, "callback is required" unless callback
|
|
299
|
+
|
|
300
|
+
@adapter.add_event_listener(materialize(object), type, callback)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def cached?(object)
|
|
304
|
+
key = cache_key(object)
|
|
305
|
+
key ? @handles.key?(key) : false
|
|
306
|
+
end
|
|
307
|
+
|
|
284
308
|
def traverse_handles(object, &block)
|
|
285
309
|
return enum_for(:traverse_handles, object) unless block
|
|
286
310
|
|