Skip to content

Commit eb956da

Browse files
committed
Add xr-active-switch.ts
1 parent 615fa1b commit eb956da

2 files changed

Lines changed: 132 additions & 0 deletions

File tree

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ 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';
2728

2829
export * from '@wonderlandengine/spatial-audio';

xr-active-switch.ts

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

0 commit comments

Comments
 (0)