Skip to content

Commit d54b8ab

Browse files
committed
Add xr-active-switch.ts
1 parent 5ab1430 commit d54b8ab

2 files changed

Lines changed: 108 additions & 0 deletions

File tree

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ export * from './vrm.js';
2424
export * from './wasd-controls.js';
2525
export * from './input-profile.js';
2626
export * from './orbital-camera.js';
27+
export * from './xr-active-switch.js';

xr-active-switch.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import {Component, Object3D} from '@wonderlandengine/api';
2+
import {property} from '@wonderlandengine/api/decorators.js';
3+
4+
const SCOPE_OPTIONS = ['this object', 'children only', 'this object & children'];
5+
const ACTION_OPTIONS = ['activate', 'deactivate', 'toggle', 'keep'];
6+
7+
/**
8+
* XR Active Switch Component for controlling component activation states
9+
* based on XR session status.
10+
*
11+
* This component allows toggling, activating, deactivating, or keeping the
12+
* current state of components when entering or exiting XR sessions.
13+
*
14+
* The scope of affected components can be limited to the current object,
15+
* its children, or both.
16+
*
17+
* Use this component to manage visibility, interactivity, or other properties
18+
* that depend on XR session states.
19+
*
20+
*/
21+
22+
export class XrActiveSwitch extends Component {
23+
static TypeName = 'xr-active-switch';
24+
25+
/** Action to perform when XR session starts */
26+
@property.enum(ACTION_OPTIONS)
27+
ifInXR: number = 0;
28+
29+
/** Action to perform when XR session ends */
30+
@property.enum(ACTION_OPTIONS)
31+
ifNotInXR: number = 1;
32+
33+
/** Scope of elements to affect */
34+
@property.enum(SCOPE_OPTIONS)
35+
scope: number = 2;
36+
37+
private components: Component[] = [];
38+
39+
start() {
40+
this.components = [];
41+
this.collectComponents();
42+
43+
// Initial state setup
44+
this.applyAction(this.engine.xr?.session ? this.ifInXR : this.ifNotInXR);
45+
46+
// Bind event handlers
47+
this.engine.onXRSessionStart.add(this.onSessionStart);
48+
this.engine.onXRSessionEnd.add(this.onSessionEnd);
49+
}
50+
51+
onActivate() {
52+
this.engine.onXRSessionStart.add(this.onSessionStart);
53+
this.engine.onXRSessionEnd.add(this.onSessionEnd);
54+
}
55+
56+
onDeactivate() {
57+
this.engine.onXRSessionStart.remove(this.onSessionStart);
58+
this.engine.onXRSessionEnd.remove(this.onSessionEnd);
59+
}
60+
61+
private collectComponents() {
62+
const mode = this.scope;
63+
64+
// Handle current object
65+
if (mode === 0 || mode === 2) {
66+
this.object
67+
.getComponents()
68+
.filter((c) => c.type !== XrActiveSwitch.TypeName)
69+
.forEach((c) => this.components.push(c));
70+
}
71+
72+
// Handle children
73+
if (mode === 1 || mode === 2) {
74+
this.processChildren(this.object);
75+
}
76+
}
77+
78+
private processChildren(obj: Object3D) {
79+
for (const child of obj.children) {
80+
child
81+
.getComponents()
82+
.filter((c) => c.type !== XrActiveSwitch.TypeName)
83+
.forEach((c) => this.components.push(c));
84+
this.processChildren(child); // Recurse through all descendants
85+
}
86+
}
87+
88+
private applyAction(action: number) {
89+
for (const comp of this.components) {
90+
if (action === 0) {
91+
comp.active = true;
92+
} else if (action === 1) {
93+
comp.active = false;
94+
} else if (action === 2) {
95+
comp.active = !comp.active;
96+
}
97+
}
98+
}
99+
100+
private onSessionStart = () => {
101+
this.applyAction(this.ifInXR);
102+
};
103+
104+
private onSessionEnd = () => {
105+
this.applyAction(this.ifNotInXR);
106+
};
107+
}

0 commit comments

Comments
 (0)