webxr 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/.rspec +3 -0
- data/CHANGELOG.md +7 -0
- data/README.md +138 -0
- data/Rakefile +21 -0
- data/examples/ar_demo.html +238 -0
- data/examples/ar_hit_test.rb +157 -0
- data/examples/basic_vr.rb +110 -0
- data/examples/controller_input.rb +91 -0
- data/examples/hand_tracking.rb +124 -0
- data/examples/hello_webxr.html +288 -0
- data/examples/inline_demo.html +261 -0
- data/examples/server.rb +34 -0
- data/examples/vr_scene_demo.html +330 -0
- data/lib/webxr/ar/anchor.rb +83 -0
- data/lib/webxr/ar/hit_test_result.rb +54 -0
- data/lib/webxr/ar/hit_test_source.rb +34 -0
- data/lib/webxr/ar/ray.rb +90 -0
- data/lib/webxr/constants.rb +61 -0
- data/lib/webxr/core/frame.rb +155 -0
- data/lib/webxr/core/render_state.rb +47 -0
- data/lib/webxr/core/session.rb +212 -0
- data/lib/webxr/core/system.rb +122 -0
- data/lib/webxr/errors.rb +18 -0
- data/lib/webxr/events/input_source_event.rb +53 -0
- data/lib/webxr/events/reference_space_event.rb +44 -0
- data/lib/webxr/events/session_event.rb +56 -0
- data/lib/webxr/geometry/pose.rb +49 -0
- data/lib/webxr/geometry/rigid_transform.rb +73 -0
- data/lib/webxr/geometry/view.rb +68 -0
- data/lib/webxr/geometry/viewer_pose.rb +40 -0
- data/lib/webxr/geometry/viewport.rb +55 -0
- data/lib/webxr/hand/hand.rb +197 -0
- data/lib/webxr/hand/joint_pose.rb +33 -0
- data/lib/webxr/hand/joint_space.rb +74 -0
- data/lib/webxr/helpers/input_helper.rb +142 -0
- data/lib/webxr/helpers/rendering_helper.rb +94 -0
- data/lib/webxr/helpers/session_manager.rb +105 -0
- data/lib/webxr/input/gamepad.rb +115 -0
- data/lib/webxr/input/gamepad_button.rb +36 -0
- data/lib/webxr/input/input_source.rb +101 -0
- data/lib/webxr/input/input_source_array.rb +86 -0
- data/lib/webxr/js_wrapper.rb +116 -0
- data/lib/webxr/layers/layer.rb +28 -0
- data/lib/webxr/layers/webgl_binding.rb +69 -0
- data/lib/webxr/layers/webgl_layer.rb +102 -0
- data/lib/webxr/layers/webgl_sub_image.rb +59 -0
- data/lib/webxr/spaces/bounded_reference_space.rb +43 -0
- data/lib/webxr/spaces/reference_space.rb +51 -0
- data/lib/webxr/spaces/space.rb +18 -0
- data/lib/webxr/version.rb +5 -0
- data/lib/webxr.rb +73 -0
- data/webxr.gemspec +33 -0
- metadata +111 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WebXR
|
|
4
|
+
# Gamepad - Represents a gamepad connected to an XR input source
|
|
5
|
+
class Gamepad < JSWrapper
|
|
6
|
+
# @param js_gamepad [JS::Object] The Gamepad JavaScript object
|
|
7
|
+
def initialize(js_gamepad)
|
|
8
|
+
super(js_gamepad)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Get the gamepad identifier
|
|
12
|
+
# @return [String]
|
|
13
|
+
def id
|
|
14
|
+
js_string(:id) || ""
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Get the gamepad index
|
|
18
|
+
# @return [Integer]
|
|
19
|
+
def index
|
|
20
|
+
js_int(:index) || -1
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Check if the gamepad is connected
|
|
24
|
+
# @return [Boolean]
|
|
25
|
+
def connected?
|
|
26
|
+
js_bool(:connected)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Get the button/axis mapping type
|
|
30
|
+
# @return [String]
|
|
31
|
+
def mapping
|
|
32
|
+
js_string(:mapping) || ""
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Get all axis values
|
|
36
|
+
# @return [Array<Float>]
|
|
37
|
+
def axes
|
|
38
|
+
js_axes = js_prop(:axes)
|
|
39
|
+
return [] if js_axes.nil?
|
|
40
|
+
|
|
41
|
+
js_array_to_a(js_axes).map(&:to_f)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Get all buttons
|
|
45
|
+
# @return [Array<GamepadButton>]
|
|
46
|
+
def buttons
|
|
47
|
+
js_buttons = js_prop(:buttons)
|
|
48
|
+
return [] if js_buttons.nil?
|
|
49
|
+
|
|
50
|
+
js_array_to_a(js_buttons).map { |b| GamepadButton.new(b) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Get a specific axis value
|
|
54
|
+
# @param index [Integer] The axis index
|
|
55
|
+
# @return [Float]
|
|
56
|
+
def axis(index)
|
|
57
|
+
axes[index] || 0.0
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Get a specific button
|
|
61
|
+
# @param index [Integer] The button index
|
|
62
|
+
# @return [GamepadButton, nil]
|
|
63
|
+
def button(index)
|
|
64
|
+
buttons[index]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Get the thumbstick X axis (typically axis 2)
|
|
68
|
+
# @return [Float]
|
|
69
|
+
def thumbstick_x
|
|
70
|
+
axis(2)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Get the thumbstick Y axis (typically axis 3)
|
|
74
|
+
# @return [Float]
|
|
75
|
+
def thumbstick_y
|
|
76
|
+
axis(3)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Get the touchpad X axis (typically axis 0)
|
|
80
|
+
# @return [Float]
|
|
81
|
+
def touchpad_x
|
|
82
|
+
axis(0)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Get the touchpad Y axis (typically axis 1)
|
|
86
|
+
# @return [Float]
|
|
87
|
+
def touchpad_y
|
|
88
|
+
axis(1)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Get the trigger button (typically button 0)
|
|
92
|
+
# @return [GamepadButton, nil]
|
|
93
|
+
def trigger
|
|
94
|
+
button(0)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Get the squeeze/grip button (typically button 1)
|
|
98
|
+
# @return [GamepadButton, nil]
|
|
99
|
+
def squeeze
|
|
100
|
+
button(1)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Get the touchpad button (typically button 2)
|
|
104
|
+
# @return [GamepadButton, nil]
|
|
105
|
+
def touchpad
|
|
106
|
+
button(2)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Get the thumbstick button (typically button 3)
|
|
110
|
+
# @return [GamepadButton, nil]
|
|
111
|
+
def thumbstick
|
|
112
|
+
button(3)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WebXR
|
|
4
|
+
# GamepadButton - Represents a button on a gamepad
|
|
5
|
+
class GamepadButton < JSWrapper
|
|
6
|
+
# @param js_button [JS::Object] The GamepadButton JavaScript object
|
|
7
|
+
def initialize(js_button)
|
|
8
|
+
super(js_button)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Check if the button is currently pressed
|
|
12
|
+
# @return [Boolean]
|
|
13
|
+
def pressed?
|
|
14
|
+
js_bool(:pressed)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Check if the button is being touched (capacitive)
|
|
18
|
+
# @return [Boolean]
|
|
19
|
+
def touched?
|
|
20
|
+
js_bool(:touched)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Get the analog value of the button (0.0 to 1.0)
|
|
24
|
+
# @return [Float]
|
|
25
|
+
def value
|
|
26
|
+
js_float(:value) || 0.0
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Check if the button value is above a threshold
|
|
30
|
+
# @param threshold [Float] The threshold (0.0 to 1.0)
|
|
31
|
+
# @return [Boolean]
|
|
32
|
+
def active?(threshold: 0.5)
|
|
33
|
+
value >= threshold
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WebXR
|
|
4
|
+
# XRInputSource - Represents an input device (controller, hand, etc.)
|
|
5
|
+
class InputSource < JSWrapper
|
|
6
|
+
# @param js_input_source [JS::Object] The XRInputSource JavaScript object
|
|
7
|
+
def initialize(js_input_source)
|
|
8
|
+
super(js_input_source)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Get the handedness of the input source
|
|
12
|
+
# @return [String] "none", "left", or "right"
|
|
13
|
+
def handedness
|
|
14
|
+
js_string(:handedness) || Handedness::NONE
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Get the target ray mode
|
|
18
|
+
# @return [String] "gaze", "tracked-pointer", "screen", or "transient-pointer"
|
|
19
|
+
def target_ray_mode
|
|
20
|
+
js_string(:targetRayMode) || TargetRayMode::GAZE
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Get the target ray space (pointing direction)
|
|
24
|
+
# @return [Space]
|
|
25
|
+
def target_ray_space
|
|
26
|
+
Space.new(js_prop(:targetRaySpace))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Get the grip space (controller position/orientation)
|
|
30
|
+
# @return [Space, nil] nil if no grip space is available
|
|
31
|
+
def grip_space
|
|
32
|
+
js_space = js_prop(:gripSpace)
|
|
33
|
+
return nil if js_space.nil?
|
|
34
|
+
|
|
35
|
+
Space.new(js_space)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Get the input source profiles
|
|
39
|
+
# @return [Array<String>]
|
|
40
|
+
def profiles
|
|
41
|
+
js_profiles = js_prop(:profiles)
|
|
42
|
+
return [] if js_profiles.nil?
|
|
43
|
+
|
|
44
|
+
js_array_to_a(js_profiles).map(&:to_s)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Get the associated gamepad (if available)
|
|
48
|
+
# @return [Gamepad, nil]
|
|
49
|
+
def gamepad
|
|
50
|
+
js_gamepad = js_prop(:gamepad)
|
|
51
|
+
return nil if js_gamepad.nil?
|
|
52
|
+
|
|
53
|
+
Gamepad.new(js_gamepad)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Get the associated hand (if hand tracking is enabled)
|
|
57
|
+
# @return [Hand::Hand, nil]
|
|
58
|
+
def hand
|
|
59
|
+
js_hand = js_prop(:hand)
|
|
60
|
+
return nil if js_hand.nil?
|
|
61
|
+
|
|
62
|
+
Hand::Hand.new(js_hand)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Check if this is the left controller
|
|
66
|
+
# @return [Boolean]
|
|
67
|
+
def left?
|
|
68
|
+
handedness == Handedness::LEFT
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Check if this is the right controller
|
|
72
|
+
# @return [Boolean]
|
|
73
|
+
def right?
|
|
74
|
+
handedness == Handedness::RIGHT
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Check if this is a tracked pointer (controller)
|
|
78
|
+
# @return [Boolean]
|
|
79
|
+
def tracked_pointer?
|
|
80
|
+
target_ray_mode == TargetRayMode::TRACKED_POINTER
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Check if this is a gaze input
|
|
84
|
+
# @return [Boolean]
|
|
85
|
+
def gaze?
|
|
86
|
+
target_ray_mode == TargetRayMode::GAZE
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Check if this is a screen-based input
|
|
90
|
+
# @return [Boolean]
|
|
91
|
+
def screen?
|
|
92
|
+
target_ray_mode == TargetRayMode::SCREEN
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Check if this input has hand tracking
|
|
96
|
+
# @return [Boolean]
|
|
97
|
+
def hand_tracking?
|
|
98
|
+
!hand.nil?
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WebXR
|
|
4
|
+
# XRInputSourceArray - Collection of input sources
|
|
5
|
+
class InputSourceArray < JSWrapper
|
|
6
|
+
include Enumerable
|
|
7
|
+
|
|
8
|
+
# @param js_array [JS::Object] The XRInputSourceArray JavaScript object
|
|
9
|
+
def initialize(js_array)
|
|
10
|
+
super(js_array)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Iterate over each input source
|
|
14
|
+
# @yield [InputSource] Each input source
|
|
15
|
+
# @return [Enumerator, void]
|
|
16
|
+
def each(&block)
|
|
17
|
+
return enum_for(:each) unless block_given?
|
|
18
|
+
|
|
19
|
+
length.times do |i|
|
|
20
|
+
yield self[i]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Get an input source by index
|
|
25
|
+
# @param index [Integer] The index
|
|
26
|
+
# @return [InputSource, nil]
|
|
27
|
+
def [](index)
|
|
28
|
+
js_source = @js[index]
|
|
29
|
+
return nil if js_source.nil?
|
|
30
|
+
|
|
31
|
+
InputSource.new(js_source)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Get the number of input sources
|
|
35
|
+
# @return [Integer]
|
|
36
|
+
def length
|
|
37
|
+
js_int(:length) || 0
|
|
38
|
+
end
|
|
39
|
+
alias size length
|
|
40
|
+
|
|
41
|
+
# Check if the array is empty
|
|
42
|
+
# @return [Boolean]
|
|
43
|
+
def empty?
|
|
44
|
+
length.zero?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Get the first input source
|
|
48
|
+
# @return [InputSource, nil]
|
|
49
|
+
def first
|
|
50
|
+
self[0]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Get the last input source
|
|
54
|
+
# @return [InputSource, nil]
|
|
55
|
+
def last
|
|
56
|
+
return nil if empty?
|
|
57
|
+
|
|
58
|
+
self[length - 1]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Find input sources by handedness
|
|
62
|
+
# @param handedness [String] "left", "right", or "none"
|
|
63
|
+
# @return [Array<InputSource>]
|
|
64
|
+
def by_handedness(handedness)
|
|
65
|
+
select { |source| source.handedness == handedness }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Get the left controller
|
|
69
|
+
# @return [InputSource, nil]
|
|
70
|
+
def left
|
|
71
|
+
find(&:left?)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Get the right controller
|
|
75
|
+
# @return [InputSource, nil]
|
|
76
|
+
def right
|
|
77
|
+
find(&:right?)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Get all tracked pointer inputs
|
|
81
|
+
# @return [Array<InputSource>]
|
|
82
|
+
def tracked_pointers
|
|
83
|
+
select(&:tracked_pointer?)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WebXR
|
|
4
|
+
# Base class for wrapping JavaScript objects
|
|
5
|
+
# Provides common utilities for JS interop via ruby.wasm
|
|
6
|
+
class JSWrapper
|
|
7
|
+
# @return [JS::Object] The wrapped JavaScript object
|
|
8
|
+
attr_reader :js
|
|
9
|
+
|
|
10
|
+
# @param js_object [JS::Object] The JavaScript object to wrap
|
|
11
|
+
def initialize(js_object)
|
|
12
|
+
@js = js_object
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Check if the wrapped JS object is nil/undefined
|
|
16
|
+
# @return [Boolean]
|
|
17
|
+
def nil?
|
|
18
|
+
@js.nil?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
protected
|
|
22
|
+
|
|
23
|
+
# Safely get a JS property
|
|
24
|
+
# @param name [String, Symbol] Property name
|
|
25
|
+
# @return [JS::Object, nil]
|
|
26
|
+
def js_prop(name)
|
|
27
|
+
@js[name]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Get a JS property as a Ruby String
|
|
31
|
+
# @param name [String, Symbol] Property name
|
|
32
|
+
# @return [String, nil]
|
|
33
|
+
def js_string(name)
|
|
34
|
+
val = @js[name]
|
|
35
|
+
val.nil? ? nil : val.to_s
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Get a JS property as a Ruby Float
|
|
39
|
+
# @param name [String, Symbol] Property name
|
|
40
|
+
# @return [Float, nil]
|
|
41
|
+
def js_float(name)
|
|
42
|
+
val = @js[name]
|
|
43
|
+
val.nil? ? nil : val.to_f
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Get a JS property as a Ruby Integer
|
|
47
|
+
# @param name [String, Symbol] Property name
|
|
48
|
+
# @return [Integer, nil]
|
|
49
|
+
def js_int(name)
|
|
50
|
+
val = @js[name]
|
|
51
|
+
val.nil? ? nil : val.to_i
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Get a JS property as a Ruby Boolean
|
|
55
|
+
# @param name [String, Symbol] Property name
|
|
56
|
+
# @return [Boolean]
|
|
57
|
+
def js_bool(name)
|
|
58
|
+
!!@js[name]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Call a JS method on the wrapped object
|
|
62
|
+
# @param method [String, Symbol] Method name
|
|
63
|
+
# @param args [Array] Arguments to pass
|
|
64
|
+
# @return [JS::Object]
|
|
65
|
+
def js_call(method, *args)
|
|
66
|
+
@js.call(method, *args)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Wait for a JS Promise and return the result
|
|
70
|
+
# @param promise [JS::Object] A JavaScript Promise
|
|
71
|
+
# @return [JS::Object] The resolved value
|
|
72
|
+
def js_await(promise)
|
|
73
|
+
JS.await(promise)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Convert a Ruby Hash to a JS object
|
|
77
|
+
# @param hash [Hash] Ruby hash to convert
|
|
78
|
+
# @return [JS::Object]
|
|
79
|
+
def to_js_object(hash)
|
|
80
|
+
obj = JS.eval("({})")
|
|
81
|
+
hash.each { |k, v| obj[k.to_s] = v }
|
|
82
|
+
obj
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Convert a Ruby Array to a JS array
|
|
86
|
+
# @param array [Array] Ruby array to convert
|
|
87
|
+
# @return [JS::Object]
|
|
88
|
+
def to_js_array(array)
|
|
89
|
+
arr = JS.eval("[]")
|
|
90
|
+
array.each { |item| arr.call(:push, item) }
|
|
91
|
+
arr
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Convert a JS array-like object to a Ruby Array
|
|
95
|
+
# @param js_array [JS::Object] JavaScript array
|
|
96
|
+
# @return [Array<JS::Object>]
|
|
97
|
+
def js_array_to_a(js_array)
|
|
98
|
+
return [] if js_array.nil?
|
|
99
|
+
|
|
100
|
+
length = js_array[:length].to_i
|
|
101
|
+
Array.new(length) { |i| js_array[i] }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Create a DOMPointReadOnly from a hash
|
|
105
|
+
# @param hash [Hash] Hash with :x, :y, :z, :w keys
|
|
106
|
+
# @return [JS::Object]
|
|
107
|
+
def create_dom_point(hash)
|
|
108
|
+
JS.global[:DOMPointReadOnly].new(
|
|
109
|
+
hash[:x] || 0,
|
|
110
|
+
hash[:y] || 0,
|
|
111
|
+
hash[:z] || 0,
|
|
112
|
+
hash[:w] || 1
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WebXR
|
|
4
|
+
# XRLayer - Base class for XR rendering layers
|
|
5
|
+
class Layer < JSWrapper
|
|
6
|
+
# @param js_layer [JS::Object] The XRLayer JavaScript object
|
|
7
|
+
def initialize(js_layer)
|
|
8
|
+
super(js_layer)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Wrap a JS layer object in the appropriate Ruby class
|
|
12
|
+
# @param js_layer [JS::Object] The JavaScript layer object
|
|
13
|
+
# @return [Layer, WebGLLayer]
|
|
14
|
+
def self.wrap(js_layer)
|
|
15
|
+
return nil if js_layer.nil?
|
|
16
|
+
|
|
17
|
+
# Check the constructor name to determine the type
|
|
18
|
+
constructor_name = js_layer[:constructor][:name].to_s
|
|
19
|
+
|
|
20
|
+
case constructor_name
|
|
21
|
+
when "XRWebGLLayer"
|
|
22
|
+
WebGLLayer.wrap(js_layer)
|
|
23
|
+
else
|
|
24
|
+
new(js_layer)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WebXR
|
|
4
|
+
# XRWebGLBinding - Provides advanced WebGL layer functionality
|
|
5
|
+
class WebGLBinding < JSWrapper
|
|
6
|
+
# @param session [Session] The XR session
|
|
7
|
+
# @param context [JS::Object] The WebGL2 rendering context
|
|
8
|
+
def initialize(session, context)
|
|
9
|
+
js_binding = JS.global[:XRWebGLBinding].new(session.js, context)
|
|
10
|
+
super(js_binding)
|
|
11
|
+
@context = context
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Get the native projection scale factor
|
|
15
|
+
# @return [Float]
|
|
16
|
+
def native_projection_scale_factor
|
|
17
|
+
js_float(:nativeProjectionScaleFactor) || 1.0
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Check if HDR is supported
|
|
21
|
+
# @return [Boolean]
|
|
22
|
+
def hdr_supported?
|
|
23
|
+
js_bool(:usesDepthValues)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Get a sub-image for a layer
|
|
27
|
+
# @param layer [Layer] The XR layer
|
|
28
|
+
# @param frame [Frame] The current XR frame
|
|
29
|
+
# @param eye [String] The eye ("left", "right", "none")
|
|
30
|
+
# @return [WebGLSubImage]
|
|
31
|
+
def sub_image(layer, frame, eye = Eye::NONE)
|
|
32
|
+
js_subimage = js_call(:getSubImage, layer.js, frame.js, eye)
|
|
33
|
+
WebGLSubImage.new(js_subimage)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Get a sub-image for a specific view
|
|
37
|
+
# @param layer [Layer] The XR layer
|
|
38
|
+
# @param view [View] The XR view
|
|
39
|
+
# @return [WebGLSubImage]
|
|
40
|
+
def view_sub_image(layer, view)
|
|
41
|
+
js_subimage = js_call(:getViewSubImage, layer.js, view.js)
|
|
42
|
+
WebGLSubImage.new(js_subimage)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Get the reflection cube map
|
|
46
|
+
# @param light_probe [Lighting::Probe] The light probe
|
|
47
|
+
# @return [JS::Object, nil] WebGL texture or nil
|
|
48
|
+
def reflection_cube_map(light_probe)
|
|
49
|
+
js_call(:getReflectionCubeMap, light_probe.js)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Get the camera image for AR
|
|
53
|
+
# @param view [View] The XR view
|
|
54
|
+
# @return [JS::Object, nil] WebGL texture or nil
|
|
55
|
+
def camera_image(view)
|
|
56
|
+
js_call(:getCameraImage, view.js)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Get depth information for a view
|
|
60
|
+
# @param view [View] The XR view
|
|
61
|
+
# @return [Depth::WebGLInformation, nil]
|
|
62
|
+
def depth_information(view)
|
|
63
|
+
js_info = js_call(:getDepthInformation, view.js)
|
|
64
|
+
return nil if js_info.nil?
|
|
65
|
+
|
|
66
|
+
Depth::WebGLInformation.new(js_info)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WebXR
|
|
4
|
+
# XRWebGLLayer - WebGL rendering layer for XR
|
|
5
|
+
class WebGLLayer < Layer
|
|
6
|
+
# Create a new WebGL layer
|
|
7
|
+
# @param session [Session] The XR session
|
|
8
|
+
# @param context [JS::Object] The WebGL rendering context
|
|
9
|
+
# @param antialias [Boolean] Enable antialiasing
|
|
10
|
+
# @param depth [Boolean] Enable depth buffer
|
|
11
|
+
# @param stencil [Boolean] Enable stencil buffer
|
|
12
|
+
# @param alpha [Boolean] Enable alpha channel
|
|
13
|
+
# @param ignore_depth_values [Boolean] Ignore compositor depth values
|
|
14
|
+
# @param framebuffer_scale_factor [Float] Framebuffer scale factor
|
|
15
|
+
def initialize(session, context, antialias: true, depth: true, stencil: false,
|
|
16
|
+
alpha: true, ignore_depth_values: false, framebuffer_scale_factor: 1.0)
|
|
17
|
+
js_options = JS.eval("({})")
|
|
18
|
+
js_options[:antialias] = antialias
|
|
19
|
+
js_options[:depth] = depth
|
|
20
|
+
js_options[:stencil] = stencil
|
|
21
|
+
js_options[:alpha] = alpha
|
|
22
|
+
js_options[:ignoreDepthValues] = ignore_depth_values
|
|
23
|
+
js_options[:framebufferScaleFactor] = framebuffer_scale_factor
|
|
24
|
+
|
|
25
|
+
js_layer = JS.global[:XRWebGLLayer].new(session.js, context, js_options)
|
|
26
|
+
super(js_layer)
|
|
27
|
+
@context = context
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Wrap an existing JS XRWebGLLayer
|
|
31
|
+
# @param js_layer [JS::Object] The JavaScript XRWebGLLayer
|
|
32
|
+
# @return [WebGLLayer]
|
|
33
|
+
def self.wrap(js_layer)
|
|
34
|
+
layer = allocate
|
|
35
|
+
layer.instance_variable_set(:@js, js_layer)
|
|
36
|
+
layer
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Get the native framebuffer scale factor for a session
|
|
40
|
+
# @param session [Session] The XR session
|
|
41
|
+
# @return [Float]
|
|
42
|
+
def self.native_framebuffer_scale_factor(session)
|
|
43
|
+
JS.global[:XRWebGLLayer].call(:getNativeFramebufferScaleFactor, session.js).to_f
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Check if antialiasing is enabled
|
|
47
|
+
# @return [Boolean]
|
|
48
|
+
def antialias?
|
|
49
|
+
js_bool(:antialias)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Check if depth values are ignored
|
|
53
|
+
# @return [Boolean]
|
|
54
|
+
def ignore_depth_values?
|
|
55
|
+
js_bool(:ignoreDepthValues)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Get the fixed foveation level
|
|
59
|
+
# @return [Float, nil] 0.0 to 1.0, or nil if not supported
|
|
60
|
+
def fixed_foveation
|
|
61
|
+
js_float(:fixedFoveation)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Set the fixed foveation level
|
|
65
|
+
# @param value [Float] 0.0 (no foveation) to 1.0 (maximum foveation)
|
|
66
|
+
def fixed_foveation=(value)
|
|
67
|
+
@js[:fixedFoveation] = value
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Get the WebGL framebuffer
|
|
71
|
+
# @return [JS::Object] The WebGL framebuffer
|
|
72
|
+
def framebuffer
|
|
73
|
+
js_prop(:framebuffer)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Get the framebuffer width in pixels
|
|
77
|
+
# @return [Integer]
|
|
78
|
+
def framebuffer_width
|
|
79
|
+
js_int(:framebufferWidth) || 0
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Get the framebuffer height in pixels
|
|
83
|
+
# @return [Integer]
|
|
84
|
+
def framebuffer_height
|
|
85
|
+
js_int(:framebufferHeight) || 0
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Get the viewport for a specific view
|
|
89
|
+
# @param view [View] The XR view
|
|
90
|
+
# @return [Viewport]
|
|
91
|
+
def viewport(view)
|
|
92
|
+
js_viewport = js_call(:getViewport, view.js)
|
|
93
|
+
Viewport.new(js_viewport)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Get the framebuffer dimensions as a hash
|
|
97
|
+
# @return [Hash] Hash with :width, :height keys
|
|
98
|
+
def framebuffer_size
|
|
99
|
+
{ width: framebuffer_width, height: framebuffer_height }
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WebXR
|
|
4
|
+
# XRWebGLSubImage - Represents a sub-image of a WebGL layer
|
|
5
|
+
class WebGLSubImage < JSWrapper
|
|
6
|
+
# @param js_subimage [JS::Object] The XRWebGLSubImage JavaScript object
|
|
7
|
+
def initialize(js_subimage)
|
|
8
|
+
super(js_subimage)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Get the color texture
|
|
12
|
+
# @return [JS::Object] WebGL texture
|
|
13
|
+
def color_texture
|
|
14
|
+
js_prop(:colorTexture)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Get the depth/stencil texture
|
|
18
|
+
# @return [JS::Object, nil] WebGL texture or nil
|
|
19
|
+
def depth_stencil_texture
|
|
20
|
+
js_prop(:depthStencilTexture)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Get the motion vector texture
|
|
24
|
+
# @return [JS::Object, nil] WebGL texture or nil
|
|
25
|
+
def motion_vector_texture
|
|
26
|
+
js_prop(:motionVectorTexture)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Get the image index (for texture arrays)
|
|
30
|
+
# @return [Integer, nil]
|
|
31
|
+
def image_index
|
|
32
|
+
js_int(:imageIndex)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Get the texture width
|
|
36
|
+
# @return [Integer]
|
|
37
|
+
def texture_width
|
|
38
|
+
js_int(:textureWidth) || 0
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Get the texture height
|
|
42
|
+
# @return [Integer]
|
|
43
|
+
def texture_height
|
|
44
|
+
js_int(:textureHeight) || 0
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Get the viewport for this sub-image
|
|
48
|
+
# @return [Viewport]
|
|
49
|
+
def viewport
|
|
50
|
+
Viewport.new(js_prop(:viewport))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Get the texture size as a hash
|
|
54
|
+
# @return [Hash] Hash with :width, :height keys
|
|
55
|
+
def texture_size
|
|
56
|
+
{ width: texture_width, height: texture_height }
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|