three-rb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +19 -0
- data/LICENSE +21 -0
- data/README.md +153 -0
- data/docs/browser-runtime.md +153 -0
- data/docs/implementation-plan.md +874 -0
- data/docs/loaded-assets-design.md +400 -0
- data/docs/next-work.md +107 -0
- data/docs/publishing.md +64 -0
- data/docs/release-readiness.md +83 -0
- data/examples/browser/README.md +54 -0
- data/examples/browser/assets/animated_triangle.gltf +123 -0
- data/examples/browser/assets/checker.svg +11 -0
- data/examples/browser/assets/compressed_triangle.gltf +74 -0
- data/examples/browser/assets/studio.hdr +5 -0
- data/examples/browser/assets/triangle.gltf +67 -0
- data/examples/browser/composition/README.md +35 -0
- data/examples/browser/composition/boot.mjs +6 -0
- data/examples/browser/composition/index.html +136 -0
- data/examples/browser/composition/main.rb +216 -0
- data/examples/browser/composition/smoke_test.mjs +266 -0
- data/examples/browser/cube/README.md +41 -0
- data/examples/browser/cube/boot.mjs +6 -0
- data/examples/browser/cube/index.html +142 -0
- data/examples/browser/cube/main.rb +62 -0
- data/examples/browser/cube/smoke_test.mjs +99 -0
- data/examples/browser/cubemap/README.md +23 -0
- data/examples/browser/cubemap/boot.mjs +6 -0
- data/examples/browser/cubemap/index.html +142 -0
- data/examples/browser/cubemap/main.rb +84 -0
- data/examples/browser/cubemap/smoke_test.mjs +91 -0
- data/examples/browser/gltf/README.md +23 -0
- data/examples/browser/gltf/boot.mjs +6 -0
- data/examples/browser/gltf/index.html +142 -0
- data/examples/browser/gltf/main.rb +105 -0
- data/examples/browser/gltf/smoke_test.mjs +162 -0
- data/examples/browser/picking/README.md +33 -0
- data/examples/browser/picking/boot.mjs +6 -0
- data/examples/browser/picking/index.html +142 -0
- data/examples/browser/picking/main.rb +113 -0
- data/examples/browser/picking/smoke_test.mjs +78 -0
- data/examples/browser/postprocessing/README.md +26 -0
- data/examples/browser/postprocessing/boot.mjs +6 -0
- data/examples/browser/postprocessing/index.html +142 -0
- data/examples/browser/postprocessing/main.rb +117 -0
- data/examples/browser/postprocessing/smoke_test.mjs +121 -0
- data/examples/browser/primitives/README.md +33 -0
- data/examples/browser/primitives/boot.mjs +6 -0
- data/examples/browser/primitives/index.html +142 -0
- data/examples/browser/primitives/main.rb +116 -0
- data/examples/browser/primitives/smoke_test.mjs +113 -0
- data/examples/browser/serialization/README.md +33 -0
- data/examples/browser/serialization/boot.mjs +6 -0
- data/examples/browser/serialization/index.html +142 -0
- data/examples/browser/serialization/main.rb +97 -0
- data/examples/browser/serialization/smoke_test.mjs +67 -0
- data/examples/browser/shared/boot.mjs +79 -0
- data/examples/browser/shared/smoke_test_helpers.mjs +151 -0
- data/examples/browser/textures/README.md +35 -0
- data/examples/browser/textures/boot.mjs +6 -0
- data/examples/browser/textures/index.html +142 -0
- data/examples/browser/textures/main.rb +142 -0
- data/examples/browser/textures/smoke_test.mjs +189 -0
- data/lib/three/animation/animation_action.rb +57 -0
- data/lib/three/animation/animation_clip.rb +22 -0
- data/lib/three/animation/animation_mixer.rb +43 -0
- data/lib/three/backends/base.rb +87 -0
- data/lib/three/backends/threejs/materialization.rb +143 -0
- data/lib/three/backends/threejs/parameters.rb +97 -0
- data/lib/three/backends/threejs/resource_management.rb +69 -0
- data/lib/three/backends/threejs/ruby_wasm_adapter.rb +873 -0
- data/lib/three/backends/threejs/synchronization.rb +224 -0
- data/lib/three/backends/threejs.rb +365 -0
- data/lib/three/cameras/camera.rb +39 -0
- data/lib/three/cameras/orthographic_camera.rb +107 -0
- data/lib/three/cameras/perspective_camera.rb +137 -0
- data/lib/three/constants.rb +40 -0
- data/lib/three/controls/orbit_controls.rb +118 -0
- data/lib/three/core/buffer_attribute.rb +151 -0
- data/lib/three/core/buffer_geometry.rb +181 -0
- data/lib/three/core/clock.rb +58 -0
- data/lib/three/core/event_dispatcher.rb +57 -0
- data/lib/three/core/layers.rb +75 -0
- data/lib/three/core/object3d.rb +331 -0
- data/lib/three/core/raycaster.rb +73 -0
- data/lib/three/dirty.rb +58 -0
- data/lib/three/exporters/three_json_exporter.rb +187 -0
- data/lib/three/geometries/box_geometry.rb +97 -0
- data/lib/three/geometries/plane_geometry.rb +70 -0
- data/lib/three/geometries/sphere_geometry.rb +107 -0
- data/lib/three/lights/ambient_light.rb +12 -0
- data/lib/three/lights/directional_light.rb +38 -0
- data/lib/three/lights/hemisphere_light.rb +34 -0
- data/lib/three/lights/light.rb +85 -0
- data/lib/three/lights/point_light.rb +33 -0
- data/lib/three/loaders/cube_texture_loader.rb +13 -0
- data/lib/three/loaders/gltf_loader.rb +48 -0
- data/lib/three/loaders/rgbe_loader.rb +15 -0
- data/lib/three/loaders/texture_loader.rb +13 -0
- data/lib/three/loaders/three_json_loader.rb +409 -0
- data/lib/three/materials/line_basic_material.rb +65 -0
- data/lib/three/materials/material.rb +158 -0
- data/lib/three/materials/mesh_basic_material.rb +64 -0
- data/lib/three/materials/mesh_lambert_material.rb +71 -0
- data/lib/three/materials/mesh_matcap_material.rb +86 -0
- data/lib/three/materials/mesh_normal_material.rb +42 -0
- data/lib/three/materials/mesh_phong_material.rb +119 -0
- data/lib/three/materials/mesh_physical_material.rb +155 -0
- data/lib/three/materials/mesh_standard_material.rb +149 -0
- data/lib/three/materials/mesh_toon_material.rb +98 -0
- data/lib/three/materials/points_material.rb +74 -0
- data/lib/three/materials/shadow_material.rb +45 -0
- data/lib/three/materials/sprite_material.rb +75 -0
- data/lib/three/math/color.rb +133 -0
- data/lib/three/math/euler.rb +197 -0
- data/lib/three/math/math_utils.rb +36 -0
- data/lib/three/math/matrix3.rb +255 -0
- data/lib/three/math/matrix4.rb +448 -0
- data/lib/three/math/quaternion.rb +277 -0
- data/lib/three/math/vector2.rb +95 -0
- data/lib/three/math/vector3.rb +396 -0
- data/lib/three/objects/external_object3d.rb +28 -0
- data/lib/three/objects/group.rb +12 -0
- data/lib/three/objects/instanced_mesh.rb +110 -0
- data/lib/three/objects/line.rb +41 -0
- data/lib/three/objects/mesh.rb +45 -0
- data/lib/three/objects/points.rb +41 -0
- data/lib/three/objects/sprite.rb +57 -0
- data/lib/three/postprocessing/dot_screen_pass.rb +83 -0
- data/lib/three/postprocessing/effect_composer.rb +56 -0
- data/lib/three/postprocessing/output_pass.rb +40 -0
- data/lib/three/postprocessing/render_pass.rb +42 -0
- data/lib/three/postprocessing/unreal_bloom_pass.rb +56 -0
- data/lib/three/renderers/renderer.rb +11 -0
- data/lib/three/renderers/threejs_renderer.rb +85 -0
- data/lib/three/scenes/scene.rb +29 -0
- data/lib/three/textures/cube_texture.rb +72 -0
- data/lib/three/textures/rgbe_texture.rb +45 -0
- data/lib/three/textures/texture.rb +200 -0
- data/lib/three/version.rb +5 -0
- data/lib/three-rb.rb +3 -0
- data/lib/three.rb +77 -0
- data/package.json +30 -0
- data/pnpm-lock.yaml +86 -0
- metadata +216 -0
|
@@ -0,0 +1,873 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Three
|
|
4
|
+
module Backends
|
|
5
|
+
class ThreeJS
|
|
6
|
+
class RubyWasmAdapter
|
|
7
|
+
TEXTURE_SLOTS = %w[
|
|
8
|
+
map
|
|
9
|
+
normalMap
|
|
10
|
+
roughnessMap
|
|
11
|
+
metalnessMap
|
|
12
|
+
aoMap
|
|
13
|
+
emissiveMap
|
|
14
|
+
alphaMap
|
|
15
|
+
bumpMap
|
|
16
|
+
displacementMap
|
|
17
|
+
envMap
|
|
18
|
+
gradientMap
|
|
19
|
+
lightMap
|
|
20
|
+
matcap
|
|
21
|
+
specularMap
|
|
22
|
+
anisotropyMap
|
|
23
|
+
clearcoatMap
|
|
24
|
+
clearcoatNormalMap
|
|
25
|
+
clearcoatRoughnessMap
|
|
26
|
+
transmissionMap
|
|
27
|
+
thicknessMap
|
|
28
|
+
iridescenceMap
|
|
29
|
+
iridescenceThicknessMap
|
|
30
|
+
sheenColorMap
|
|
31
|
+
sheenRoughnessMap
|
|
32
|
+
specularColorMap
|
|
33
|
+
specularIntensityMap
|
|
34
|
+
].freeze
|
|
35
|
+
|
|
36
|
+
def initialize(three: nil)
|
|
37
|
+
@three = three || default_three
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def new_webgl_renderer(options = {})
|
|
41
|
+
parameters = stringify_keys(options)
|
|
42
|
+
parameters["canvas"] = resolve_canvas(options[:canvas] || options["canvas"]) if options[:canvas] || options["canvas"]
|
|
43
|
+
@three[:WebGLRenderer].new(parameters)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def renderer_dom_element(renderer)
|
|
47
|
+
renderer[:domElement]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def set_renderer_size(renderer, width, height)
|
|
51
|
+
renderer.call(:setSize, width, height)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def set_clear_color(renderer, color, alpha = 1)
|
|
55
|
+
renderer.call(:setClearColor, color, alpha)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def set_renderer_shadow_map(renderer, enabled: nil, type: nil, auto_update: nil)
|
|
59
|
+
shadow_map = renderer[:shadowMap]
|
|
60
|
+
shadow_map[:enabled] = enabled unless enabled.nil?
|
|
61
|
+
shadow_map[:type] = type unless type.nil?
|
|
62
|
+
shadow_map[:autoUpdate] = auto_update unless auto_update.nil?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def set_animation_loop(renderer, callback)
|
|
66
|
+
renderer.call(:setAnimationLoop, callback)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def render(renderer, scene, camera)
|
|
70
|
+
render_helper = JS.global[:__threeRbRender]
|
|
71
|
+
if render_helper.typeof == "function"
|
|
72
|
+
JS.global[:__threeRbCurrentRenderer] = renderer
|
|
73
|
+
JS.global[:__threeRbCurrentScene] = scene
|
|
74
|
+
JS.global[:__threeRbCurrentCamera] = camera
|
|
75
|
+
JS.global.call(:__threeRbRender)
|
|
76
|
+
else
|
|
77
|
+
renderer.call(:render, scene, camera)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def new_effect_composer(renderer)
|
|
82
|
+
effect_composer_constructor.new(renderer)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def effect_composer_add_pass(composer, pass)
|
|
86
|
+
composer.call(:addPass, pass)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def effect_composer_set_size(composer, width, height)
|
|
90
|
+
composer.call(:setSize, width, height)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def effect_composer_render(composer)
|
|
94
|
+
render_helper = JS.global[:__threeRbRenderComposer]
|
|
95
|
+
if render_helper.typeof == "function"
|
|
96
|
+
JS.global[:__threeRbCurrentComposer] = composer
|
|
97
|
+
JS.global.call(:__threeRbRenderComposer)
|
|
98
|
+
else
|
|
99
|
+
composer.call(:render)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def dispose_effect_composer(composer)
|
|
104
|
+
composer.call(:dispose)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def new_render_pass(scene, camera)
|
|
108
|
+
render_pass_constructor.new(scene, camera)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def new_unreal_bloom_pass(resolution, strength, radius, threshold)
|
|
112
|
+
unreal_bloom_pass_constructor.new(@three[:Vector2].new(*resolution), strength, radius, threshold)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def new_dot_screen_pass(center, angle, scale)
|
|
116
|
+
dot_screen_pass_constructor.new(@three[:Vector2].new(*center), angle, scale)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def new_output_pass
|
|
120
|
+
output_pass_constructor.new
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def set_postprocessing_pass_property(pass, name, value)
|
|
124
|
+
pass[name] = value
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def set_postprocessing_pass_uniform(pass, name, value)
|
|
128
|
+
uniform = pass[:uniforms][name]
|
|
129
|
+
return unless js_present?(uniform)
|
|
130
|
+
|
|
131
|
+
target = uniform[:value]
|
|
132
|
+
if value.respond_to?(:to_a) && js_present?(target) && target.respond_to?(:call)
|
|
133
|
+
target.call(:set, *value.to_a)
|
|
134
|
+
else
|
|
135
|
+
uniform[:value] = value
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def new_orbit_controls(camera, dom_element)
|
|
140
|
+
constructor = orbit_controls_constructor
|
|
141
|
+
dom_element ? constructor.new(camera, dom_element) : constructor.new(camera)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def new_raycaster
|
|
145
|
+
@three[:Raycaster].new
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def set_raycaster_from_camera(raycaster, coords, camera)
|
|
149
|
+
raycaster.call(:setFromCamera, @three[:Vector2].new(*coords), camera)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def intersect_objects(raycaster, objects, recursive: false)
|
|
153
|
+
raycaster.call(:intersectObjects, js_array(objects), recursive).to_a
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def load_texture(source, parameters = {})
|
|
157
|
+
texture = @three[:TextureLoader].new.call(:load, source)
|
|
158
|
+
update_texture(texture, parameters)
|
|
159
|
+
texture
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def load_cube_texture(sources, parameters = {})
|
|
163
|
+
texture = @three[:CubeTextureLoader].new.call(:load, js_array(sources))
|
|
164
|
+
update_texture(texture, parameters)
|
|
165
|
+
texture
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def load_rgbe_texture(source, parameters = {})
|
|
169
|
+
texture = rgbe_loader_constructor.new.call(:loadAsync, source)
|
|
170
|
+
texture = texture.await if texture.respond_to?(:await)
|
|
171
|
+
update_texture(texture, parameters)
|
|
172
|
+
texture
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def load_gltf(source, draco_decoder_path: nil, draco_decoder_config: nil)
|
|
176
|
+
loader = gltf_loader_constructor.new
|
|
177
|
+
configure_draco_loader(loader, draco_decoder_path, draco_decoder_config) if draco_decoder_path
|
|
178
|
+
loader.call(:loadAsync, source)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def gltf_animations(gltf)
|
|
182
|
+
animations = gltf[:animations]
|
|
183
|
+
js_present?(animations) ? animations.to_a : []
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def animation_clip_name(clip)
|
|
187
|
+
value = clip[:name]
|
|
188
|
+
js_present?(value) ? value.to_s : ""
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def animation_clip_duration(clip)
|
|
192
|
+
value = clip[:duration]
|
|
193
|
+
js_present?(value) ? value.to_f : 0.0
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def new_animation_mixer(root)
|
|
197
|
+
@three[:AnimationMixer].new(root)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def animation_mixer_clip_action(mixer, clip, root = nil)
|
|
201
|
+
root ? mixer.call(:clipAction, clip, root) : mixer.call(:clipAction, clip)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def update_animation_mixer(mixer, delta)
|
|
205
|
+
mixer.call(:update, delta)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def stop_all_animation_actions(mixer)
|
|
209
|
+
mixer.call(:stopAllAction)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def uncache_animation_root(mixer, root)
|
|
213
|
+
mixer.call(:uncacheRoot, root)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def set_animation_action_property(action, name, value)
|
|
217
|
+
action[name] = value
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def play_animation_action(action)
|
|
221
|
+
action.call(:play)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def stop_animation_action(action)
|
|
225
|
+
action.call(:stop)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def reset_animation_action(action)
|
|
229
|
+
action.call(:reset)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def fade_in_animation_action(action, duration)
|
|
233
|
+
action.call(:fadeIn, duration)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def fade_out_animation_action(action, duration)
|
|
237
|
+
action.call(:fadeOut, duration)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def update_texture(texture, parameters)
|
|
241
|
+
texture[:mapping] = parameters[:mapping] unless parameters[:mapping].nil?
|
|
242
|
+
texture[:colorSpace] = parameters[:color_space] unless parameters[:color_space].nil?
|
|
243
|
+
texture[:flipY] = parameters[:flip_y] unless parameters[:flip_y].nil?
|
|
244
|
+
texture[:wrapS] = parameters[:wrap_s] unless parameters[:wrap_s].nil?
|
|
245
|
+
texture[:wrapT] = parameters[:wrap_t] unless parameters[:wrap_t].nil?
|
|
246
|
+
texture[:magFilter] = parameters[:mag_filter] unless parameters[:mag_filter].nil?
|
|
247
|
+
texture[:minFilter] = parameters[:min_filter] unless parameters[:min_filter].nil?
|
|
248
|
+
texture[:offset].call(:set, *parameters[:offset]) if parameters[:offset]
|
|
249
|
+
texture[:repeat].call(:set, *parameters[:repeat]) if parameters[:repeat]
|
|
250
|
+
texture[:center].call(:set, *parameters[:center]) if parameters[:center]
|
|
251
|
+
texture[:rotation] = parameters[:rotation] unless parameters[:rotation].nil?
|
|
252
|
+
texture[:matrixAutoUpdate] = parameters[:matrix_auto_update] unless parameters[:matrix_auto_update].nil?
|
|
253
|
+
texture[:matrix].call(:fromArray, js_array(parameters[:matrix])) if parameters[:matrix]
|
|
254
|
+
texture.call(:updateMatrix) if parameters[:matrix_auto_update]
|
|
255
|
+
texture[:needsUpdate] = true
|
|
256
|
+
texture
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def set_control_property(control, name, value)
|
|
260
|
+
control[name] = value
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def set_orbit_controls_target(control, target)
|
|
264
|
+
control[:target].call(:set, *target)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def update_controls(control)
|
|
268
|
+
control.call(:update)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def dispose_controls(control)
|
|
272
|
+
control.call(:dispose)
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def object_transform(object)
|
|
276
|
+
[
|
|
277
|
+
js_vector_to_a(object[:position], 3),
|
|
278
|
+
js_vector_to_a(object[:quaternion], 4),
|
|
279
|
+
js_vector_to_a(object[:scale], 3)
|
|
280
|
+
]
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def new_scene
|
|
284
|
+
@three[:Scene].new
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def new_group
|
|
288
|
+
@three[:Group].new
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def new_object3d
|
|
292
|
+
@three[:Object3D].new
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def new_perspective_camera(fov, aspect, near, far)
|
|
296
|
+
@three[:PerspectiveCamera].new(fov, aspect, near, far)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def new_orthographic_camera(left, right, top, bottom, near, far)
|
|
300
|
+
@three[:OrthographicCamera].new(left, right, top, bottom, near, far)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def new_ambient_light(color, intensity)
|
|
304
|
+
@three[:AmbientLight].new(color, intensity)
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def new_directional_light(color, intensity)
|
|
308
|
+
@three[:DirectionalLight].new(color, intensity)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def new_point_light(color, intensity, distance, decay)
|
|
312
|
+
@three[:PointLight].new(color, intensity, distance, decay)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def new_hemisphere_light(sky_color, ground_color, intensity)
|
|
316
|
+
@three[:HemisphereLight].new(sky_color, ground_color, intensity)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def new_mesh(geometry, material)
|
|
320
|
+
@three[:Mesh].new(geometry, material)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def new_line(geometry, material)
|
|
324
|
+
@three[:Line].new(geometry, material)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def new_points(geometry, material)
|
|
328
|
+
@three[:Points].new(geometry, material)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def new_sprite(material)
|
|
332
|
+
@three[:Sprite].new(material)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def new_instanced_mesh(geometry, material, count)
|
|
336
|
+
@three[:InstancedMesh].new(geometry, material, count)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def set_object_geometry(object, geometry)
|
|
340
|
+
object[:geometry] = geometry
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def set_object_material(object, material)
|
|
344
|
+
object[:material] = material
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def set_mesh_geometry(mesh, geometry)
|
|
348
|
+
set_object_geometry(mesh, geometry)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def set_mesh_material(mesh, material)
|
|
352
|
+
set_object_material(mesh, material)
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def set_sprite_center(sprite, center)
|
|
356
|
+
sprite[:center].call(:set, *center)
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def set_object_layers(object, mask)
|
|
360
|
+
object[:layers][:mask] = mask if js_present?(object[:layers])
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def set_instanced_mesh_count(mesh, count)
|
|
364
|
+
mesh[:count] = count
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def set_instanced_mesh_matrix_at(mesh, index, elements)
|
|
368
|
+
matrix = @three[:Matrix4].new
|
|
369
|
+
matrix.call(:fromArray, js_array(elements))
|
|
370
|
+
mesh.call(:setMatrixAt, index, matrix)
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def set_instanced_mesh_instance_matrix_needs_update(mesh, value)
|
|
374
|
+
mesh[:instanceMatrix][:needsUpdate] = value
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def set_instanced_mesh_color_at(mesh, index, color)
|
|
378
|
+
mesh.call(:setColorAt, index, @three[:Color].new(*color))
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def set_instanced_mesh_instance_color_needs_update(mesh, value)
|
|
382
|
+
instance_color = mesh[:instanceColor]
|
|
383
|
+
instance_color[:needsUpdate] = value if js_present?(instance_color)
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
def new_box_geometry(width, height, depth, width_segments, height_segments, depth_segments)
|
|
387
|
+
@three[:BoxGeometry].new(width, height, depth, width_segments, height_segments, depth_segments)
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def new_plane_geometry(width, height, width_segments, height_segments)
|
|
391
|
+
@three[:PlaneGeometry].new(width, height, width_segments, height_segments)
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def new_sphere_geometry(radius, width_segments, height_segments, phi_start, phi_length, theta_start, theta_length)
|
|
395
|
+
@three[:SphereGeometry].new(radius, width_segments, height_segments, phi_start, phi_length, theta_start, theta_length)
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def new_buffer_geometry
|
|
399
|
+
@three[:BufferGeometry].new
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def new_buffer_attribute(component_type, array, item_size, normalized)
|
|
403
|
+
typed_array = typed_array(component_type, array)
|
|
404
|
+
@three[:BufferAttribute].new(typed_array, item_size, normalized)
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def set_geometry_index(geometry, attribute)
|
|
408
|
+
geometry.call(:setIndex, attribute)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def set_geometry_attribute(geometry, name, attribute)
|
|
412
|
+
geometry.call(:setAttribute, name.to_s, attribute)
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def delete_geometry_attribute(geometry, name)
|
|
416
|
+
geometry.call(:deleteAttribute, name.to_s)
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def clear_geometry_groups(geometry)
|
|
420
|
+
geometry.call(:clearGroups)
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def add_geometry_group(geometry, start, count, material_index)
|
|
424
|
+
geometry.call(:addGroup, start, count, material_index)
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def set_geometry_draw_range(geometry, start, count)
|
|
428
|
+
geometry.call(:setDrawRange, start, count)
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def new_mesh_basic_material(parameters)
|
|
432
|
+
@three[:MeshBasicMaterial].new(stringify_keys(parameters))
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def new_line_basic_material(parameters)
|
|
436
|
+
@three[:LineBasicMaterial].new(stringify_keys(parameters))
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def new_mesh_lambert_material(parameters)
|
|
440
|
+
@three[:MeshLambertMaterial].new(stringify_keys(parameters))
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
def new_mesh_matcap_material(parameters)
|
|
444
|
+
@three[:MeshMatcapMaterial].new(stringify_keys(parameters))
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
def new_mesh_normal_material(parameters)
|
|
448
|
+
@three[:MeshNormalMaterial].new(stringify_keys(parameters))
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def new_mesh_phong_material(parameters)
|
|
452
|
+
@three[:MeshPhongMaterial].new(stringify_keys(parameters))
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
def new_mesh_physical_material(parameters)
|
|
456
|
+
@three[:MeshPhysicalMaterial].new(stringify_keys(parameters))
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
def new_mesh_standard_material(parameters)
|
|
460
|
+
@three[:MeshStandardMaterial].new(stringify_keys(parameters))
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def new_mesh_toon_material(parameters)
|
|
464
|
+
material = @three[:MeshToonMaterial].new(stringify_keys(parameters))
|
|
465
|
+
update_material(material, parameters)
|
|
466
|
+
material
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
def new_points_material(parameters)
|
|
470
|
+
@three[:PointsMaterial].new(stringify_keys(parameters))
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def new_shadow_material(parameters)
|
|
474
|
+
@three[:ShadowMaterial].new(stringify_keys(parameters))
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def new_sprite_material(parameters)
|
|
478
|
+
@three[:SpriteMaterial].new(stringify_keys(parameters))
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
def set_object_name(object, name)
|
|
482
|
+
object[:name] = name
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
def set_object_visible(object, visible)
|
|
486
|
+
object[:visible] = visible
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
def set_object_shadow(object, cast_shadow, receive_shadow)
|
|
490
|
+
object[:castShadow] = cast_shadow
|
|
491
|
+
object[:receiveShadow] = receive_shadow
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def set_object_transform(object, position, quaternion, scale)
|
|
495
|
+
object[:position].call(:set, *position)
|
|
496
|
+
object[:quaternion].call(:set, *quaternion)
|
|
497
|
+
object[:scale].call(:set, *scale)
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
def update_perspective_camera(camera, fov, aspect, near, far, zoom)
|
|
501
|
+
camera[:fov] = fov
|
|
502
|
+
camera[:aspect] = aspect
|
|
503
|
+
camera[:near] = near
|
|
504
|
+
camera[:far] = far
|
|
505
|
+
camera[:zoom] = zoom
|
|
506
|
+
camera.call(:updateProjectionMatrix)
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def update_orthographic_camera(camera, left, right, top, bottom, near, far, zoom)
|
|
510
|
+
camera[:left] = left
|
|
511
|
+
camera[:right] = right
|
|
512
|
+
camera[:top] = top
|
|
513
|
+
camera[:bottom] = bottom
|
|
514
|
+
camera[:near] = near
|
|
515
|
+
camera[:far] = far
|
|
516
|
+
camera[:zoom] = zoom
|
|
517
|
+
camera.call(:updateProjectionMatrix)
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
def update_light(light, color, intensity)
|
|
521
|
+
light[:color].call(:setHex, color)
|
|
522
|
+
light[:intensity] = intensity
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
def update_point_light(light, color, intensity, distance, decay)
|
|
526
|
+
update_light(light, color, intensity)
|
|
527
|
+
light[:distance] = distance
|
|
528
|
+
light[:decay] = decay
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
def update_hemisphere_light(light, sky_color, ground_color, intensity)
|
|
532
|
+
update_light(light, sky_color, intensity)
|
|
533
|
+
light[:groundColor].call(:setHex, ground_color)
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
def update_light_shadow(light, parameters)
|
|
537
|
+
shadow = light[:shadow]
|
|
538
|
+
return unless js_present?(shadow)
|
|
539
|
+
|
|
540
|
+
if parameters[:map_size]
|
|
541
|
+
shadow[:mapSize].call(:set, *parameters[:map_size])
|
|
542
|
+
end
|
|
543
|
+
shadow[:bias] = parameters[:bias] unless parameters[:bias].nil?
|
|
544
|
+
shadow[:normalBias] = parameters[:normal_bias] unless parameters[:normal_bias].nil?
|
|
545
|
+
shadow[:radius] = parameters[:radius] unless parameters[:radius].nil?
|
|
546
|
+
|
|
547
|
+
camera = shadow[:camera]
|
|
548
|
+
if js_present?(camera) && parameters[:camera]
|
|
549
|
+
parameters[:camera].each do |key, value|
|
|
550
|
+
camera[key] = value
|
|
551
|
+
end
|
|
552
|
+
camera.call(:updateProjectionMatrix)
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def update_material(material, parameters)
|
|
557
|
+
parameters.each do |key, value|
|
|
558
|
+
if MATERIAL_COLOR_PARAMETERS.include?(key)
|
|
559
|
+
material[key].call(:setHex, value)
|
|
560
|
+
else
|
|
561
|
+
material[key] = value
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
material[:needsUpdate] = true
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
def add_child(parent, child)
|
|
568
|
+
parent.call(:add, child)
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
def clear_children(parent)
|
|
572
|
+
parent.call(:clear)
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
def set_scene_background(scene, background)
|
|
576
|
+
scene[:background] = background
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
def set_scene_environment(scene, environment)
|
|
580
|
+
scene[:environment] = environment
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
def dispose(handle)
|
|
584
|
+
handle.call(:dispose) if handle.respond_to?(:call)
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
def object_handle_key(object)
|
|
588
|
+
return nil unless js_present?(object)
|
|
589
|
+
|
|
590
|
+
uuid = object[:uuid]
|
|
591
|
+
js_present?(uuid) ? uuid.to_s : nil
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
def intersection_distance(intersection)
|
|
595
|
+
value = intersection[:distance]
|
|
596
|
+
js_present?(value) ? value.to_f : nil
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
def intersection_point(intersection)
|
|
600
|
+
point = intersection[:point]
|
|
601
|
+
js_present?(point) ? js_vector_to_a(point, 3) : nil
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
def intersection_object(intersection)
|
|
605
|
+
intersection[:object]
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
def intersection_uv(intersection)
|
|
609
|
+
uv = intersection[:uv]
|
|
610
|
+
js_present?(uv) ? js_vector_to_a(uv, 2) : nil
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
def intersection_face_index(intersection)
|
|
614
|
+
value = intersection[:faceIndex]
|
|
615
|
+
js_present?(value) ? value.to_i : nil
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
def intersection_index(intersection)
|
|
619
|
+
value = intersection[:index]
|
|
620
|
+
js_present?(value) ? value.to_i : nil
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
def intersection_instance_id(intersection)
|
|
624
|
+
value = intersection[:instanceId]
|
|
625
|
+
js_present?(value) ? value.to_i : nil
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
def traverse_object3d(object, callback)
|
|
629
|
+
object.call(:traverse, callback)
|
|
630
|
+
object
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
def dispose_object3d_subtree(
|
|
634
|
+
object,
|
|
635
|
+
remove: true,
|
|
636
|
+
dispose_geometries: true,
|
|
637
|
+
dispose_materials: true,
|
|
638
|
+
dispose_textures: false,
|
|
639
|
+
dispose_skeletons: true
|
|
640
|
+
)
|
|
641
|
+
resources = {
|
|
642
|
+
geometries: JS.global[:Set].new,
|
|
643
|
+
materials: JS.global[:Set].new,
|
|
644
|
+
textures: JS.global[:Set].new,
|
|
645
|
+
skeletons: JS.global[:Set].new
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
traverse_object3d(object, proc do |node|
|
|
649
|
+
collect_object3d_resources(
|
|
650
|
+
node,
|
|
651
|
+
resources,
|
|
652
|
+
dispose_geometries: dispose_geometries,
|
|
653
|
+
dispose_materials: dispose_materials,
|
|
654
|
+
dispose_textures: dispose_textures,
|
|
655
|
+
dispose_skeletons: dispose_skeletons
|
|
656
|
+
)
|
|
657
|
+
end)
|
|
658
|
+
|
|
659
|
+
remove_from_js_parent(object) if remove
|
|
660
|
+
|
|
661
|
+
dispose_js_set(resources[:geometries])
|
|
662
|
+
dispose_js_set(resources[:materials])
|
|
663
|
+
dispose_js_set(resources[:textures])
|
|
664
|
+
dispose_js_set(resources[:skeletons])
|
|
665
|
+
object
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
private
|
|
669
|
+
|
|
670
|
+
def default_three
|
|
671
|
+
require "js"
|
|
672
|
+
JS.global[:THREE]
|
|
673
|
+
rescue LoadError
|
|
674
|
+
raise RuntimeError, "Three::Backends::ThreeJS requires ruby.wasm's js gem or an injected adapter"
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
def orbit_controls_constructor
|
|
678
|
+
require "js"
|
|
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"
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
def effect_composer_constructor
|
|
688
|
+
require "js"
|
|
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"
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
def render_pass_constructor
|
|
698
|
+
require "js"
|
|
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"
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
def unreal_bloom_pass_constructor
|
|
708
|
+
require "js"
|
|
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"
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
def dot_screen_pass_constructor
|
|
718
|
+
require "js"
|
|
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"
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
def output_pass_constructor
|
|
728
|
+
require "js"
|
|
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"
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
def gltf_loader_constructor
|
|
738
|
+
require "js"
|
|
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"
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
def draco_loader_constructor
|
|
748
|
+
require "js"
|
|
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"
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
def configure_draco_loader(loader, decoder_path, decoder_config)
|
|
758
|
+
draco_loader = draco_loader_constructor.new
|
|
759
|
+
draco_loader.call(:setDecoderPath, decoder_path)
|
|
760
|
+
draco_loader.call(:setDecoderConfig, stringify_keys(decoder_config)) if decoder_config
|
|
761
|
+
loader.call(:setDRACOLoader, draco_loader)
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
def rgbe_loader_constructor
|
|
765
|
+
require "js"
|
|
766
|
+
constructor = JS.global[:THREE_RGBE_LOADER]
|
|
767
|
+
raise RuntimeError, "Three::Loaders::RGBELoader requires globalThis.THREE_RGBE_LOADER" if constructor.typeof == "undefined"
|
|
768
|
+
|
|
769
|
+
constructor
|
|
770
|
+
rescue LoadError
|
|
771
|
+
raise RuntimeError, "Three::Loaders::RGBELoader requires ruby.wasm's js gem or an injected adapter"
|
|
772
|
+
end
|
|
773
|
+
|
|
774
|
+
def resolve_canvas(canvas)
|
|
775
|
+
return canvas unless canvas.is_a?(String)
|
|
776
|
+
|
|
777
|
+
JS.global[:document].call(:querySelector, canvas)
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
def typed_array(component_type, array)
|
|
781
|
+
constructor =
|
|
782
|
+
case component_type
|
|
783
|
+
when :float32 then JS.global[:Float32Array]
|
|
784
|
+
when :uint16 then JS.global[:Uint16Array]
|
|
785
|
+
when :uint32 then JS.global[:Uint32Array]
|
|
786
|
+
else JS.global[:Array]
|
|
787
|
+
end
|
|
788
|
+
constructor.new(array)
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
def js_array(values)
|
|
792
|
+
array = JS.global[:Array].new
|
|
793
|
+
values.each { |value| array.call(:push, value) }
|
|
794
|
+
array
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
def stringify_keys(hash)
|
|
798
|
+
hash.each_with_object({}) do |(key, value), result|
|
|
799
|
+
result[key.to_s] = value
|
|
800
|
+
end
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
def js_vector_to_a(vector, length)
|
|
804
|
+
array = vector.call(:toArray)
|
|
805
|
+
length.times.map { |index| array[index].to_f }
|
|
806
|
+
end
|
|
807
|
+
|
|
808
|
+
def collect_object3d_resources(node, resources, dispose_geometries:, dispose_materials:, dispose_textures:, dispose_skeletons:)
|
|
809
|
+
if dispose_geometries
|
|
810
|
+
geometry = node[:geometry]
|
|
811
|
+
resources[:geometries].call(:add, geometry) if js_present?(geometry)
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
if dispose_materials
|
|
815
|
+
collect_materials(node[:material], resources, dispose_textures: dispose_textures)
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
if dispose_textures
|
|
819
|
+
collect_texture(node[:background], resources)
|
|
820
|
+
collect_texture(node[:environment], resources)
|
|
821
|
+
end
|
|
822
|
+
|
|
823
|
+
return unless dispose_skeletons
|
|
824
|
+
|
|
825
|
+
skeleton = node[:skeleton]
|
|
826
|
+
resources[:skeletons].call(:add, skeleton) if js_present?(skeleton)
|
|
827
|
+
end
|
|
828
|
+
|
|
829
|
+
def collect_materials(material, resources, dispose_textures:)
|
|
830
|
+
return unless js_present?(material)
|
|
831
|
+
|
|
832
|
+
if JS.global[:Array].call(:isArray, material) == JS::True
|
|
833
|
+
material[:length].to_i.times do |index|
|
|
834
|
+
collect_material(material[index], resources, dispose_textures: dispose_textures)
|
|
835
|
+
end
|
|
836
|
+
else
|
|
837
|
+
collect_material(material, resources, dispose_textures: dispose_textures)
|
|
838
|
+
end
|
|
839
|
+
end
|
|
840
|
+
|
|
841
|
+
def collect_material(material, resources, dispose_textures:)
|
|
842
|
+
return unless js_present?(material)
|
|
843
|
+
|
|
844
|
+
resources[:materials].call(:add, material)
|
|
845
|
+
return unless dispose_textures
|
|
846
|
+
|
|
847
|
+
TEXTURE_SLOTS.each { |slot| collect_texture(material[slot], resources) }
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
def collect_texture(texture, resources)
|
|
851
|
+
resources[:textures].call(:add, texture) if js_present?(texture)
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
def dispose_js_set(resources)
|
|
855
|
+
resources.call(:forEach, proc { |resource| dispose(resource) })
|
|
856
|
+
end
|
|
857
|
+
|
|
858
|
+
def remove_from_js_parent(object)
|
|
859
|
+
parent = object[:parent]
|
|
860
|
+
parent.call(:remove, object) if js_present?(parent)
|
|
861
|
+
end
|
|
862
|
+
|
|
863
|
+
def js_present?(object)
|
|
864
|
+
return false if object.nil?
|
|
865
|
+
return false if object == JS::Undefined || object == JS::Null
|
|
866
|
+
return false if object.respond_to?(:typeof) && object.typeof == "undefined"
|
|
867
|
+
|
|
868
|
+
true
|
|
869
|
+
end
|
|
870
|
+
end
|
|
871
|
+
end
|
|
872
|
+
end
|
|
873
|
+
end
|