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,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../core/object3d"
|
|
4
|
+
require_relative "../math/color"
|
|
5
|
+
|
|
6
|
+
module Three
|
|
7
|
+
class Light < Object3D
|
|
8
|
+
attr_reader :color, :intensity
|
|
9
|
+
attr_reader :shadow_map_size, :shadow_bias, :shadow_normal_bias, :shadow_radius
|
|
10
|
+
|
|
11
|
+
def initialize(color = 0xffffff, intensity = 1)
|
|
12
|
+
super()
|
|
13
|
+
@type = "Light"
|
|
14
|
+
@color = Color.new(color)
|
|
15
|
+
@intensity = intensity
|
|
16
|
+
@shadow_map_size = [512, 512]
|
|
17
|
+
@shadow_bias = 0
|
|
18
|
+
@shadow_normal_bias = 0
|
|
19
|
+
@shadow_radius = 1
|
|
20
|
+
bind_color_changes
|
|
21
|
+
mark_dirty!(:light)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def color=(value)
|
|
25
|
+
@color = value.is_a?(Color) ? value : Color.new(value)
|
|
26
|
+
bind_color_changes
|
|
27
|
+
mark_dirty!(:light)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def intensity=(value)
|
|
31
|
+
@intensity = value
|
|
32
|
+
mark_dirty!(:light)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def shadow_map_size=(value)
|
|
36
|
+
array = coerce_shadow_map_size(value)
|
|
37
|
+
@shadow_map_size = array
|
|
38
|
+
mark_dirty!(:shadow)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def shadow_bias=(value)
|
|
42
|
+
@shadow_bias = value
|
|
43
|
+
mark_dirty!(:shadow)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def shadow_normal_bias=(value)
|
|
47
|
+
@shadow_normal_bias = value
|
|
48
|
+
mark_dirty!(:shadow)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def shadow_radius=(value)
|
|
52
|
+
@shadow_radius = value
|
|
53
|
+
mark_dirty!(:shadow)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def dispose
|
|
57
|
+
dispatch_event(:dispose)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def to_h
|
|
61
|
+
super.merge(
|
|
62
|
+
color: @color.hex,
|
|
63
|
+
intensity: @intensity,
|
|
64
|
+
shadow_map_size: @shadow_map_size.dup,
|
|
65
|
+
shadow_bias: @shadow_bias,
|
|
66
|
+
shadow_normal_bias: @shadow_normal_bias,
|
|
67
|
+
shadow_radius: @shadow_radius
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def bind_color_changes
|
|
74
|
+
@color.on_change { mark_dirty!(:light) }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def coerce_shadow_map_size(value)
|
|
78
|
+
array = value.to_ary if value.respond_to?(:to_ary)
|
|
79
|
+
array ||= value.to_a if value.respond_to?(:to_a)
|
|
80
|
+
raise TypeError, "shadow_map_size must be array-like [width, height]" unless array && array.length >= 2
|
|
81
|
+
|
|
82
|
+
[array[0], array[1]]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "light"
|
|
4
|
+
|
|
5
|
+
module Three
|
|
6
|
+
class PointLight < Light
|
|
7
|
+
attr_reader :distance, :decay
|
|
8
|
+
|
|
9
|
+
def initialize(color = 0xffffff, intensity = 1, distance = 0, decay = 2)
|
|
10
|
+
super(color, intensity)
|
|
11
|
+
@type = "PointLight"
|
|
12
|
+
@distance = distance
|
|
13
|
+
@decay = decay
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def distance=(value)
|
|
17
|
+
@distance = value
|
|
18
|
+
mark_dirty!(:light)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def decay=(value)
|
|
22
|
+
@decay = value
|
|
23
|
+
mark_dirty!(:light)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_h
|
|
27
|
+
super.merge(
|
|
28
|
+
distance: @distance,
|
|
29
|
+
decay: @decay
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../backends/threejs"
|
|
4
|
+
require_relative "../animation/animation_clip"
|
|
5
|
+
require_relative "../objects/external_object3d"
|
|
6
|
+
|
|
7
|
+
module Three
|
|
8
|
+
module Loaders
|
|
9
|
+
class GLTF
|
|
10
|
+
attr_reader :handle, :scene, :animations
|
|
11
|
+
|
|
12
|
+
def initialize(handle, adapter:)
|
|
13
|
+
@handle = handle
|
|
14
|
+
@adapter = adapter
|
|
15
|
+
@scene = ExternalObject3D.new(read_property(handle, :scene), type: "GLTFScene")
|
|
16
|
+
@animations = @adapter.gltf_animations(handle).map do |animation|
|
|
17
|
+
AnimationClip.new(animation, adapter: @adapter)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def read_property(object, name)
|
|
24
|
+
object[name]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class GLTFLoader
|
|
29
|
+
def initialize(adapter: nil, backend: nil, draco_decoder_path: nil, draco_decoder_config: nil)
|
|
30
|
+
@adapter = adapter || backend&.adapter || Backends::ThreeJS::RubyWasmAdapter.new
|
|
31
|
+
@draco_decoder_path = draco_decoder_path
|
|
32
|
+
@draco_decoder_config = draco_decoder_config
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def load(source)
|
|
36
|
+
result = @adapter.load_gltf(
|
|
37
|
+
source,
|
|
38
|
+
draco_decoder_path: @draco_decoder_path,
|
|
39
|
+
draco_decoder_config: @draco_decoder_config
|
|
40
|
+
)
|
|
41
|
+
result = result.await if result.respond_to?(:await)
|
|
42
|
+
gltf = GLTF.new(result, adapter: @adapter)
|
|
43
|
+
yield gltf if block_given?
|
|
44
|
+
gltf
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../textures/rgbe_texture"
|
|
4
|
+
|
|
5
|
+
module Three
|
|
6
|
+
module Loaders
|
|
7
|
+
class RGBELoader
|
|
8
|
+
def load(source, **parameters)
|
|
9
|
+
texture = RGBETexture.new(source, **parameters)
|
|
10
|
+
yield texture if block_given?
|
|
11
|
+
texture
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Three
|
|
6
|
+
module Loaders
|
|
7
|
+
class ThreeJSONLoader
|
|
8
|
+
def parse(input)
|
|
9
|
+
data = input.is_a?(String) ? JSON.parse(input) : input
|
|
10
|
+
@textures = build_resource_map(value(data, :textures)) { |entry| build_texture(entry) }
|
|
11
|
+
@geometries = build_resource_map(value(data, :geometries)) { |entry| build_geometry(entry) }
|
|
12
|
+
@materials = build_resource_map(value(data, :materials)) { |entry| build_material(entry) }
|
|
13
|
+
build_object(value(data, :object))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def build_resource_map(entries)
|
|
19
|
+
Array(entries).each_with_object({}) do |entry, result|
|
|
20
|
+
result[value(entry, :uuid)] = yield(entry)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def build_texture(entry)
|
|
25
|
+
case value(entry, :type)
|
|
26
|
+
when "CubeTexture"
|
|
27
|
+
CubeTexture.new(
|
|
28
|
+
value(entry, :sources) || value(entry, :source),
|
|
29
|
+
**texture_parameters(entry)
|
|
30
|
+
)
|
|
31
|
+
when "RGBETexture"
|
|
32
|
+
RGBETexture.new(value(entry, :source), **texture_parameters(entry))
|
|
33
|
+
else
|
|
34
|
+
Texture.new(value(entry, :source), **texture_parameters(entry))
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def texture_parameters(entry)
|
|
39
|
+
{
|
|
40
|
+
mapping: value(entry, :mapping),
|
|
41
|
+
color_space: value(entry, :color_space),
|
|
42
|
+
flip_y: value(entry, :flip_y),
|
|
43
|
+
wrap_s: value(entry, :wrap_s),
|
|
44
|
+
wrap_t: value(entry, :wrap_t),
|
|
45
|
+
mag_filter: value(entry, :mag_filter),
|
|
46
|
+
min_filter: value(entry, :min_filter),
|
|
47
|
+
offset: value(entry, :offset),
|
|
48
|
+
repeat: value(entry, :repeat),
|
|
49
|
+
center: value(entry, :center),
|
|
50
|
+
rotation: value(entry, :rotation),
|
|
51
|
+
matrix_auto_update: value(entry, :matrix_auto_update),
|
|
52
|
+
matrix: value(entry, :matrix)
|
|
53
|
+
}.compact
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def build_geometry(entry)
|
|
57
|
+
case value(entry, :type)
|
|
58
|
+
when "BoxGeometry"
|
|
59
|
+
parameters = value(entry, :parameters) || {}
|
|
60
|
+
BoxGeometry.new(
|
|
61
|
+
value(parameters, :width) || 1,
|
|
62
|
+
value(parameters, :height) || 1,
|
|
63
|
+
value(parameters, :depth) || 1,
|
|
64
|
+
width_segments: value(parameters, :width_segments) || 1,
|
|
65
|
+
height_segments: value(parameters, :height_segments) || 1,
|
|
66
|
+
depth_segments: value(parameters, :depth_segments) || 1
|
|
67
|
+
)
|
|
68
|
+
when "PlaneGeometry"
|
|
69
|
+
parameters = value(entry, :parameters) || {}
|
|
70
|
+
PlaneGeometry.new(
|
|
71
|
+
value(parameters, :width) || 1,
|
|
72
|
+
value(parameters, :height) || 1,
|
|
73
|
+
width_segments: value(parameters, :width_segments) || 1,
|
|
74
|
+
height_segments: value(parameters, :height_segments) || 1
|
|
75
|
+
)
|
|
76
|
+
when "SphereGeometry"
|
|
77
|
+
parameters = value(entry, :parameters) || {}
|
|
78
|
+
SphereGeometry.new(
|
|
79
|
+
value(parameters, :radius) || 1,
|
|
80
|
+
width_segments: value(parameters, :width_segments) || 32,
|
|
81
|
+
height_segments: value(parameters, :height_segments) || 16,
|
|
82
|
+
phi_start: value(parameters, :phi_start) || 0,
|
|
83
|
+
phi_length: value(parameters, :phi_length) || Math::PI * 2,
|
|
84
|
+
theta_start: value(parameters, :theta_start) || 0,
|
|
85
|
+
theta_length: value(parameters, :theta_length) || Math::PI
|
|
86
|
+
)
|
|
87
|
+
else
|
|
88
|
+
build_buffer_geometry(entry)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def build_buffer_geometry(entry)
|
|
93
|
+
geometry = BufferGeometry.new
|
|
94
|
+
geometry.name = value(entry, :name) if value(entry, :name)
|
|
95
|
+
geometry.set_index(build_buffer_attribute(value(entry, :index))) if value(entry, :index)
|
|
96
|
+
|
|
97
|
+
(value(entry, :attributes) || {}).each do |name, attribute_entry|
|
|
98
|
+
geometry.set_attribute(name.to_sym, build_buffer_attribute(attribute_entry))
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
Array(value(entry, :groups)).each do |group|
|
|
102
|
+
geometry.add_group(
|
|
103
|
+
value(group, :start),
|
|
104
|
+
value(group, :count),
|
|
105
|
+
value(group, :material_index) || 0
|
|
106
|
+
)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
geometry
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def build_buffer_attribute(entry)
|
|
113
|
+
component_type = (value(entry, :component_type) || :generic).to_sym
|
|
114
|
+
array = value(entry, :array) || []
|
|
115
|
+
item_size = value(entry, :item_size)
|
|
116
|
+
normalized = value(entry, :normalized) || false
|
|
117
|
+
|
|
118
|
+
case component_type
|
|
119
|
+
when :float32
|
|
120
|
+
Float32BufferAttribute.new(array, item_size, normalized)
|
|
121
|
+
when :uint16
|
|
122
|
+
Uint16BufferAttribute.new(array, item_size, normalized)
|
|
123
|
+
when :uint32
|
|
124
|
+
Uint32BufferAttribute.new(array, item_size, normalized)
|
|
125
|
+
else
|
|
126
|
+
BufferAttribute.new(array, item_size, normalized, component_type: component_type)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def build_material(entry)
|
|
131
|
+
parameters = material_parameters(entry)
|
|
132
|
+
case value(entry, :type)
|
|
133
|
+
when "MeshBasicMaterial"
|
|
134
|
+
MeshBasicMaterial.new(parameters)
|
|
135
|
+
when "LineBasicMaterial"
|
|
136
|
+
LineBasicMaterial.new(parameters)
|
|
137
|
+
when "MeshLambertMaterial"
|
|
138
|
+
MeshLambertMaterial.new(parameters)
|
|
139
|
+
when "MeshMatcapMaterial"
|
|
140
|
+
MeshMatcapMaterial.new(parameters)
|
|
141
|
+
when "MeshNormalMaterial"
|
|
142
|
+
MeshNormalMaterial.new(parameters)
|
|
143
|
+
when "MeshPhongMaterial"
|
|
144
|
+
MeshPhongMaterial.new(parameters)
|
|
145
|
+
when "MeshPhysicalMaterial"
|
|
146
|
+
MeshPhysicalMaterial.new(parameters)
|
|
147
|
+
when "MeshStandardMaterial"
|
|
148
|
+
MeshStandardMaterial.new(parameters)
|
|
149
|
+
when "MeshToonMaterial"
|
|
150
|
+
MeshToonMaterial.new(parameters)
|
|
151
|
+
when "PointsMaterial"
|
|
152
|
+
PointsMaterial.new(parameters)
|
|
153
|
+
when "ShadowMaterial"
|
|
154
|
+
ShadowMaterial.new(parameters)
|
|
155
|
+
when "SpriteMaterial"
|
|
156
|
+
SpriteMaterial.new(parameters)
|
|
157
|
+
else
|
|
158
|
+
Material.new(parameters)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def material_parameters(entry)
|
|
163
|
+
parameters = {}
|
|
164
|
+
%i[
|
|
165
|
+
name
|
|
166
|
+
side
|
|
167
|
+
opacity
|
|
168
|
+
transparent
|
|
169
|
+
visible
|
|
170
|
+
vertex_colors
|
|
171
|
+
color
|
|
172
|
+
emissive
|
|
173
|
+
specular
|
|
174
|
+
shininess
|
|
175
|
+
roughness
|
|
176
|
+
metalness
|
|
177
|
+
anisotropy
|
|
178
|
+
anisotropy_rotation
|
|
179
|
+
clearcoat
|
|
180
|
+
clearcoat_roughness
|
|
181
|
+
transmission
|
|
182
|
+
thickness
|
|
183
|
+
ior
|
|
184
|
+
reflectivity
|
|
185
|
+
iridescence
|
|
186
|
+
iridescence_ior
|
|
187
|
+
iridescence_thickness_range
|
|
188
|
+
sheen
|
|
189
|
+
sheen_color
|
|
190
|
+
sheen_roughness
|
|
191
|
+
dispersion
|
|
192
|
+
specular_intensity
|
|
193
|
+
specular_color
|
|
194
|
+
attenuation_distance
|
|
195
|
+
attenuation_color
|
|
196
|
+
wireframe
|
|
197
|
+
wireframe_linewidth
|
|
198
|
+
linewidth
|
|
199
|
+
linecap
|
|
200
|
+
linejoin
|
|
201
|
+
fog
|
|
202
|
+
flat_shading
|
|
203
|
+
rotation
|
|
204
|
+
size
|
|
205
|
+
size_attenuation
|
|
206
|
+
].each do |key|
|
|
207
|
+
next unless has_value?(entry, key)
|
|
208
|
+
|
|
209
|
+
parameters[key] = value(entry, key)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
texture_slots(entry).each do |slot|
|
|
213
|
+
uuid = value(entry, slot)
|
|
214
|
+
parameters[slot] = @textures[uuid] if uuid
|
|
215
|
+
end
|
|
216
|
+
parameters
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def texture_slots(entry)
|
|
220
|
+
%i[
|
|
221
|
+
map
|
|
222
|
+
alpha_map
|
|
223
|
+
ao_map
|
|
224
|
+
bump_map
|
|
225
|
+
displacement_map
|
|
226
|
+
emissive_map
|
|
227
|
+
env_map
|
|
228
|
+
gradient_map
|
|
229
|
+
light_map
|
|
230
|
+
matcap
|
|
231
|
+
metalness_map
|
|
232
|
+
normal_map
|
|
233
|
+
roughness_map
|
|
234
|
+
specular_map
|
|
235
|
+
anisotropy_map
|
|
236
|
+
clearcoat_map
|
|
237
|
+
clearcoat_normal_map
|
|
238
|
+
clearcoat_roughness_map
|
|
239
|
+
transmission_map
|
|
240
|
+
thickness_map
|
|
241
|
+
iridescence_map
|
|
242
|
+
iridescence_thickness_map
|
|
243
|
+
sheen_color_map
|
|
244
|
+
sheen_roughness_map
|
|
245
|
+
specular_color_map
|
|
246
|
+
specular_intensity_map
|
|
247
|
+
].select { |slot| has_value?(entry, slot) }
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def build_object(entry)
|
|
251
|
+
object = instantiate_object(entry)
|
|
252
|
+
apply_object_properties(object, entry)
|
|
253
|
+
Array(value(entry, :children)).each { |child| object.add(build_object(child)) }
|
|
254
|
+
object
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def instantiate_object(entry)
|
|
258
|
+
case value(entry, :type)
|
|
259
|
+
when "Scene"
|
|
260
|
+
build_scene(entry)
|
|
261
|
+
when "PerspectiveCamera"
|
|
262
|
+
PerspectiveCamera.new(
|
|
263
|
+
value(entry, :fov) || 50,
|
|
264
|
+
aspect: value(entry, :aspect) || 1,
|
|
265
|
+
near: value(entry, :near) || 0.1,
|
|
266
|
+
far: value(entry, :far) || 2000
|
|
267
|
+
)
|
|
268
|
+
when "OrthographicCamera"
|
|
269
|
+
OrthographicCamera.new(
|
|
270
|
+
value(entry, :left),
|
|
271
|
+
value(entry, :right),
|
|
272
|
+
value(entry, :top),
|
|
273
|
+
value(entry, :bottom),
|
|
274
|
+
near: value(entry, :near) || 0.1,
|
|
275
|
+
far: value(entry, :far) || 2000
|
|
276
|
+
)
|
|
277
|
+
when "AmbientLight"
|
|
278
|
+
AmbientLight.new(value(entry, :color) || 0xffffff, value(entry, :intensity) || 1)
|
|
279
|
+
when "DirectionalLight"
|
|
280
|
+
build_directional_light(entry)
|
|
281
|
+
when "PointLight"
|
|
282
|
+
PointLight.new(
|
|
283
|
+
value(entry, :color) || 0xffffff,
|
|
284
|
+
value(entry, :intensity) || 1,
|
|
285
|
+
value(entry, :distance) || 0,
|
|
286
|
+
value(entry, :decay) || 2
|
|
287
|
+
)
|
|
288
|
+
when "HemisphereLight"
|
|
289
|
+
HemisphereLight.new(
|
|
290
|
+
value(entry, :color) || 0xffffff,
|
|
291
|
+
value(entry, :ground_color) || 0xffffff,
|
|
292
|
+
value(entry, :intensity) || 1
|
|
293
|
+
)
|
|
294
|
+
when "InstancedMesh"
|
|
295
|
+
build_instanced_mesh(entry)
|
|
296
|
+
when "Mesh"
|
|
297
|
+
Mesh.new(@geometries[value(entry, :geometry)], material_reference(entry))
|
|
298
|
+
when "Line"
|
|
299
|
+
Line.new(@geometries[value(entry, :geometry)], material_reference(entry))
|
|
300
|
+
when "Points"
|
|
301
|
+
Points.new(@geometries[value(entry, :geometry)], material_reference(entry))
|
|
302
|
+
when "Sprite"
|
|
303
|
+
build_sprite(entry)
|
|
304
|
+
when "Group"
|
|
305
|
+
Group.new
|
|
306
|
+
else
|
|
307
|
+
Object3D.new
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def build_scene(entry)
|
|
312
|
+
scene = Scene.new
|
|
313
|
+
scene.background = @textures[value(entry, :background)] if value(entry, :background)
|
|
314
|
+
scene.environment = @textures[value(entry, :environment)] if value(entry, :environment)
|
|
315
|
+
scene
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def build_directional_light(entry)
|
|
319
|
+
light = DirectionalLight.new(value(entry, :color) || 0xffffff, value(entry, :intensity) || 1)
|
|
320
|
+
camera = value(entry, :shadow_camera)
|
|
321
|
+
light.set_shadow_camera(**symbolize_keys(camera)) if camera
|
|
322
|
+
light
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def build_instanced_mesh(entry)
|
|
326
|
+
mesh = InstancedMesh.new(
|
|
327
|
+
@geometries[value(entry, :geometry)],
|
|
328
|
+
material_reference(entry),
|
|
329
|
+
value(entry, :capacity) || value(entry, :count) || 1
|
|
330
|
+
)
|
|
331
|
+
mesh.count = value(entry, :count) if has_value?(entry, :count)
|
|
332
|
+
|
|
333
|
+
Array(value(entry, :instance_matrices)).each_with_index do |matrix, index|
|
|
334
|
+
mesh.set_matrix_at(index, matrix)
|
|
335
|
+
end
|
|
336
|
+
Array(value(entry, :instance_colors)).each_with_index do |color, index|
|
|
337
|
+
mesh.set_color_at(index, color) if color
|
|
338
|
+
end
|
|
339
|
+
mesh
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def build_sprite(entry)
|
|
343
|
+
sprite = Sprite.new(material_reference(entry))
|
|
344
|
+
sprite.center = value(entry, :center) if value(entry, :center)
|
|
345
|
+
sprite
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def material_reference(entry)
|
|
349
|
+
material = value(entry, :material)
|
|
350
|
+
return material.map { |uuid| @materials[uuid] } if material.is_a?(Array)
|
|
351
|
+
|
|
352
|
+
@materials[material]
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def apply_object_properties(object, entry)
|
|
356
|
+
object.name = value(entry, :name) if has_value?(entry, :name)
|
|
357
|
+
object.visible = value(entry, :visible) if has_value?(entry, :visible)
|
|
358
|
+
object.layers.mask = value(entry, :layers) if has_value?(entry, :layers)
|
|
359
|
+
object.cast_shadow = value(entry, :cast_shadow) if has_value?(entry, :cast_shadow)
|
|
360
|
+
object.receive_shadow = value(entry, :receive_shadow) if has_value?(entry, :receive_shadow)
|
|
361
|
+
object.matrix_auto_update = value(entry, :matrix_auto_update) if has_value?(entry, :matrix_auto_update)
|
|
362
|
+
object.user_data = value(entry, :user_data) || {}
|
|
363
|
+
|
|
364
|
+
object.position.set(*value(entry, :position)) if value(entry, :position)
|
|
365
|
+
object.quaternion.set(*value(entry, :quaternion)) if value(entry, :quaternion)
|
|
366
|
+
object.scale.set(*value(entry, :scale)) if value(entry, :scale)
|
|
367
|
+
|
|
368
|
+
apply_camera_properties(object, entry)
|
|
369
|
+
apply_light_properties(object, entry)
|
|
370
|
+
object
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def apply_camera_properties(object, entry)
|
|
374
|
+
return unless object.is_a?(Camera)
|
|
375
|
+
|
|
376
|
+
object.zoom = value(entry, :zoom) if has_value?(entry, :zoom)
|
|
377
|
+
object.update_projection_matrix if object.respond_to?(:update_projection_matrix)
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def apply_light_properties(object, entry)
|
|
381
|
+
return unless object.is_a?(Light)
|
|
382
|
+
|
|
383
|
+
object.shadow_map_size = value(entry, :shadow_map_size) if value(entry, :shadow_map_size)
|
|
384
|
+
object.shadow_bias = value(entry, :shadow_bias) if has_value?(entry, :shadow_bias)
|
|
385
|
+
object.shadow_normal_bias = value(entry, :shadow_normal_bias) if has_value?(entry, :shadow_normal_bias)
|
|
386
|
+
object.shadow_radius = value(entry, :shadow_radius) if has_value?(entry, :shadow_radius)
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def has_value?(hash, key)
|
|
390
|
+
hash.key?(key) || hash.key?(key.to_s)
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def value(hash, key)
|
|
394
|
+
return nil unless hash
|
|
395
|
+
return hash[key] if hash.key?(key)
|
|
396
|
+
|
|
397
|
+
hash[key.to_s]
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def symbolize_keys(hash)
|
|
401
|
+
return {} unless hash
|
|
402
|
+
|
|
403
|
+
hash.each_with_object({}) do |(key, value), result|
|
|
404
|
+
result[key.to_sym] = value
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../math/color"
|
|
4
|
+
require_relative "material"
|
|
5
|
+
|
|
6
|
+
module Three
|
|
7
|
+
class LineBasicMaterial < Material
|
|
8
|
+
attr_reader :color, :linewidth, :linecap, :linejoin, :fog
|
|
9
|
+
|
|
10
|
+
def initialize(parameters = nil)
|
|
11
|
+
super(nil)
|
|
12
|
+
@type = "LineBasicMaterial"
|
|
13
|
+
@color = Color.new(0xffffff)
|
|
14
|
+
@linewidth = 1
|
|
15
|
+
@linecap = "round"
|
|
16
|
+
@linejoin = "round"
|
|
17
|
+
@fog = true
|
|
18
|
+
bind_color_changes
|
|
19
|
+
set_values(parameters) if parameters
|
|
20
|
+
mark_dirty!
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def color=(value)
|
|
24
|
+
@color = value.is_a?(Color) ? value : Color.new(value)
|
|
25
|
+
bind_color_changes
|
|
26
|
+
mark_dirty!(:parameters)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def linewidth=(value)
|
|
30
|
+
@linewidth = value
|
|
31
|
+
mark_dirty!(:parameters)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def linecap=(value)
|
|
35
|
+
@linecap = value
|
|
36
|
+
mark_dirty!(:parameters)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def linejoin=(value)
|
|
40
|
+
@linejoin = value
|
|
41
|
+
mark_dirty!(:parameters)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def fog=(value)
|
|
45
|
+
@fog = value
|
|
46
|
+
mark_dirty!(:parameters)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def to_h
|
|
50
|
+
super.merge(
|
|
51
|
+
color: @color.hex,
|
|
52
|
+
linewidth: @linewidth,
|
|
53
|
+
linecap: @linecap,
|
|
54
|
+
linejoin: @linejoin,
|
|
55
|
+
fog: @fog
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def bind_color_changes
|
|
62
|
+
@color.on_change { mark_dirty!(:parameters) }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|