@scenography/plugin-cardboard 0.2.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.
- package/dist/index.cjs +91 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +48 -0
- package/dist/index.d.mts +48 -0
- package/dist/index.mjs +89 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +42 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let three = require("three");
|
|
3
|
+
//#region src/DeviceOrientationControls.ts
|
|
4
|
+
const DEG2RAD = Math.PI / 180;
|
|
5
|
+
const ZEE = new three.Vector3(0, 0, 1);
|
|
6
|
+
const SCREEN_TO_VIEW = new three.Quaternion(-Math.SQRT1_2, 0, 0, Math.SQRT1_2);
|
|
7
|
+
const tmpEuler = new three.Euler();
|
|
8
|
+
const tmpScreenAdjust = new three.Quaternion();
|
|
9
|
+
/**
|
|
10
|
+
* Orient the camera from the device gyroscope (DeviceOrientation API) for
|
|
11
|
+
* cardboard/VR head-look. It only writes the camera while it has live sensor
|
|
12
|
+
* data, so on a desktop (no gyro) it's a no-op and the other controls keep the
|
|
13
|
+
* camera. iOS needs a permission prompt that must come from a user gesture —
|
|
14
|
+
* `enable()` is meant to be called from the cardboard button's click handler.
|
|
15
|
+
*/
|
|
16
|
+
var DeviceOrientationControls = class {
|
|
17
|
+
camera;
|
|
18
|
+
enabled = false;
|
|
19
|
+
hasData = false;
|
|
20
|
+
alpha = 0;
|
|
21
|
+
beta = 0;
|
|
22
|
+
gamma = 0;
|
|
23
|
+
screenAngle = 0;
|
|
24
|
+
constructor(camera) {
|
|
25
|
+
this.camera = camera;
|
|
26
|
+
this.readScreenAngle();
|
|
27
|
+
}
|
|
28
|
+
/** Request permission (iOS) and start listening. Safe to call repeatedly. */
|
|
29
|
+
enable() {
|
|
30
|
+
if (this.enabled) return;
|
|
31
|
+
this.enabled = true;
|
|
32
|
+
const DOE = window.DeviceOrientationEvent;
|
|
33
|
+
if (DOE && typeof DOE.requestPermission === "function") DOE.requestPermission().then((state) => state === "granted" ? this.attach() : this.enabled = false).catch(() => this.enabled = false);
|
|
34
|
+
else this.attach();
|
|
35
|
+
}
|
|
36
|
+
disable() {
|
|
37
|
+
if (!this.enabled) return;
|
|
38
|
+
this.enabled = false;
|
|
39
|
+
this.hasData = false;
|
|
40
|
+
window.removeEventListener("deviceorientation", this.onOrientation);
|
|
41
|
+
window.removeEventListener("orientationchange", this.onScreenChange);
|
|
42
|
+
}
|
|
43
|
+
/** Apply the latest reading to the camera. No-op until sensor data arrives. */
|
|
44
|
+
update() {
|
|
45
|
+
if (!this.enabled || !this.hasData) return;
|
|
46
|
+
tmpEuler.set(this.beta, this.alpha, -this.gamma, "YXZ");
|
|
47
|
+
this.camera.quaternion.setFromEuler(tmpEuler);
|
|
48
|
+
this.camera.quaternion.multiply(SCREEN_TO_VIEW);
|
|
49
|
+
this.camera.quaternion.multiply(tmpScreenAdjust.setFromAxisAngle(ZEE, -this.screenAngle));
|
|
50
|
+
}
|
|
51
|
+
attach() {
|
|
52
|
+
window.addEventListener("deviceorientation", this.onOrientation);
|
|
53
|
+
window.addEventListener("orientationchange", this.onScreenChange);
|
|
54
|
+
}
|
|
55
|
+
readScreenAngle() {
|
|
56
|
+
this.screenAngle = (screen.orientation?.angle ?? 0) * DEG2RAD;
|
|
57
|
+
}
|
|
58
|
+
onScreenChange = () => this.readScreenAngle();
|
|
59
|
+
onOrientation = (event) => {
|
|
60
|
+
if (event.alpha == null) return;
|
|
61
|
+
this.alpha = event.alpha * DEG2RAD;
|
|
62
|
+
this.beta = (event.beta ?? 0) * DEG2RAD;
|
|
63
|
+
this.gamma = (event.gamma ?? 0) * DEG2RAD;
|
|
64
|
+
this.hasData = true;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/plugin.ts
|
|
69
|
+
/**
|
|
70
|
+
* Cardboard head-look — a *system* plugin. It orients the kernel camera from the
|
|
71
|
+
* device gyroscope for phone-in-a-headset viewing, taking the final say on
|
|
72
|
+
* orientation each frame (so it must be registered *after* the roaming/framing
|
|
73
|
+
* systems). The kernel's stereo-render `cardboard` toggle drives its `setEnabled`
|
|
74
|
+
* (which requests the iOS motion-sensor permission, hence a user gesture).
|
|
75
|
+
*/
|
|
76
|
+
const cardboard = () => ({
|
|
77
|
+
name: "cardboard",
|
|
78
|
+
setup: (ctx) => {
|
|
79
|
+
const gyro = new DeviceOrientationControls(ctx.camera);
|
|
80
|
+
return {
|
|
81
|
+
update: () => gyro.update(),
|
|
82
|
+
dispose: () => gyro.disable(),
|
|
83
|
+
api: { setEnabled: (enabled) => enabled ? gyro.enable() : gyro.disable() }
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
//#endregion
|
|
88
|
+
exports.DeviceOrientationControls = DeviceOrientationControls;
|
|
89
|
+
exports.cardboard = cardboard;
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["Vector3","Quaternion","Euler"],"sources":["../src/DeviceOrientationControls.ts","../src/plugin.ts"],"sourcesContent":["import { Euler, Quaternion, Vector3, type PerspectiveCamera } from \"three\";\n\nconst DEG2RAD = Math.PI / 180;\nconst ZEE = new Vector3(0, 0, 1);\n// -90° about X: the phone's screen faces the user, but the camera looks out its\n// back, so the sensor frame is rotated into the viewing direction.\nconst SCREEN_TO_VIEW = new Quaternion(-Math.SQRT1_2, 0, 0, Math.SQRT1_2);\n\nconst tmpEuler = new Euler();\nconst tmpScreenAdjust = new Quaternion();\n\n/** iOS 13+ gates the motion sensors behind an async permission prompt. */\ninterface DeviceOrientationEventStatic {\n requestPermission?: () => Promise<\"granted\" | \"denied\">;\n}\n\n/**\n * Orient the camera from the device gyroscope (DeviceOrientation API) for\n * cardboard/VR head-look. It only writes the camera while it has live sensor\n * data, so on a desktop (no gyro) it's a no-op and the other controls keep the\n * camera. iOS needs a permission prompt that must come from a user gesture —\n * `enable()` is meant to be called from the cardboard button's click handler.\n */\nexport class DeviceOrientationControls {\n private enabled = false;\n private hasData = false;\n private alpha = 0;\n private beta = 0;\n private gamma = 0;\n private screenAngle = 0;\n\n constructor(private readonly camera: PerspectiveCamera) {\n this.readScreenAngle();\n }\n\n /** Request permission (iOS) and start listening. Safe to call repeatedly. */\n enable(): void {\n if (this.enabled) return;\n this.enabled = true;\n const DOE = (window as unknown as { DeviceOrientationEvent?: DeviceOrientationEventStatic })\n .DeviceOrientationEvent;\n if (DOE && typeof DOE.requestPermission === \"function\") {\n DOE.requestPermission()\n .then((state) => (state === \"granted\" ? this.attach() : (this.enabled = false)))\n .catch(() => (this.enabled = false));\n } else {\n this.attach();\n }\n }\n\n disable(): void {\n if (!this.enabled) return;\n this.enabled = false;\n this.hasData = false;\n window.removeEventListener(\"deviceorientation\", this.onOrientation);\n window.removeEventListener(\"orientationchange\", this.onScreenChange);\n }\n\n /** Apply the latest reading to the camera. No-op until sensor data arrives. */\n update(): void {\n if (!this.enabled || !this.hasData) return;\n tmpEuler.set(this.beta, this.alpha, -this.gamma, \"YXZ\");\n this.camera.quaternion.setFromEuler(tmpEuler);\n this.camera.quaternion.multiply(SCREEN_TO_VIEW);\n this.camera.quaternion.multiply(tmpScreenAdjust.setFromAxisAngle(ZEE, -this.screenAngle));\n }\n\n private attach(): void {\n window.addEventListener(\"deviceorientation\", this.onOrientation);\n window.addEventListener(\"orientationchange\", this.onScreenChange);\n }\n\n private readScreenAngle(): void {\n this.screenAngle = (screen.orientation?.angle ?? 0) * DEG2RAD;\n }\n\n private readonly onScreenChange = (): void => this.readScreenAngle();\n\n private readonly onOrientation = (event: DeviceOrientationEvent): void => {\n if (event.alpha == null) return;\n this.alpha = event.alpha * DEG2RAD;\n this.beta = (event.beta ?? 0) * DEG2RAD;\n this.gamma = (event.gamma ?? 0) * DEG2RAD;\n this.hasData = true;\n };\n}\n","import type { SystemPlugin } from \"@scenography/engine\";\n\nimport { DeviceOrientationControls } from \"./DeviceOrientationControls\";\n\n/** What the kernel's cardboard render toggle drives on this system. */\nexport interface CardboardApi {\n /** Turn gyroscope head-look on/off (must run in a user gesture on iOS). */\n setEnabled(enabled: boolean): void;\n}\n\n/**\n * Cardboard head-look — a *system* plugin. It orients the kernel camera from the\n * device gyroscope for phone-in-a-headset viewing, taking the final say on\n * orientation each frame (so it must be registered *after* the roaming/framing\n * systems). The kernel's stereo-render `cardboard` toggle drives its `setEnabled`\n * (which requests the iOS motion-sensor permission, hence a user gesture).\n */\nexport const cardboard = (): SystemPlugin<CardboardApi> => ({\n name: \"cardboard\",\n setup: (ctx) => {\n const gyro = new DeviceOrientationControls(ctx.camera);\n return {\n update: () => gyro.update(),\n dispose: () => gyro.disable(),\n api: { setEnabled: (enabled) => (enabled ? gyro.enable() : gyro.disable()) },\n };\n },\n});\n"],"mappings":";;;AAEA,MAAM,UAAU,KAAK,KAAK;AAC1B,MAAM,MAAM,IAAIA,MAAAA,QAAQ,GAAG,GAAG,CAAC;AAG/B,MAAM,iBAAiB,IAAIC,MAAAA,WAAW,CAAC,KAAK,SAAS,GAAG,GAAG,KAAK,OAAO;AAEvE,MAAM,WAAW,IAAIC,MAAAA,MAAM;AAC3B,MAAM,kBAAkB,IAAID,MAAAA,WAAW;;;;;;;;AAcvC,IAAa,4BAAb,MAAuC;CAQR;CAP7B,UAAkB;CAClB,UAAkB;CAClB,QAAgB;CAChB,OAAe;CACf,QAAgB;CAChB,cAAsB;CAEtB,YAAY,QAA4C;EAA3B,KAAA,SAAA;EAC3B,KAAK,gBAAgB;CACvB;;CAGA,SAAe;EACb,IAAI,KAAK,SAAS;EAClB,KAAK,UAAU;EACf,MAAM,MAAO,OACV;EACH,IAAI,OAAO,OAAO,IAAI,sBAAsB,YAC1C,IAAI,kBAAkB,CAAC,CACpB,MAAM,UAAW,UAAU,YAAY,KAAK,OAAO,IAAK,KAAK,UAAU,KAAO,CAAC,CAC/E,YAAa,KAAK,UAAU,KAAM;OAErC,KAAK,OAAO;CAEhB;CAEA,UAAgB;EACd,IAAI,CAAC,KAAK,SAAS;EACnB,KAAK,UAAU;EACf,KAAK,UAAU;EACf,OAAO,oBAAoB,qBAAqB,KAAK,aAAa;EAClE,OAAO,oBAAoB,qBAAqB,KAAK,cAAc;CACrE;;CAGA,SAAe;EACb,IAAI,CAAC,KAAK,WAAW,CAAC,KAAK,SAAS;EACpC,SAAS,IAAI,KAAK,MAAM,KAAK,OAAO,CAAC,KAAK,OAAO,KAAK;EACtD,KAAK,OAAO,WAAW,aAAa,QAAQ;EAC5C,KAAK,OAAO,WAAW,SAAS,cAAc;EAC9C,KAAK,OAAO,WAAW,SAAS,gBAAgB,iBAAiB,KAAK,CAAC,KAAK,WAAW,CAAC;CAC1F;CAEA,SAAuB;EACrB,OAAO,iBAAiB,qBAAqB,KAAK,aAAa;EAC/D,OAAO,iBAAiB,qBAAqB,KAAK,cAAc;CAClE;CAEA,kBAAgC;EAC9B,KAAK,eAAe,OAAO,aAAa,SAAS,KAAK;CACxD;CAEA,uBAA8C,KAAK,gBAAgB;CAEnE,iBAAkC,UAAwC;EACxE,IAAI,MAAM,SAAS,MAAM;EACzB,KAAK,QAAQ,MAAM,QAAQ;EAC3B,KAAK,QAAQ,MAAM,QAAQ,KAAK;EAChC,KAAK,SAAS,MAAM,SAAS,KAAK;EAClC,KAAK,UAAU;CACjB;AACF;;;;;;;;;;ACpEA,MAAa,mBAA+C;CAC1D,MAAM;CACN,QAAQ,QAAQ;EACd,MAAM,OAAO,IAAI,0BAA0B,IAAI,MAAM;EACrD,OAAO;GACL,cAAc,KAAK,OAAO;GAC1B,eAAe,KAAK,QAAQ;GAC5B,KAAK,EAAE,aAAa,YAAa,UAAU,KAAK,OAAO,IAAI,KAAK,QAAQ,EAAG;EAC7E;CACF;AACF"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { SystemPlugin } from "@scenography/engine";
|
|
2
|
+
import { PerspectiveCamera } from "three";
|
|
3
|
+
|
|
4
|
+
//#region src/plugin.d.ts
|
|
5
|
+
/** What the kernel's cardboard render toggle drives on this system. */
|
|
6
|
+
interface CardboardApi {
|
|
7
|
+
/** Turn gyroscope head-look on/off (must run in a user gesture on iOS). */
|
|
8
|
+
setEnabled(enabled: boolean): void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Cardboard head-look — a *system* plugin. It orients the kernel camera from the
|
|
12
|
+
* device gyroscope for phone-in-a-headset viewing, taking the final say on
|
|
13
|
+
* orientation each frame (so it must be registered *after* the roaming/framing
|
|
14
|
+
* systems). The kernel's stereo-render `cardboard` toggle drives its `setEnabled`
|
|
15
|
+
* (which requests the iOS motion-sensor permission, hence a user gesture).
|
|
16
|
+
*/
|
|
17
|
+
declare const cardboard: () => SystemPlugin<CardboardApi>;
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/DeviceOrientationControls.d.ts
|
|
20
|
+
/**
|
|
21
|
+
* Orient the camera from the device gyroscope (DeviceOrientation API) for
|
|
22
|
+
* cardboard/VR head-look. It only writes the camera while it has live sensor
|
|
23
|
+
* data, so on a desktop (no gyro) it's a no-op and the other controls keep the
|
|
24
|
+
* camera. iOS needs a permission prompt that must come from a user gesture —
|
|
25
|
+
* `enable()` is meant to be called from the cardboard button's click handler.
|
|
26
|
+
*/
|
|
27
|
+
declare class DeviceOrientationControls {
|
|
28
|
+
private readonly camera;
|
|
29
|
+
private enabled;
|
|
30
|
+
private hasData;
|
|
31
|
+
private alpha;
|
|
32
|
+
private beta;
|
|
33
|
+
private gamma;
|
|
34
|
+
private screenAngle;
|
|
35
|
+
constructor(camera: PerspectiveCamera);
|
|
36
|
+
/** Request permission (iOS) and start listening. Safe to call repeatedly. */
|
|
37
|
+
enable(): void;
|
|
38
|
+
disable(): void;
|
|
39
|
+
/** Apply the latest reading to the camera. No-op until sensor data arrives. */
|
|
40
|
+
update(): void;
|
|
41
|
+
private attach;
|
|
42
|
+
private readScreenAngle;
|
|
43
|
+
private readonly onScreenChange;
|
|
44
|
+
private readonly onOrientation;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { type CardboardApi, DeviceOrientationControls, cardboard };
|
|
48
|
+
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { PerspectiveCamera } from "three";
|
|
2
|
+
import { SystemPlugin } from "@scenography/engine";
|
|
3
|
+
|
|
4
|
+
//#region src/plugin.d.ts
|
|
5
|
+
/** What the kernel's cardboard render toggle drives on this system. */
|
|
6
|
+
interface CardboardApi {
|
|
7
|
+
/** Turn gyroscope head-look on/off (must run in a user gesture on iOS). */
|
|
8
|
+
setEnabled(enabled: boolean): void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Cardboard head-look — a *system* plugin. It orients the kernel camera from the
|
|
12
|
+
* device gyroscope for phone-in-a-headset viewing, taking the final say on
|
|
13
|
+
* orientation each frame (so it must be registered *after* the roaming/framing
|
|
14
|
+
* systems). The kernel's stereo-render `cardboard` toggle drives its `setEnabled`
|
|
15
|
+
* (which requests the iOS motion-sensor permission, hence a user gesture).
|
|
16
|
+
*/
|
|
17
|
+
declare const cardboard: () => SystemPlugin<CardboardApi>;
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/DeviceOrientationControls.d.ts
|
|
20
|
+
/**
|
|
21
|
+
* Orient the camera from the device gyroscope (DeviceOrientation API) for
|
|
22
|
+
* cardboard/VR head-look. It only writes the camera while it has live sensor
|
|
23
|
+
* data, so on a desktop (no gyro) it's a no-op and the other controls keep the
|
|
24
|
+
* camera. iOS needs a permission prompt that must come from a user gesture —
|
|
25
|
+
* `enable()` is meant to be called from the cardboard button's click handler.
|
|
26
|
+
*/
|
|
27
|
+
declare class DeviceOrientationControls {
|
|
28
|
+
private readonly camera;
|
|
29
|
+
private enabled;
|
|
30
|
+
private hasData;
|
|
31
|
+
private alpha;
|
|
32
|
+
private beta;
|
|
33
|
+
private gamma;
|
|
34
|
+
private screenAngle;
|
|
35
|
+
constructor(camera: PerspectiveCamera);
|
|
36
|
+
/** Request permission (iOS) and start listening. Safe to call repeatedly. */
|
|
37
|
+
enable(): void;
|
|
38
|
+
disable(): void;
|
|
39
|
+
/** Apply the latest reading to the camera. No-op until sensor data arrives. */
|
|
40
|
+
update(): void;
|
|
41
|
+
private attach;
|
|
42
|
+
private readScreenAngle;
|
|
43
|
+
private readonly onScreenChange;
|
|
44
|
+
private readonly onOrientation;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { type CardboardApi, DeviceOrientationControls, cardboard };
|
|
48
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Euler, Quaternion, Vector3 } from "three";
|
|
2
|
+
//#region src/DeviceOrientationControls.ts
|
|
3
|
+
const DEG2RAD = Math.PI / 180;
|
|
4
|
+
const ZEE = new Vector3(0, 0, 1);
|
|
5
|
+
const SCREEN_TO_VIEW = new Quaternion(-Math.SQRT1_2, 0, 0, Math.SQRT1_2);
|
|
6
|
+
const tmpEuler = new Euler();
|
|
7
|
+
const tmpScreenAdjust = new Quaternion();
|
|
8
|
+
/**
|
|
9
|
+
* Orient the camera from the device gyroscope (DeviceOrientation API) for
|
|
10
|
+
* cardboard/VR head-look. It only writes the camera while it has live sensor
|
|
11
|
+
* data, so on a desktop (no gyro) it's a no-op and the other controls keep the
|
|
12
|
+
* camera. iOS needs a permission prompt that must come from a user gesture —
|
|
13
|
+
* `enable()` is meant to be called from the cardboard button's click handler.
|
|
14
|
+
*/
|
|
15
|
+
var DeviceOrientationControls = class {
|
|
16
|
+
camera;
|
|
17
|
+
enabled = false;
|
|
18
|
+
hasData = false;
|
|
19
|
+
alpha = 0;
|
|
20
|
+
beta = 0;
|
|
21
|
+
gamma = 0;
|
|
22
|
+
screenAngle = 0;
|
|
23
|
+
constructor(camera) {
|
|
24
|
+
this.camera = camera;
|
|
25
|
+
this.readScreenAngle();
|
|
26
|
+
}
|
|
27
|
+
/** Request permission (iOS) and start listening. Safe to call repeatedly. */
|
|
28
|
+
enable() {
|
|
29
|
+
if (this.enabled) return;
|
|
30
|
+
this.enabled = true;
|
|
31
|
+
const DOE = window.DeviceOrientationEvent;
|
|
32
|
+
if (DOE && typeof DOE.requestPermission === "function") DOE.requestPermission().then((state) => state === "granted" ? this.attach() : this.enabled = false).catch(() => this.enabled = false);
|
|
33
|
+
else this.attach();
|
|
34
|
+
}
|
|
35
|
+
disable() {
|
|
36
|
+
if (!this.enabled) return;
|
|
37
|
+
this.enabled = false;
|
|
38
|
+
this.hasData = false;
|
|
39
|
+
window.removeEventListener("deviceorientation", this.onOrientation);
|
|
40
|
+
window.removeEventListener("orientationchange", this.onScreenChange);
|
|
41
|
+
}
|
|
42
|
+
/** Apply the latest reading to the camera. No-op until sensor data arrives. */
|
|
43
|
+
update() {
|
|
44
|
+
if (!this.enabled || !this.hasData) return;
|
|
45
|
+
tmpEuler.set(this.beta, this.alpha, -this.gamma, "YXZ");
|
|
46
|
+
this.camera.quaternion.setFromEuler(tmpEuler);
|
|
47
|
+
this.camera.quaternion.multiply(SCREEN_TO_VIEW);
|
|
48
|
+
this.camera.quaternion.multiply(tmpScreenAdjust.setFromAxisAngle(ZEE, -this.screenAngle));
|
|
49
|
+
}
|
|
50
|
+
attach() {
|
|
51
|
+
window.addEventListener("deviceorientation", this.onOrientation);
|
|
52
|
+
window.addEventListener("orientationchange", this.onScreenChange);
|
|
53
|
+
}
|
|
54
|
+
readScreenAngle() {
|
|
55
|
+
this.screenAngle = (screen.orientation?.angle ?? 0) * DEG2RAD;
|
|
56
|
+
}
|
|
57
|
+
onScreenChange = () => this.readScreenAngle();
|
|
58
|
+
onOrientation = (event) => {
|
|
59
|
+
if (event.alpha == null) return;
|
|
60
|
+
this.alpha = event.alpha * DEG2RAD;
|
|
61
|
+
this.beta = (event.beta ?? 0) * DEG2RAD;
|
|
62
|
+
this.gamma = (event.gamma ?? 0) * DEG2RAD;
|
|
63
|
+
this.hasData = true;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/plugin.ts
|
|
68
|
+
/**
|
|
69
|
+
* Cardboard head-look — a *system* plugin. It orients the kernel camera from the
|
|
70
|
+
* device gyroscope for phone-in-a-headset viewing, taking the final say on
|
|
71
|
+
* orientation each frame (so it must be registered *after* the roaming/framing
|
|
72
|
+
* systems). The kernel's stereo-render `cardboard` toggle drives its `setEnabled`
|
|
73
|
+
* (which requests the iOS motion-sensor permission, hence a user gesture).
|
|
74
|
+
*/
|
|
75
|
+
const cardboard = () => ({
|
|
76
|
+
name: "cardboard",
|
|
77
|
+
setup: (ctx) => {
|
|
78
|
+
const gyro = new DeviceOrientationControls(ctx.camera);
|
|
79
|
+
return {
|
|
80
|
+
update: () => gyro.update(),
|
|
81
|
+
dispose: () => gyro.disable(),
|
|
82
|
+
api: { setEnabled: (enabled) => enabled ? gyro.enable() : gyro.disable() }
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
//#endregion
|
|
87
|
+
export { DeviceOrientationControls, cardboard };
|
|
88
|
+
|
|
89
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/DeviceOrientationControls.ts","../src/plugin.ts"],"sourcesContent":["import { Euler, Quaternion, Vector3, type PerspectiveCamera } from \"three\";\n\nconst DEG2RAD = Math.PI / 180;\nconst ZEE = new Vector3(0, 0, 1);\n// -90° about X: the phone's screen faces the user, but the camera looks out its\n// back, so the sensor frame is rotated into the viewing direction.\nconst SCREEN_TO_VIEW = new Quaternion(-Math.SQRT1_2, 0, 0, Math.SQRT1_2);\n\nconst tmpEuler = new Euler();\nconst tmpScreenAdjust = new Quaternion();\n\n/** iOS 13+ gates the motion sensors behind an async permission prompt. */\ninterface DeviceOrientationEventStatic {\n requestPermission?: () => Promise<\"granted\" | \"denied\">;\n}\n\n/**\n * Orient the camera from the device gyroscope (DeviceOrientation API) for\n * cardboard/VR head-look. It only writes the camera while it has live sensor\n * data, so on a desktop (no gyro) it's a no-op and the other controls keep the\n * camera. iOS needs a permission prompt that must come from a user gesture —\n * `enable()` is meant to be called from the cardboard button's click handler.\n */\nexport class DeviceOrientationControls {\n private enabled = false;\n private hasData = false;\n private alpha = 0;\n private beta = 0;\n private gamma = 0;\n private screenAngle = 0;\n\n constructor(private readonly camera: PerspectiveCamera) {\n this.readScreenAngle();\n }\n\n /** Request permission (iOS) and start listening. Safe to call repeatedly. */\n enable(): void {\n if (this.enabled) return;\n this.enabled = true;\n const DOE = (window as unknown as { DeviceOrientationEvent?: DeviceOrientationEventStatic })\n .DeviceOrientationEvent;\n if (DOE && typeof DOE.requestPermission === \"function\") {\n DOE.requestPermission()\n .then((state) => (state === \"granted\" ? this.attach() : (this.enabled = false)))\n .catch(() => (this.enabled = false));\n } else {\n this.attach();\n }\n }\n\n disable(): void {\n if (!this.enabled) return;\n this.enabled = false;\n this.hasData = false;\n window.removeEventListener(\"deviceorientation\", this.onOrientation);\n window.removeEventListener(\"orientationchange\", this.onScreenChange);\n }\n\n /** Apply the latest reading to the camera. No-op until sensor data arrives. */\n update(): void {\n if (!this.enabled || !this.hasData) return;\n tmpEuler.set(this.beta, this.alpha, -this.gamma, \"YXZ\");\n this.camera.quaternion.setFromEuler(tmpEuler);\n this.camera.quaternion.multiply(SCREEN_TO_VIEW);\n this.camera.quaternion.multiply(tmpScreenAdjust.setFromAxisAngle(ZEE, -this.screenAngle));\n }\n\n private attach(): void {\n window.addEventListener(\"deviceorientation\", this.onOrientation);\n window.addEventListener(\"orientationchange\", this.onScreenChange);\n }\n\n private readScreenAngle(): void {\n this.screenAngle = (screen.orientation?.angle ?? 0) * DEG2RAD;\n }\n\n private readonly onScreenChange = (): void => this.readScreenAngle();\n\n private readonly onOrientation = (event: DeviceOrientationEvent): void => {\n if (event.alpha == null) return;\n this.alpha = event.alpha * DEG2RAD;\n this.beta = (event.beta ?? 0) * DEG2RAD;\n this.gamma = (event.gamma ?? 0) * DEG2RAD;\n this.hasData = true;\n };\n}\n","import type { SystemPlugin } from \"@scenography/engine\";\n\nimport { DeviceOrientationControls } from \"./DeviceOrientationControls\";\n\n/** What the kernel's cardboard render toggle drives on this system. */\nexport interface CardboardApi {\n /** Turn gyroscope head-look on/off (must run in a user gesture on iOS). */\n setEnabled(enabled: boolean): void;\n}\n\n/**\n * Cardboard head-look — a *system* plugin. It orients the kernel camera from the\n * device gyroscope for phone-in-a-headset viewing, taking the final say on\n * orientation each frame (so it must be registered *after* the roaming/framing\n * systems). The kernel's stereo-render `cardboard` toggle drives its `setEnabled`\n * (which requests the iOS motion-sensor permission, hence a user gesture).\n */\nexport const cardboard = (): SystemPlugin<CardboardApi> => ({\n name: \"cardboard\",\n setup: (ctx) => {\n const gyro = new DeviceOrientationControls(ctx.camera);\n return {\n update: () => gyro.update(),\n dispose: () => gyro.disable(),\n api: { setEnabled: (enabled) => (enabled ? gyro.enable() : gyro.disable()) },\n };\n },\n});\n"],"mappings":";;AAEA,MAAM,UAAU,KAAK,KAAK;AAC1B,MAAM,MAAM,IAAI,QAAQ,GAAG,GAAG,CAAC;AAG/B,MAAM,iBAAiB,IAAI,WAAW,CAAC,KAAK,SAAS,GAAG,GAAG,KAAK,OAAO;AAEvE,MAAM,WAAW,IAAI,MAAM;AAC3B,MAAM,kBAAkB,IAAI,WAAW;;;;;;;;AAcvC,IAAa,4BAAb,MAAuC;CAQR;CAP7B,UAAkB;CAClB,UAAkB;CAClB,QAAgB;CAChB,OAAe;CACf,QAAgB;CAChB,cAAsB;CAEtB,YAAY,QAA4C;EAA3B,KAAA,SAAA;EAC3B,KAAK,gBAAgB;CACvB;;CAGA,SAAe;EACb,IAAI,KAAK,SAAS;EAClB,KAAK,UAAU;EACf,MAAM,MAAO,OACV;EACH,IAAI,OAAO,OAAO,IAAI,sBAAsB,YAC1C,IAAI,kBAAkB,CAAC,CACpB,MAAM,UAAW,UAAU,YAAY,KAAK,OAAO,IAAK,KAAK,UAAU,KAAO,CAAC,CAC/E,YAAa,KAAK,UAAU,KAAM;OAErC,KAAK,OAAO;CAEhB;CAEA,UAAgB;EACd,IAAI,CAAC,KAAK,SAAS;EACnB,KAAK,UAAU;EACf,KAAK,UAAU;EACf,OAAO,oBAAoB,qBAAqB,KAAK,aAAa;EAClE,OAAO,oBAAoB,qBAAqB,KAAK,cAAc;CACrE;;CAGA,SAAe;EACb,IAAI,CAAC,KAAK,WAAW,CAAC,KAAK,SAAS;EACpC,SAAS,IAAI,KAAK,MAAM,KAAK,OAAO,CAAC,KAAK,OAAO,KAAK;EACtD,KAAK,OAAO,WAAW,aAAa,QAAQ;EAC5C,KAAK,OAAO,WAAW,SAAS,cAAc;EAC9C,KAAK,OAAO,WAAW,SAAS,gBAAgB,iBAAiB,KAAK,CAAC,KAAK,WAAW,CAAC;CAC1F;CAEA,SAAuB;EACrB,OAAO,iBAAiB,qBAAqB,KAAK,aAAa;EAC/D,OAAO,iBAAiB,qBAAqB,KAAK,cAAc;CAClE;CAEA,kBAAgC;EAC9B,KAAK,eAAe,OAAO,aAAa,SAAS,KAAK;CACxD;CAEA,uBAA8C,KAAK,gBAAgB;CAEnE,iBAAkC,UAAwC;EACxE,IAAI,MAAM,SAAS,MAAM;EACzB,KAAK,QAAQ,MAAM,QAAQ;EAC3B,KAAK,QAAQ,MAAM,QAAQ,KAAK;EAChC,KAAK,SAAS,MAAM,SAAS,KAAK;EAClC,KAAK,UAAU;CACjB;AACF;;;;;;;;;;ACpEA,MAAa,mBAA+C;CAC1D,MAAM;CACN,QAAQ,QAAQ;EACd,MAAM,OAAO,IAAI,0BAA0B,IAAI,MAAM;EACrD,OAAO;GACL,cAAc,KAAK,OAAO;GAC1B,eAAe,KAAK,QAAQ;GAC5B,KAAK,EAAE,aAAa,YAAa,UAAU,KAAK,OAAO,IAAI,KAAK,QAAQ,EAAG;EAC7E;CACF;AACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@scenography/plugin-cardboard",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Cardboard plugin for scenography: gyroscope head-look (DeviceOrientation) for phone-in-a-headset viewing. A system plugin driven by the kernel's stereo-render toggle.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"type": "module",
|
|
10
|
+
"main": "./dist/index.cjs",
|
|
11
|
+
"module": "./dist/index.mjs",
|
|
12
|
+
"types": "./dist/index.d.cts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": {
|
|
16
|
+
"types": "./dist/index.d.mts",
|
|
17
|
+
"default": "./dist/index.mjs"
|
|
18
|
+
},
|
|
19
|
+
"require": {
|
|
20
|
+
"types": "./dist/index.d.cts",
|
|
21
|
+
"default": "./dist/index.cjs"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@scenography/scene": "0.2.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/three": "^0.160.0",
|
|
30
|
+
"three": "^0.160.0",
|
|
31
|
+
"@scenography/engine": "0.2.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"three": ">=0.160.0",
|
|
35
|
+
"@scenography/engine": "0.2.0"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsdown",
|
|
39
|
+
"dev": "tsdown --watch",
|
|
40
|
+
"typecheck": "tsc --noEmit"
|
|
41
|
+
}
|
|
42
|
+
}
|