Favivon - Correção

This commit is contained in:
2026-05-30 19:59:39 -03:00
parent 76ddaa815d
commit d7dfd221f0
32859 changed files with 5459654 additions and 404 deletions
+119
View File
@@ -0,0 +1,119 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const ARButton = {
createButton(renderer, sessionInit = {}) {
const button = document.createElement("button");
function showStartAR() {
if (sessionInit.domOverlay === void 0) {
const overlay = document.createElement("div");
overlay.style.display = "none";
document.body.appendChild(overlay);
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width", "38px");
svg.setAttribute("height", "38px");
svg.style.position = "absolute";
svg.style.right = "20px";
svg.style.top = "20px";
svg.addEventListener("click", function() {
currentSession == null ? void 0 : currentSession.end();
});
overlay.appendChild(svg);
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("d", "M 12,12 L 28,28 M 28,12 12,28");
path.setAttribute("stroke", "#fff");
path.setAttribute("stroke-width", "2px");
svg.appendChild(path);
if (sessionInit.optionalFeatures === void 0) {
sessionInit.optionalFeatures = [];
}
sessionInit.optionalFeatures.push("dom-overlay");
sessionInit.domOverlay = { root: overlay };
}
let currentSession = null;
async function onSessionStarted(session) {
session.addEventListener("end", onSessionEnded);
renderer.xr.setReferenceSpaceType("local");
await renderer.xr.setSession(session);
button.textContent = "STOP AR";
sessionInit.domOverlay.root.style.display = "";
currentSession = session;
}
function onSessionEnded() {
currentSession.removeEventListener("end", onSessionEnded);
button.textContent = "START AR";
sessionInit.domOverlay.root.style.display = "none";
currentSession = null;
}
button.style.display = "";
button.style.cursor = "pointer";
button.style.left = "calc(50% - 50px)";
button.style.width = "100px";
button.textContent = "START AR";
button.onmouseenter = () => {
button.style.opacity = "1.0";
};
button.onmouseleave = () => {
button.style.opacity = "0.5";
};
button.onclick = () => {
if (currentSession === null) {
navigator.xr.requestSession("immersive-ar", sessionInit).then(onSessionStarted);
} else {
currentSession.end();
}
};
}
function disableButton() {
button.style.display = "";
button.style.cursor = "auto";
button.style.left = "calc(50% - 75px)";
button.style.width = "150px";
button.onmouseenter = null;
button.onmouseleave = null;
button.onclick = null;
}
function showARNotSupported() {
disableButton();
button.textContent = "AR NOT SUPPORTED";
}
function stylizeElement(element) {
element.style.position = "absolute";
element.style.bottom = "20px";
element.style.padding = "12px 6px";
element.style.border = "1px solid #fff";
element.style.borderRadius = "4px";
element.style.background = "rgba(0,0,0,0.1)";
element.style.color = "#fff";
element.style.font = "normal 13px sans-serif";
element.style.textAlign = "center";
element.style.opacity = "0.5";
element.style.outline = "none";
element.style.zIndex = "999";
}
if ("xr" in navigator) {
button.id = "ARButton";
button.style.display = "none";
stylizeElement(button);
navigator.xr.isSessionSupported("immersive-ar").then(function(supported) {
supported ? showStartAR() : showARNotSupported();
}).catch(showARNotSupported);
return button;
} else {
const message = document.createElement("a");
if (window.isSecureContext === false) {
message.href = document.location.href.replace(/^http:/, "https:");
message.innerHTML = "WEBXR NEEDS HTTPS";
} else {
message.href = "https://immersiveweb.dev/";
message.innerHTML = "WEBXR NOT AVAILABLE";
}
message.style.left = "calc(50% - 90px)";
message.style.width = "180px";
message.style.textDecoration = "none";
stylizeElement(message);
return message;
}
}
};
exports.ARButton = ARButton;
//# sourceMappingURL=ARButton.cjs.map
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
/// <reference types="webxr" />
import { WebGLRenderer } from 'three';
declare const ARButton: {
createButton(renderer: WebGLRenderer, sessionInit?: XRSessionInit): HTMLButtonElement | HTMLAnchorElement;
};
export { ARButton };
+119
View File
@@ -0,0 +1,119 @@
const ARButton = {
createButton(renderer, sessionInit = {}) {
const button = document.createElement("button");
function showStartAR() {
if (sessionInit.domOverlay === void 0) {
const overlay = document.createElement("div");
overlay.style.display = "none";
document.body.appendChild(overlay);
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width", "38px");
svg.setAttribute("height", "38px");
svg.style.position = "absolute";
svg.style.right = "20px";
svg.style.top = "20px";
svg.addEventListener("click", function() {
currentSession == null ? void 0 : currentSession.end();
});
overlay.appendChild(svg);
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("d", "M 12,12 L 28,28 M 28,12 12,28");
path.setAttribute("stroke", "#fff");
path.setAttribute("stroke-width", "2px");
svg.appendChild(path);
if (sessionInit.optionalFeatures === void 0) {
sessionInit.optionalFeatures = [];
}
sessionInit.optionalFeatures.push("dom-overlay");
sessionInit.domOverlay = { root: overlay };
}
let currentSession = null;
async function onSessionStarted(session) {
session.addEventListener("end", onSessionEnded);
renderer.xr.setReferenceSpaceType("local");
await renderer.xr.setSession(session);
button.textContent = "STOP AR";
sessionInit.domOverlay.root.style.display = "";
currentSession = session;
}
function onSessionEnded() {
currentSession.removeEventListener("end", onSessionEnded);
button.textContent = "START AR";
sessionInit.domOverlay.root.style.display = "none";
currentSession = null;
}
button.style.display = "";
button.style.cursor = "pointer";
button.style.left = "calc(50% - 50px)";
button.style.width = "100px";
button.textContent = "START AR";
button.onmouseenter = () => {
button.style.opacity = "1.0";
};
button.onmouseleave = () => {
button.style.opacity = "0.5";
};
button.onclick = () => {
if (currentSession === null) {
navigator.xr.requestSession("immersive-ar", sessionInit).then(onSessionStarted);
} else {
currentSession.end();
}
};
}
function disableButton() {
button.style.display = "";
button.style.cursor = "auto";
button.style.left = "calc(50% - 75px)";
button.style.width = "150px";
button.onmouseenter = null;
button.onmouseleave = null;
button.onclick = null;
}
function showARNotSupported() {
disableButton();
button.textContent = "AR NOT SUPPORTED";
}
function stylizeElement(element) {
element.style.position = "absolute";
element.style.bottom = "20px";
element.style.padding = "12px 6px";
element.style.border = "1px solid #fff";
element.style.borderRadius = "4px";
element.style.background = "rgba(0,0,0,0.1)";
element.style.color = "#fff";
element.style.font = "normal 13px sans-serif";
element.style.textAlign = "center";
element.style.opacity = "0.5";
element.style.outline = "none";
element.style.zIndex = "999";
}
if ("xr" in navigator) {
button.id = "ARButton";
button.style.display = "none";
stylizeElement(button);
navigator.xr.isSessionSupported("immersive-ar").then(function(supported) {
supported ? showStartAR() : showARNotSupported();
}).catch(showARNotSupported);
return button;
} else {
const message = document.createElement("a");
if (window.isSecureContext === false) {
message.href = document.location.href.replace(/^http:/, "https:");
message.innerHTML = "WEBXR NEEDS HTTPS";
} else {
message.href = "https://immersiveweb.dev/";
message.innerHTML = "WEBXR NOT AVAILABLE";
}
message.style.left = "calc(50% - 90px)";
message.style.width = "180px";
message.style.textDecoration = "none";
stylizeElement(message);
return message;
}
}
};
export {
ARButton
};
//# sourceMappingURL=ARButton.js.map
File diff suppressed because one or more lines are too long
+83
View File
@@ -0,0 +1,83 @@
"use strict";
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const THREE = require("three");
const XRHandMeshModel = require("./XRHandMeshModel.cjs");
const TOUCH_RADIUS = 0.01;
const POINTING_JOINT = "index-finger-tip";
class OculusHandModel extends THREE.Object3D {
constructor(controller, leftModelPath, rightModelPath) {
super();
__publicField(this, "controller");
__publicField(this, "motionController");
__publicField(this, "envMap");
__publicField(this, "mesh");
__publicField(this, "xrInputSource");
this.controller = controller;
this.motionController = null;
this.envMap = null;
this.mesh = null;
this.xrInputSource = null;
controller.addEventListener("connected", (event) => {
const xrInputSource = event.data;
if (xrInputSource.hand && !this.motionController) {
this.xrInputSource = xrInputSource;
this.motionController = new XRHandMeshModel.XRHandMeshModel(
this,
controller,
void 0,
xrInputSource.handedness,
xrInputSource.handedness === "left" ? leftModelPath : rightModelPath
);
}
});
controller.addEventListener("disconnected", () => {
this.dispose();
});
}
updateMatrixWorld(force) {
super.updateMatrixWorld(force);
if (this.motionController) {
this.motionController.updateMesh();
}
}
getPointerPosition() {
const indexFingerTip = this.controller.joints[POINTING_JOINT];
if (indexFingerTip) {
return indexFingerTip.position;
} else {
return null;
}
}
intersectBoxObject(boxObject) {
const pointerPosition = this.getPointerPosition();
if (pointerPosition) {
const indexSphere = new THREE.Sphere(pointerPosition, TOUCH_RADIUS);
const box = new THREE.Box3().setFromObject(boxObject);
return indexSphere.intersectsBox(box);
} else {
return false;
}
}
checkButton(button) {
if (this.intersectBoxObject(button)) {
button.onPress();
} else {
button.onClear();
}
if (button.isPressed()) {
button.whilePressed();
}
}
dispose() {
this.clear();
this.motionController = null;
}
}
exports.OculusHandModel = OculusHandModel;
//# sourceMappingURL=OculusHandModel.cjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"OculusHandModel.cjs","sources":["../../src/webxr/OculusHandModel.ts"],"sourcesContent":["import { Object3D, Sphere, Box3, Mesh, Texture, Vector3 } from 'three'\nimport { XRHandMeshModel } from './XRHandMeshModel'\n\nconst TOUCH_RADIUS = 0.01\nconst POINTING_JOINT = 'index-finger-tip'\n\nexport interface XRButton extends Object3D {\n onPress(): void\n onClear(): void\n isPressed(): boolean\n whilePressed(): void\n}\n\nclass OculusHandModel extends Object3D {\n controller: Object3D\n motionController: XRHandMeshModel | null\n envMap: Texture | null\n mesh: Mesh | null\n xrInputSource: XRInputSource | null\n\n constructor(controller: Object3D, leftModelPath?: string, rightModelPath?: string) {\n super()\n\n this.controller = controller\n this.motionController = null\n this.envMap = null\n\n this.mesh = null\n this.xrInputSource = null\n\n controller.addEventListener('connected', (event) => {\n const xrInputSource = (event as any).data\n\n if (xrInputSource.hand && !this.motionController) {\n this.xrInputSource = xrInputSource\n\n this.motionController = new XRHandMeshModel(\n this,\n controller,\n undefined,\n xrInputSource.handedness,\n xrInputSource.handedness === 'left' ? leftModelPath : rightModelPath,\n )\n }\n })\n\n controller.addEventListener('disconnected', () => {\n this.dispose()\n })\n }\n\n updateMatrixWorld(force?: boolean): void {\n super.updateMatrixWorld(force)\n\n if (this.motionController) {\n this.motionController.updateMesh()\n }\n }\n\n getPointerPosition(): Vector3 | null {\n // @ts-ignore XRController needs to extend Group\n const indexFingerTip = this.controller.joints[POINTING_JOINT]\n if (indexFingerTip) {\n return indexFingerTip.position\n } else {\n return null\n }\n }\n\n intersectBoxObject(boxObject: Object3D): boolean {\n const pointerPosition = this.getPointerPosition()\n if (pointerPosition) {\n const indexSphere = new Sphere(pointerPosition, TOUCH_RADIUS)\n const box = new Box3().setFromObject(boxObject)\n return indexSphere.intersectsBox(box)\n } else {\n return false\n }\n }\n\n checkButton(button: XRButton): void {\n if (this.intersectBoxObject(button)) {\n button.onPress()\n } else {\n button.onClear()\n }\n\n if (button.isPressed()) {\n button.whilePressed()\n }\n }\n\n dispose(): void {\n this.clear()\n this.motionController = null\n }\n}\n\nexport { OculusHandModel }\n"],"names":["Object3D","XRHandMeshModel","Sphere","Box3"],"mappings":";;;;;;;;;;AAGA,MAAM,eAAe;AACrB,MAAM,iBAAiB;AASvB,MAAM,wBAAwBA,MAAAA,SAAS;AAAA,EAOrC,YAAY,YAAsB,eAAwB,gBAAyB;AAC3E;AAPR;AACA;AACA;AACA;AACA;AAKE,SAAK,aAAa;AAClB,SAAK,mBAAmB;AACxB,SAAK,SAAS;AAEd,SAAK,OAAO;AACZ,SAAK,gBAAgB;AAEV,eAAA,iBAAiB,aAAa,CAAC,UAAU;AAClD,YAAM,gBAAiB,MAAc;AAErC,UAAI,cAAc,QAAQ,CAAC,KAAK,kBAAkB;AAChD,aAAK,gBAAgB;AAErB,aAAK,mBAAmB,IAAIC,gBAAA;AAAA,UAC1B;AAAA,UACA;AAAA,UACA;AAAA,UACA,cAAc;AAAA,UACd,cAAc,eAAe,SAAS,gBAAgB;AAAA,QAAA;AAAA,MAE1D;AAAA,IAAA,CACD;AAEU,eAAA,iBAAiB,gBAAgB,MAAM;AAChD,WAAK,QAAQ;AAAA,IAAA,CACd;AAAA,EACH;AAAA,EAEA,kBAAkB,OAAuB;AACvC,UAAM,kBAAkB,KAAK;AAE7B,QAAI,KAAK,kBAAkB;AACzB,WAAK,iBAAiB;IACxB;AAAA,EACF;AAAA,EAEA,qBAAqC;AAEnC,UAAM,iBAAiB,KAAK,WAAW,OAAO,cAAc;AAC5D,QAAI,gBAAgB;AAClB,aAAO,eAAe;AAAA,IAAA,OACjB;AACE,aAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,mBAAmB,WAA8B;AACzC,UAAA,kBAAkB,KAAK;AAC7B,QAAI,iBAAiB;AACnB,YAAM,cAAc,IAAIC,MAAAA,OAAO,iBAAiB,YAAY;AAC5D,YAAM,MAAM,IAAIC,MAAAA,KAAK,EAAE,cAAc,SAAS;AACvC,aAAA,YAAY,cAAc,GAAG;AAAA,IAAA,OAC/B;AACE,aAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,YAAY,QAAwB;AAC9B,QAAA,KAAK,mBAAmB,MAAM,GAAG;AACnC,aAAO,QAAQ;AAAA,IAAA,OACV;AACL,aAAO,QAAQ;AAAA,IACjB;AAEI,QAAA,OAAO,aAAa;AACtB,aAAO,aAAa;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,UAAgB;AACd,SAAK,MAAM;AACX,SAAK,mBAAmB;AAAA,EAC1B;AACF;;"}
+23
View File
@@ -0,0 +1,23 @@
/// <reference types="webxr" />
import { Object3D, Mesh, Texture, Vector3 } from 'three';
import { XRHandMeshModel } from './XRHandMeshModel';
export interface XRButton extends Object3D {
onPress(): void;
onClear(): void;
isPressed(): boolean;
whilePressed(): void;
}
declare class OculusHandModel extends Object3D {
controller: Object3D;
motionController: XRHandMeshModel | null;
envMap: Texture | null;
mesh: Mesh | null;
xrInputSource: XRInputSource | null;
constructor(controller: Object3D, leftModelPath?: string, rightModelPath?: string);
updateMatrixWorld(force?: boolean): void;
getPointerPosition(): Vector3 | null;
intersectBoxObject(boxObject: Object3D): boolean;
checkButton(button: XRButton): void;
dispose(): void;
}
export { OculusHandModel };
+83
View File
@@ -0,0 +1,83 @@
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
import { Object3D, Sphere, Box3 } from "three";
import { XRHandMeshModel } from "./XRHandMeshModel.js";
const TOUCH_RADIUS = 0.01;
const POINTING_JOINT = "index-finger-tip";
class OculusHandModel extends Object3D {
constructor(controller, leftModelPath, rightModelPath) {
super();
__publicField(this, "controller");
__publicField(this, "motionController");
__publicField(this, "envMap");
__publicField(this, "mesh");
__publicField(this, "xrInputSource");
this.controller = controller;
this.motionController = null;
this.envMap = null;
this.mesh = null;
this.xrInputSource = null;
controller.addEventListener("connected", (event) => {
const xrInputSource = event.data;
if (xrInputSource.hand && !this.motionController) {
this.xrInputSource = xrInputSource;
this.motionController = new XRHandMeshModel(
this,
controller,
void 0,
xrInputSource.handedness,
xrInputSource.handedness === "left" ? leftModelPath : rightModelPath
);
}
});
controller.addEventListener("disconnected", () => {
this.dispose();
});
}
updateMatrixWorld(force) {
super.updateMatrixWorld(force);
if (this.motionController) {
this.motionController.updateMesh();
}
}
getPointerPosition() {
const indexFingerTip = this.controller.joints[POINTING_JOINT];
if (indexFingerTip) {
return indexFingerTip.position;
} else {
return null;
}
}
intersectBoxObject(boxObject) {
const pointerPosition = this.getPointerPosition();
if (pointerPosition) {
const indexSphere = new Sphere(pointerPosition, TOUCH_RADIUS);
const box = new Box3().setFromObject(boxObject);
return indexSphere.intersectsBox(box);
} else {
return false;
}
}
checkButton(button) {
if (this.intersectBoxObject(button)) {
button.onPress();
} else {
button.onClear();
}
if (button.isPressed()) {
button.whilePressed();
}
}
dispose() {
this.clear();
this.motionController = null;
}
}
export {
OculusHandModel
};
//# sourceMappingURL=OculusHandModel.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"OculusHandModel.js","sources":["../../src/webxr/OculusHandModel.ts"],"sourcesContent":["import { Object3D, Sphere, Box3, Mesh, Texture, Vector3 } from 'three'\nimport { XRHandMeshModel } from './XRHandMeshModel'\n\nconst TOUCH_RADIUS = 0.01\nconst POINTING_JOINT = 'index-finger-tip'\n\nexport interface XRButton extends Object3D {\n onPress(): void\n onClear(): void\n isPressed(): boolean\n whilePressed(): void\n}\n\nclass OculusHandModel extends Object3D {\n controller: Object3D\n motionController: XRHandMeshModel | null\n envMap: Texture | null\n mesh: Mesh | null\n xrInputSource: XRInputSource | null\n\n constructor(controller: Object3D, leftModelPath?: string, rightModelPath?: string) {\n super()\n\n this.controller = controller\n this.motionController = null\n this.envMap = null\n\n this.mesh = null\n this.xrInputSource = null\n\n controller.addEventListener('connected', (event) => {\n const xrInputSource = (event as any).data\n\n if (xrInputSource.hand && !this.motionController) {\n this.xrInputSource = xrInputSource\n\n this.motionController = new XRHandMeshModel(\n this,\n controller,\n undefined,\n xrInputSource.handedness,\n xrInputSource.handedness === 'left' ? leftModelPath : rightModelPath,\n )\n }\n })\n\n controller.addEventListener('disconnected', () => {\n this.dispose()\n })\n }\n\n updateMatrixWorld(force?: boolean): void {\n super.updateMatrixWorld(force)\n\n if (this.motionController) {\n this.motionController.updateMesh()\n }\n }\n\n getPointerPosition(): Vector3 | null {\n // @ts-ignore XRController needs to extend Group\n const indexFingerTip = this.controller.joints[POINTING_JOINT]\n if (indexFingerTip) {\n return indexFingerTip.position\n } else {\n return null\n }\n }\n\n intersectBoxObject(boxObject: Object3D): boolean {\n const pointerPosition = this.getPointerPosition()\n if (pointerPosition) {\n const indexSphere = new Sphere(pointerPosition, TOUCH_RADIUS)\n const box = new Box3().setFromObject(boxObject)\n return indexSphere.intersectsBox(box)\n } else {\n return false\n }\n }\n\n checkButton(button: XRButton): void {\n if (this.intersectBoxObject(button)) {\n button.onPress()\n } else {\n button.onClear()\n }\n\n if (button.isPressed()) {\n button.whilePressed()\n }\n }\n\n dispose(): void {\n this.clear()\n this.motionController = null\n }\n}\n\nexport { OculusHandModel }\n"],"names":[],"mappings":";;;;;;;;AAGA,MAAM,eAAe;AACrB,MAAM,iBAAiB;AASvB,MAAM,wBAAwB,SAAS;AAAA,EAOrC,YAAY,YAAsB,eAAwB,gBAAyB;AAC3E;AAPR;AACA;AACA;AACA;AACA;AAKE,SAAK,aAAa;AAClB,SAAK,mBAAmB;AACxB,SAAK,SAAS;AAEd,SAAK,OAAO;AACZ,SAAK,gBAAgB;AAEV,eAAA,iBAAiB,aAAa,CAAC,UAAU;AAClD,YAAM,gBAAiB,MAAc;AAErC,UAAI,cAAc,QAAQ,CAAC,KAAK,kBAAkB;AAChD,aAAK,gBAAgB;AAErB,aAAK,mBAAmB,IAAI;AAAA,UAC1B;AAAA,UACA;AAAA,UACA;AAAA,UACA,cAAc;AAAA,UACd,cAAc,eAAe,SAAS,gBAAgB;AAAA,QAAA;AAAA,MAE1D;AAAA,IAAA,CACD;AAEU,eAAA,iBAAiB,gBAAgB,MAAM;AAChD,WAAK,QAAQ;AAAA,IAAA,CACd;AAAA,EACH;AAAA,EAEA,kBAAkB,OAAuB;AACvC,UAAM,kBAAkB,KAAK;AAE7B,QAAI,KAAK,kBAAkB;AACzB,WAAK,iBAAiB;IACxB;AAAA,EACF;AAAA,EAEA,qBAAqC;AAEnC,UAAM,iBAAiB,KAAK,WAAW,OAAO,cAAc;AAC5D,QAAI,gBAAgB;AAClB,aAAO,eAAe;AAAA,IAAA,OACjB;AACE,aAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,mBAAmB,WAA8B;AACzC,UAAA,kBAAkB,KAAK;AAC7B,QAAI,iBAAiB;AACnB,YAAM,cAAc,IAAI,OAAO,iBAAiB,YAAY;AAC5D,YAAM,MAAM,IAAI,KAAK,EAAE,cAAc,SAAS;AACvC,aAAA,YAAY,cAAc,GAAG;AAAA,IAAA,OAC/B;AACE,aAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,YAAY,QAAwB;AAC9B,QAAA,KAAK,mBAAmB,MAAM,GAAG;AACnC,aAAO,QAAQ;AAAA,IAAA,OACV;AACL,aAAO,QAAQ;AAAA,IACjB;AAEI,QAAA,OAAO,aAAa;AACtB,aAAO,aAAa;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,UAAgB;AACd,SAAK,MAAM;AACX,SAAK,mBAAmB;AAAA,EAC1B;AACF;"}
+242
View File
@@ -0,0 +1,242 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const THREE = require("three");
function _interopNamespaceDefault(e) {
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
if (e) {
for (const k in e) {
if (k !== "default") {
const d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: () => e[k]
});
}
}
}
n.default = e;
return Object.freeze(n);
}
const THREE__namespace = /* @__PURE__ */ _interopNamespaceDefault(THREE);
const PINCH_MAX = 0.05;
const PINCH_THRESHOLD = 0.02;
const PINCH_MIN = 0.01;
const POINTER_ADVANCE_MAX = 0.02;
const POINTER_OPACITY_MAX = 1;
const POINTER_OPACITY_MIN = 0.4;
const POINTER_FRONT_RADIUS = 2e-3;
const POINTER_REAR_RADIUS = 0.01;
const POINTER_REAR_RADIUS_MIN = 3e-3;
const POINTER_LENGTH = 0.035;
const POINTER_SEGMENTS = 16;
const POINTER_RINGS = 12;
const POINTER_HEMISPHERE_ANGLE = 110;
const YAXIS = /* @__PURE__ */ new THREE__namespace.Vector3(0, 1, 0);
const ZAXIS = /* @__PURE__ */ new THREE__namespace.Vector3(0, 0, 1);
const CURSOR_RADIUS = 0.02;
const CURSOR_MAX_DISTANCE = 1.5;
class OculusHandPointerModel extends THREE__namespace.Object3D {
constructor(hand, controller) {
super();
this.hand = hand;
this.controller = controller;
this.motionController = null;
this.envMap = null;
this.mesh = null;
this.pointerGeometry = null;
this.pointerMesh = null;
this.pointerObject = null;
this.pinched = false;
this.attached = false;
this.cursorObject = null;
this.raycaster = null;
this._onConnected = this._onConnected.bind(this);
this._onDisconnected = this._onDisconnected.bind(this);
this.hand.addEventListener("connected", this._onConnected);
this.hand.addEventListener("disconnected", this._onDisconnected);
}
_onConnected(event) {
const xrInputSource = event.data;
if (xrInputSource.hand) {
this.visible = true;
this.xrInputSource = xrInputSource;
this.createPointer();
}
}
_onDisconnected() {
var _a, _b;
this.visible = false;
this.xrInputSource = null;
(_a = this.pointerGeometry) == null ? void 0 : _a.dispose();
(_b = this.pointerMesh) == null ? void 0 : _b.material.dispose();
this.clear();
}
_drawVerticesRing(vertices, baseVector, ringIndex) {
const segmentVector = baseVector.clone();
for (var i = 0; i < POINTER_SEGMENTS; i++) {
segmentVector.applyAxisAngle(ZAXIS, Math.PI * 2 / POINTER_SEGMENTS);
const vid = ringIndex * POINTER_SEGMENTS + i;
vertices[3 * vid] = segmentVector.x;
vertices[3 * vid + 1] = segmentVector.y;
vertices[3 * vid + 2] = segmentVector.z;
}
}
_updatePointerVertices(rearRadius) {
const vertices = this.pointerGeometry.attributes.position.array;
const frontFaceBase = new THREE__namespace.Vector3(POINTER_FRONT_RADIUS, 0, -1 * (POINTER_LENGTH - rearRadius));
this._drawVerticesRing(vertices, frontFaceBase, 0);
const rearBase = new THREE__namespace.Vector3(
Math.sin(Math.PI * POINTER_HEMISPHERE_ANGLE / 180) * rearRadius,
Math.cos(Math.PI * POINTER_HEMISPHERE_ANGLE / 180) * rearRadius,
0
);
for (var i = 0; i < POINTER_RINGS; i++) {
this._drawVerticesRing(vertices, rearBase, i + 1);
rearBase.applyAxisAngle(YAXIS, Math.PI * POINTER_HEMISPHERE_ANGLE / 180 / (POINTER_RINGS * -2));
}
const frontCenterIndex = POINTER_SEGMENTS * (1 + POINTER_RINGS);
const rearCenterIndex = POINTER_SEGMENTS * (1 + POINTER_RINGS) + 1;
const frontCenter = new THREE__namespace.Vector3(0, 0, -1 * (POINTER_LENGTH - rearRadius));
vertices[frontCenterIndex * 3] = frontCenter.x;
vertices[frontCenterIndex * 3 + 1] = frontCenter.y;
vertices[frontCenterIndex * 3 + 2] = frontCenter.z;
const rearCenter = new THREE__namespace.Vector3(0, 0, rearRadius);
vertices[rearCenterIndex * 3] = rearCenter.x;
vertices[rearCenterIndex * 3 + 1] = rearCenter.y;
vertices[rearCenterIndex * 3 + 2] = rearCenter.z;
this.pointerGeometry.setAttribute("position", new THREE__namespace.Float32BufferAttribute(vertices, 3));
}
createPointer() {
var i, j;
const vertices = new Array(((POINTER_RINGS + 1) * POINTER_SEGMENTS + 2) * 3).fill(0);
const indices = [];
this.pointerGeometry = new THREE__namespace.BufferGeometry();
this.pointerGeometry.setAttribute("position", new THREE__namespace.Float32BufferAttribute(vertices, 3));
this._updatePointerVertices(POINTER_REAR_RADIUS);
for (i = 0; i < POINTER_RINGS; i++) {
for (j = 0; j < POINTER_SEGMENTS - 1; j++) {
indices.push(i * POINTER_SEGMENTS + j, i * POINTER_SEGMENTS + j + 1, (i + 1) * POINTER_SEGMENTS + j);
indices.push(i * POINTER_SEGMENTS + j + 1, (i + 1) * POINTER_SEGMENTS + j + 1, (i + 1) * POINTER_SEGMENTS + j);
}
indices.push((i + 1) * POINTER_SEGMENTS - 1, i * POINTER_SEGMENTS, (i + 2) * POINTER_SEGMENTS - 1);
indices.push(i * POINTER_SEGMENTS, (i + 1) * POINTER_SEGMENTS, (i + 2) * POINTER_SEGMENTS - 1);
}
const frontCenterIndex = POINTER_SEGMENTS * (1 + POINTER_RINGS);
const rearCenterIndex = POINTER_SEGMENTS * (1 + POINTER_RINGS) + 1;
for (i = 0; i < POINTER_SEGMENTS - 1; i++) {
indices.push(frontCenterIndex, i + 1, i);
indices.push(rearCenterIndex, i + POINTER_SEGMENTS * POINTER_RINGS, i + POINTER_SEGMENTS * POINTER_RINGS + 1);
}
indices.push(frontCenterIndex, 0, POINTER_SEGMENTS - 1);
indices.push(rearCenterIndex, POINTER_SEGMENTS * (POINTER_RINGS + 1) - 1, POINTER_SEGMENTS * POINTER_RINGS);
const material = new THREE__namespace.MeshBasicMaterial();
material.transparent = true;
material.opacity = POINTER_OPACITY_MIN;
this.pointerGeometry.setIndex(indices);
this.pointerMesh = new THREE__namespace.Mesh(this.pointerGeometry, material);
this.pointerMesh.position.set(0, 0, -1 * POINTER_REAR_RADIUS);
this.pointerObject = new THREE__namespace.Object3D();
this.pointerObject.add(this.pointerMesh);
this.raycaster = new THREE__namespace.Raycaster();
const cursorGeometry = new THREE__namespace.SphereGeometry(CURSOR_RADIUS, 10, 10);
const cursorMaterial = new THREE__namespace.MeshBasicMaterial();
cursorMaterial.transparent = true;
cursorMaterial.opacity = POINTER_OPACITY_MIN;
this.cursorObject = new THREE__namespace.Mesh(cursorGeometry, cursorMaterial);
this.pointerObject.add(this.cursorObject);
this.add(this.pointerObject);
}
_updateRaycaster() {
if (this.raycaster) {
const pointerMatrix = this.pointerObject.matrixWorld;
const tempMatrix = new THREE__namespace.Matrix4();
tempMatrix.identity().extractRotation(pointerMatrix);
this.raycaster.ray.origin.setFromMatrixPosition(pointerMatrix);
this.raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix);
}
}
_updatePointer() {
this.pointerObject.visible = this.controller.visible;
const indexTip = this.hand.joints["index-finger-tip"];
const thumbTip = this.hand.joints["thumb-tip"];
const distance = indexTip.position.distanceTo(thumbTip.position);
const position = indexTip.position.clone().add(thumbTip.position).multiplyScalar(0.5);
this.pointerObject.position.copy(position);
this.pointerObject.quaternion.copy(this.controller.quaternion);
this.pinched = distance <= PINCH_THRESHOLD;
const pinchScale = (distance - PINCH_MIN) / (PINCH_MAX - PINCH_MIN);
const focusScale = (distance - PINCH_MIN) / (PINCH_THRESHOLD - PINCH_MIN);
if (pinchScale > 1) {
this._updatePointerVertices(POINTER_REAR_RADIUS);
this.pointerMesh.position.set(0, 0, -1 * POINTER_REAR_RADIUS);
this.pointerMesh.material.opacity = POINTER_OPACITY_MIN;
} else if (pinchScale > 0) {
const rearRadius = (POINTER_REAR_RADIUS - POINTER_REAR_RADIUS_MIN) * pinchScale + POINTER_REAR_RADIUS_MIN;
this._updatePointerVertices(rearRadius);
if (focusScale < 1) {
this.pointerMesh.position.set(0, 0, -1 * rearRadius - (1 - focusScale) * POINTER_ADVANCE_MAX);
this.pointerMesh.material.opacity = POINTER_OPACITY_MIN + (1 - focusScale) * (POINTER_OPACITY_MAX - POINTER_OPACITY_MIN);
} else {
this.pointerMesh.position.set(0, 0, -1 * rearRadius);
this.pointerMesh.material.opacity = POINTER_OPACITY_MIN;
}
} else {
this._updatePointerVertices(POINTER_REAR_RADIUS_MIN);
this.pointerMesh.position.set(0, 0, -1 * POINTER_REAR_RADIUS_MIN - POINTER_ADVANCE_MAX);
this.pointerMesh.material.opacity = POINTER_OPACITY_MAX;
}
this.cursorObject.material.opacity = this.pointerMesh.material.opacity;
}
updateMatrixWorld(force) {
super.updateMatrixWorld(force);
if (this.pointerGeometry) {
this._updatePointer();
this._updateRaycaster();
}
}
isPinched() {
return this.pinched;
}
setAttached(attached) {
this.attached = attached;
}
isAttached() {
return this.attached;
}
intersectObject(object, recursive = true) {
if (this.raycaster) {
return this.raycaster.intersectObject(object, recursive);
}
}
intersectObjects(objects, recursive = true) {
if (this.raycaster) {
return this.raycaster.intersectObjects(objects, recursive);
}
}
checkIntersections(objects, recursive = false) {
if (this.raycaster && !this.attached) {
const intersections = this.raycaster.intersectObjects(objects, recursive);
const direction = new THREE__namespace.Vector3(0, 0, -1);
if (intersections.length > 0) {
const intersection = intersections[0];
const distance = intersection.distance;
this.cursorObject.position.copy(direction.multiplyScalar(distance));
} else {
this.cursorObject.position.copy(direction.multiplyScalar(CURSOR_MAX_DISTANCE));
}
}
}
setCursor(distance) {
const direction = new THREE__namespace.Vector3(0, 0, -1);
if (this.raycaster && !this.attached) {
this.cursorObject.position.copy(direction.multiplyScalar(distance));
}
}
dispose() {
this._onDisconnected();
this.hand.removeEventListener("connected", this._onConnected);
this.hand.removeEventListener("disconnected", this._onDisconnected);
}
}
exports.OculusHandPointerModel = OculusHandPointerModel;
//# sourceMappingURL=OculusHandPointerModel.cjs.map
File diff suppressed because one or more lines are too long
+65
View File
@@ -0,0 +1,65 @@
import {
BufferGeometry,
Intersection,
Mesh,
MeshBasicMaterial,
Object3D,
Raycaster,
SphereGeometry,
Texture,
Vector3,
} from 'three'
export class OculusHandPointerModel extends Object3D {
hand: Object3D
controller: Object3D
motionController: Object3D | null
envMap: Texture | null
mesh: Mesh | null
pointerGeometry: BufferGeometry | null
pointerMesh: Mesh<BufferGeometry, MeshBasicMaterial> | null
pointerObject: Object3D | null
pinched: boolean
attached: boolean
cursorObject: Mesh<SphereGeometry, MeshBasicMaterial> | null
raycaster: Raycaster
visible: boolean
xrInputSource: unknown
constructor(hand: Object3D, controller: Object3D)
private _drawVerticesRing(vertices: number[], baseVector: Vector3, ringIndex: number): void
private _updatePointerVertices(rearRadius: number): void
public createPointer(): void
private _updateRaycaster(): void
private _updatePointer(): void
public updateMatrixWorld(force?: boolean): void
public isPinched(): boolean
public setAttached(attached: boolean): void
public isAttached(): boolean
public intersectObject(object: Object3D, recursive?: boolean): Intersection[] | void
public intersectObjects(objects: Object3D[], recursive?: boolean): Intersection[] | void
public checkIntersections(objects: Object3D[], recursive?: boolean): void
public setCursor(distance: number): void
public dispose(): void
}
+225
View File
@@ -0,0 +1,225 @@
import * as THREE from "three";
const PINCH_MAX = 0.05;
const PINCH_THRESHOLD = 0.02;
const PINCH_MIN = 0.01;
const POINTER_ADVANCE_MAX = 0.02;
const POINTER_OPACITY_MAX = 1;
const POINTER_OPACITY_MIN = 0.4;
const POINTER_FRONT_RADIUS = 2e-3;
const POINTER_REAR_RADIUS = 0.01;
const POINTER_REAR_RADIUS_MIN = 3e-3;
const POINTER_LENGTH = 0.035;
const POINTER_SEGMENTS = 16;
const POINTER_RINGS = 12;
const POINTER_HEMISPHERE_ANGLE = 110;
const YAXIS = /* @__PURE__ */ new THREE.Vector3(0, 1, 0);
const ZAXIS = /* @__PURE__ */ new THREE.Vector3(0, 0, 1);
const CURSOR_RADIUS = 0.02;
const CURSOR_MAX_DISTANCE = 1.5;
class OculusHandPointerModel extends THREE.Object3D {
constructor(hand, controller) {
super();
this.hand = hand;
this.controller = controller;
this.motionController = null;
this.envMap = null;
this.mesh = null;
this.pointerGeometry = null;
this.pointerMesh = null;
this.pointerObject = null;
this.pinched = false;
this.attached = false;
this.cursorObject = null;
this.raycaster = null;
this._onConnected = this._onConnected.bind(this);
this._onDisconnected = this._onDisconnected.bind(this);
this.hand.addEventListener("connected", this._onConnected);
this.hand.addEventListener("disconnected", this._onDisconnected);
}
_onConnected(event) {
const xrInputSource = event.data;
if (xrInputSource.hand) {
this.visible = true;
this.xrInputSource = xrInputSource;
this.createPointer();
}
}
_onDisconnected() {
var _a, _b;
this.visible = false;
this.xrInputSource = null;
(_a = this.pointerGeometry) == null ? void 0 : _a.dispose();
(_b = this.pointerMesh) == null ? void 0 : _b.material.dispose();
this.clear();
}
_drawVerticesRing(vertices, baseVector, ringIndex) {
const segmentVector = baseVector.clone();
for (var i = 0; i < POINTER_SEGMENTS; i++) {
segmentVector.applyAxisAngle(ZAXIS, Math.PI * 2 / POINTER_SEGMENTS);
const vid = ringIndex * POINTER_SEGMENTS + i;
vertices[3 * vid] = segmentVector.x;
vertices[3 * vid + 1] = segmentVector.y;
vertices[3 * vid + 2] = segmentVector.z;
}
}
_updatePointerVertices(rearRadius) {
const vertices = this.pointerGeometry.attributes.position.array;
const frontFaceBase = new THREE.Vector3(POINTER_FRONT_RADIUS, 0, -1 * (POINTER_LENGTH - rearRadius));
this._drawVerticesRing(vertices, frontFaceBase, 0);
const rearBase = new THREE.Vector3(
Math.sin(Math.PI * POINTER_HEMISPHERE_ANGLE / 180) * rearRadius,
Math.cos(Math.PI * POINTER_HEMISPHERE_ANGLE / 180) * rearRadius,
0
);
for (var i = 0; i < POINTER_RINGS; i++) {
this._drawVerticesRing(vertices, rearBase, i + 1);
rearBase.applyAxisAngle(YAXIS, Math.PI * POINTER_HEMISPHERE_ANGLE / 180 / (POINTER_RINGS * -2));
}
const frontCenterIndex = POINTER_SEGMENTS * (1 + POINTER_RINGS);
const rearCenterIndex = POINTER_SEGMENTS * (1 + POINTER_RINGS) + 1;
const frontCenter = new THREE.Vector3(0, 0, -1 * (POINTER_LENGTH - rearRadius));
vertices[frontCenterIndex * 3] = frontCenter.x;
vertices[frontCenterIndex * 3 + 1] = frontCenter.y;
vertices[frontCenterIndex * 3 + 2] = frontCenter.z;
const rearCenter = new THREE.Vector3(0, 0, rearRadius);
vertices[rearCenterIndex * 3] = rearCenter.x;
vertices[rearCenterIndex * 3 + 1] = rearCenter.y;
vertices[rearCenterIndex * 3 + 2] = rearCenter.z;
this.pointerGeometry.setAttribute("position", new THREE.Float32BufferAttribute(vertices, 3));
}
createPointer() {
var i, j;
const vertices = new Array(((POINTER_RINGS + 1) * POINTER_SEGMENTS + 2) * 3).fill(0);
const indices = [];
this.pointerGeometry = new THREE.BufferGeometry();
this.pointerGeometry.setAttribute("position", new THREE.Float32BufferAttribute(vertices, 3));
this._updatePointerVertices(POINTER_REAR_RADIUS);
for (i = 0; i < POINTER_RINGS; i++) {
for (j = 0; j < POINTER_SEGMENTS - 1; j++) {
indices.push(i * POINTER_SEGMENTS + j, i * POINTER_SEGMENTS + j + 1, (i + 1) * POINTER_SEGMENTS + j);
indices.push(i * POINTER_SEGMENTS + j + 1, (i + 1) * POINTER_SEGMENTS + j + 1, (i + 1) * POINTER_SEGMENTS + j);
}
indices.push((i + 1) * POINTER_SEGMENTS - 1, i * POINTER_SEGMENTS, (i + 2) * POINTER_SEGMENTS - 1);
indices.push(i * POINTER_SEGMENTS, (i + 1) * POINTER_SEGMENTS, (i + 2) * POINTER_SEGMENTS - 1);
}
const frontCenterIndex = POINTER_SEGMENTS * (1 + POINTER_RINGS);
const rearCenterIndex = POINTER_SEGMENTS * (1 + POINTER_RINGS) + 1;
for (i = 0; i < POINTER_SEGMENTS - 1; i++) {
indices.push(frontCenterIndex, i + 1, i);
indices.push(rearCenterIndex, i + POINTER_SEGMENTS * POINTER_RINGS, i + POINTER_SEGMENTS * POINTER_RINGS + 1);
}
indices.push(frontCenterIndex, 0, POINTER_SEGMENTS - 1);
indices.push(rearCenterIndex, POINTER_SEGMENTS * (POINTER_RINGS + 1) - 1, POINTER_SEGMENTS * POINTER_RINGS);
const material = new THREE.MeshBasicMaterial();
material.transparent = true;
material.opacity = POINTER_OPACITY_MIN;
this.pointerGeometry.setIndex(indices);
this.pointerMesh = new THREE.Mesh(this.pointerGeometry, material);
this.pointerMesh.position.set(0, 0, -1 * POINTER_REAR_RADIUS);
this.pointerObject = new THREE.Object3D();
this.pointerObject.add(this.pointerMesh);
this.raycaster = new THREE.Raycaster();
const cursorGeometry = new THREE.SphereGeometry(CURSOR_RADIUS, 10, 10);
const cursorMaterial = new THREE.MeshBasicMaterial();
cursorMaterial.transparent = true;
cursorMaterial.opacity = POINTER_OPACITY_MIN;
this.cursorObject = new THREE.Mesh(cursorGeometry, cursorMaterial);
this.pointerObject.add(this.cursorObject);
this.add(this.pointerObject);
}
_updateRaycaster() {
if (this.raycaster) {
const pointerMatrix = this.pointerObject.matrixWorld;
const tempMatrix = new THREE.Matrix4();
tempMatrix.identity().extractRotation(pointerMatrix);
this.raycaster.ray.origin.setFromMatrixPosition(pointerMatrix);
this.raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix);
}
}
_updatePointer() {
this.pointerObject.visible = this.controller.visible;
const indexTip = this.hand.joints["index-finger-tip"];
const thumbTip = this.hand.joints["thumb-tip"];
const distance = indexTip.position.distanceTo(thumbTip.position);
const position = indexTip.position.clone().add(thumbTip.position).multiplyScalar(0.5);
this.pointerObject.position.copy(position);
this.pointerObject.quaternion.copy(this.controller.quaternion);
this.pinched = distance <= PINCH_THRESHOLD;
const pinchScale = (distance - PINCH_MIN) / (PINCH_MAX - PINCH_MIN);
const focusScale = (distance - PINCH_MIN) / (PINCH_THRESHOLD - PINCH_MIN);
if (pinchScale > 1) {
this._updatePointerVertices(POINTER_REAR_RADIUS);
this.pointerMesh.position.set(0, 0, -1 * POINTER_REAR_RADIUS);
this.pointerMesh.material.opacity = POINTER_OPACITY_MIN;
} else if (pinchScale > 0) {
const rearRadius = (POINTER_REAR_RADIUS - POINTER_REAR_RADIUS_MIN) * pinchScale + POINTER_REAR_RADIUS_MIN;
this._updatePointerVertices(rearRadius);
if (focusScale < 1) {
this.pointerMesh.position.set(0, 0, -1 * rearRadius - (1 - focusScale) * POINTER_ADVANCE_MAX);
this.pointerMesh.material.opacity = POINTER_OPACITY_MIN + (1 - focusScale) * (POINTER_OPACITY_MAX - POINTER_OPACITY_MIN);
} else {
this.pointerMesh.position.set(0, 0, -1 * rearRadius);
this.pointerMesh.material.opacity = POINTER_OPACITY_MIN;
}
} else {
this._updatePointerVertices(POINTER_REAR_RADIUS_MIN);
this.pointerMesh.position.set(0, 0, -1 * POINTER_REAR_RADIUS_MIN - POINTER_ADVANCE_MAX);
this.pointerMesh.material.opacity = POINTER_OPACITY_MAX;
}
this.cursorObject.material.opacity = this.pointerMesh.material.opacity;
}
updateMatrixWorld(force) {
super.updateMatrixWorld(force);
if (this.pointerGeometry) {
this._updatePointer();
this._updateRaycaster();
}
}
isPinched() {
return this.pinched;
}
setAttached(attached) {
this.attached = attached;
}
isAttached() {
return this.attached;
}
intersectObject(object, recursive = true) {
if (this.raycaster) {
return this.raycaster.intersectObject(object, recursive);
}
}
intersectObjects(objects, recursive = true) {
if (this.raycaster) {
return this.raycaster.intersectObjects(objects, recursive);
}
}
checkIntersections(objects, recursive = false) {
if (this.raycaster && !this.attached) {
const intersections = this.raycaster.intersectObjects(objects, recursive);
const direction = new THREE.Vector3(0, 0, -1);
if (intersections.length > 0) {
const intersection = intersections[0];
const distance = intersection.distance;
this.cursorObject.position.copy(direction.multiplyScalar(distance));
} else {
this.cursorObject.position.copy(direction.multiplyScalar(CURSOR_MAX_DISTANCE));
}
}
}
setCursor(distance) {
const direction = new THREE.Vector3(0, 0, -1);
if (this.raycaster && !this.attached) {
this.cursorObject.position.copy(direction.multiplyScalar(distance));
}
}
dispose() {
this._onDisconnected();
this.hand.removeEventListener("connected", this._onConnected);
this.hand.removeEventListener("disconnected", this._onDisconnected);
}
}
export {
OculusHandPointerModel
};
//# sourceMappingURL=OculusHandPointerModel.js.map
File diff suppressed because one or more lines are too long
+49
View File
@@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const THREE = require("three");
function _interopNamespaceDefault(e) {
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
if (e) {
for (const k in e) {
if (k !== "default") {
const d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: () => e[k]
});
}
}
}
n.default = e;
return Object.freeze(n);
}
const THREE__namespace = /* @__PURE__ */ _interopNamespaceDefault(THREE);
function createText(message, height) {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
let metrics = null;
const textHeight = 100;
context.font = "normal " + textHeight + "px Arial";
metrics = context.measureText(message);
const textWidth = metrics.width;
canvas.width = textWidth;
canvas.height = textHeight;
context.font = "normal " + textHeight + "px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = "#ffffff";
context.fillText(message, textWidth / 2, textHeight / 2);
const texture = new THREE__namespace.Texture(canvas);
texture.needsUpdate = true;
const material = new THREE__namespace.MeshBasicMaterial({
color: 16777215,
side: THREE__namespace.DoubleSide,
map: texture,
transparent: true
});
const geometry = new THREE__namespace.PlaneGeometry(height * textWidth / textHeight, height);
const plane = new THREE__namespace.Mesh(geometry, material);
return plane;
}
exports.createText = createText;
//# sourceMappingURL=Text2D.cjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Text2D.cjs","sources":["../../src/webxr/Text2D.js"],"sourcesContent":["import * as THREE from 'three'\n\nfunction createText(message, height) {\n const canvas = document.createElement('canvas')\n const context = canvas.getContext('2d')\n let metrics = null\n const textHeight = 100\n context.font = 'normal ' + textHeight + 'px Arial'\n metrics = context.measureText(message)\n const textWidth = metrics.width\n canvas.width = textWidth\n canvas.height = textHeight\n context.font = 'normal ' + textHeight + 'px Arial'\n context.textAlign = 'center'\n context.textBaseline = 'middle'\n context.fillStyle = '#ffffff'\n context.fillText(message, textWidth / 2, textHeight / 2)\n\n const texture = new THREE.Texture(canvas)\n texture.needsUpdate = true\n //var spriteAlignment = new THREE.Vector2(0,0) ;\n const material = new THREE.MeshBasicMaterial({\n color: 0xffffff,\n side: THREE.DoubleSide,\n map: texture,\n transparent: true,\n })\n const geometry = new THREE.PlaneGeometry((height * textWidth) / textHeight, height)\n const plane = new THREE.Mesh(geometry, material)\n return plane\n}\n\nexport { createText }\n"],"names":["THREE"],"mappings":";;;;;;;;;;;;;;;;;;;;AAEA,SAAS,WAAW,SAAS,QAAQ;AACnC,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,QAAM,UAAU,OAAO,WAAW,IAAI;AACtC,MAAI,UAAU;AACd,QAAM,aAAa;AACnB,UAAQ,OAAO,YAAY,aAAa;AACxC,YAAU,QAAQ,YAAY,OAAO;AACrC,QAAM,YAAY,QAAQ;AAC1B,SAAO,QAAQ;AACf,SAAO,SAAS;AAChB,UAAQ,OAAO,YAAY,aAAa;AACxC,UAAQ,YAAY;AACpB,UAAQ,eAAe;AACvB,UAAQ,YAAY;AACpB,UAAQ,SAAS,SAAS,YAAY,GAAG,aAAa,CAAC;AAEvD,QAAM,UAAU,IAAIA,iBAAM,QAAQ,MAAM;AACxC,UAAQ,cAAc;AAEtB,QAAM,WAAW,IAAIA,iBAAM,kBAAkB;AAAA,IAC3C,OAAO;AAAA,IACP,MAAMA,iBAAM;AAAA,IACZ,KAAK;AAAA,IACL,aAAa;AAAA,EACjB,CAAG;AACD,QAAM,WAAW,IAAIA,iBAAM,cAAe,SAAS,YAAa,YAAY,MAAM;AAClF,QAAM,QAAQ,IAAIA,iBAAM,KAAK,UAAU,QAAQ;AAC/C,SAAO;AACT;;"}
+3
View File
@@ -0,0 +1,3 @@
import { Mesh } from 'three'
export function createText(message: string, height: number): Mesh
+32
View File
@@ -0,0 +1,32 @@
import * as THREE from "three";
function createText(message, height) {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
let metrics = null;
const textHeight = 100;
context.font = "normal " + textHeight + "px Arial";
metrics = context.measureText(message);
const textWidth = metrics.width;
canvas.width = textWidth;
canvas.height = textHeight;
context.font = "normal " + textHeight + "px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = "#ffffff";
context.fillText(message, textWidth / 2, textHeight / 2);
const texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
const material = new THREE.MeshBasicMaterial({
color: 16777215,
side: THREE.DoubleSide,
map: texture,
transparent: true
});
const geometry = new THREE.PlaneGeometry(height * textWidth / textHeight, height);
const plane = new THREE.Mesh(geometry, material);
return plane;
}
export {
createText
};
//# sourceMappingURL=Text2D.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Text2D.js","sources":["../../src/webxr/Text2D.js"],"sourcesContent":["import * as THREE from 'three'\n\nfunction createText(message, height) {\n const canvas = document.createElement('canvas')\n const context = canvas.getContext('2d')\n let metrics = null\n const textHeight = 100\n context.font = 'normal ' + textHeight + 'px Arial'\n metrics = context.measureText(message)\n const textWidth = metrics.width\n canvas.width = textWidth\n canvas.height = textHeight\n context.font = 'normal ' + textHeight + 'px Arial'\n context.textAlign = 'center'\n context.textBaseline = 'middle'\n context.fillStyle = '#ffffff'\n context.fillText(message, textWidth / 2, textHeight / 2)\n\n const texture = new THREE.Texture(canvas)\n texture.needsUpdate = true\n //var spriteAlignment = new THREE.Vector2(0,0) ;\n const material = new THREE.MeshBasicMaterial({\n color: 0xffffff,\n side: THREE.DoubleSide,\n map: texture,\n transparent: true,\n })\n const geometry = new THREE.PlaneGeometry((height * textWidth) / textHeight, height)\n const plane = new THREE.Mesh(geometry, material)\n return plane\n}\n\nexport { createText }\n"],"names":[],"mappings":";AAEA,SAAS,WAAW,SAAS,QAAQ;AACnC,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,QAAM,UAAU,OAAO,WAAW,IAAI;AACtC,MAAI,UAAU;AACd,QAAM,aAAa;AACnB,UAAQ,OAAO,YAAY,aAAa;AACxC,YAAU,QAAQ,YAAY,OAAO;AACrC,QAAM,YAAY,QAAQ;AAC1B,SAAO,QAAQ;AACf,SAAO,SAAS;AAChB,UAAQ,OAAO,YAAY,aAAa;AACxC,UAAQ,YAAY;AACpB,UAAQ,eAAe;AACvB,UAAQ,YAAY;AACpB,UAAQ,SAAS,SAAS,YAAY,GAAG,aAAa,CAAC;AAEvD,QAAM,UAAU,IAAI,MAAM,QAAQ,MAAM;AACxC,UAAQ,cAAc;AAEtB,QAAM,WAAW,IAAI,MAAM,kBAAkB;AAAA,IAC3C,OAAO;AAAA,IACP,MAAM,MAAM;AAAA,IACZ,KAAK;AAAA,IACL,aAAa;AAAA,EACjB,CAAG;AACD,QAAM,WAAW,IAAI,MAAM,cAAe,SAAS,YAAa,YAAY,MAAM;AAClF,QAAM,QAAQ,IAAI,MAAM,KAAK,UAAU,QAAQ;AAC/C,SAAO;AACT;"}
+115
View File
@@ -0,0 +1,115 @@
"use strict";
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const VRButton = /* @__PURE__ */ (() => {
const _VRButton = class {
static createButton(renderer, sessionInit = {}) {
const button = document.createElement("button");
function showEnterVR() {
let currentSession = null;
async function onSessionStarted(session) {
session.addEventListener("end", onSessionEnded);
await renderer.xr.setSession(session);
button.textContent = "EXIT VR";
currentSession = session;
}
function onSessionEnded() {
currentSession.removeEventListener("end", onSessionEnded);
button.textContent = "ENTER VR";
currentSession = null;
}
button.style.display = "";
button.style.cursor = "pointer";
button.style.left = "calc(50% - 50px)";
button.style.width = "100px";
button.textContent = "ENTER VR";
button.onmouseenter = () => {
button.style.opacity = "1.0";
};
button.onmouseleave = () => {
button.style.opacity = "0.5";
};
button.onclick = () => {
var _a;
if (currentSession === null) {
const optionalFeatures = [sessionInit.optionalFeatures, "local-floor", "bounded-floor", "hand-tracking"].flat().filter(Boolean);
(_a = navigator.xr) == null ? void 0 : _a.requestSession("immersive-vr", { ...sessionInit, optionalFeatures }).then(onSessionStarted);
} else {
currentSession.end();
}
};
}
function disableButton() {
button.style.display = "";
button.style.cursor = "auto";
button.style.left = "calc(50% - 75px)";
button.style.width = "150px";
button.onmouseenter = null;
button.onmouseleave = null;
button.onclick = null;
}
function showWebXRNotFound() {
disableButton();
button.textContent = "VR NOT SUPPORTED";
}
function stylizeElement(element) {
element.style.position = "absolute";
element.style.bottom = "20px";
element.style.padding = "12px 6px";
element.style.border = "1px solid #fff";
element.style.borderRadius = "4px";
element.style.background = "rgba(0,0,0,0.1)";
element.style.color = "#fff";
element.style.font = "normal 13px sans-serif";
element.style.textAlign = "center";
element.style.opacity = "0.5";
element.style.outline = "none";
element.style.zIndex = "999";
}
if ("xr" in navigator) {
stylizeElement(button);
button.id = "VRButton";
button.style.display = "none";
navigator.xr.isSessionSupported("immersive-vr").then((supported) => {
supported ? showEnterVR() : showWebXRNotFound();
if (supported && _VRButton.xrSessionIsGranted) {
button.click();
}
});
return button;
} else {
const message = document.createElement("a");
if (window.isSecureContext === false) {
message.href = document.location.href.replace(/^http:/, "https:");
message.innerHTML = "WEBXR NEEDS HTTPS";
} else {
message.href = "https://immersiveweb.dev/";
message.innerHTML = "WEBXR NOT AVAILABLE";
}
message.style.left = "calc(50% - 90px)";
message.style.width = "180px";
message.style.textDecoration = "none";
stylizeElement(message);
return message;
}
}
static registerSessionGrantedListener() {
if (typeof navigator !== "undefined" && "xr" in navigator) {
navigator.xr.addEventListener("sessiongranted", () => {
_VRButton.xrSessionIsGranted = true;
});
}
}
};
let VRButton2 = _VRButton;
__publicField(VRButton2, "xrSessionIsGranted", false);
VRButton2.registerSessionGrantedListener();
return VRButton2;
})();
exports.VRButton = VRButton;
//# sourceMappingURL=VRButton.cjs.map
File diff suppressed because one or more lines are too long
+11
View File
@@ -0,0 +1,11 @@
import { WebGLRenderer } from 'three'
declare class VRButton {
static createButton(renderer: WebGLRenderer, sessionInit?: XRSessionInit): HTMLButtonElement | HTMLAnchorElement
static xrSessionIsGranted: boolean
static registerSessionGrantedListener(): void
}
export { VRButton }
+115
View File
@@ -0,0 +1,115 @@
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
const VRButton = /* @__PURE__ */ (() => {
const _VRButton = class {
static createButton(renderer, sessionInit = {}) {
const button = document.createElement("button");
function showEnterVR() {
let currentSession = null;
async function onSessionStarted(session) {
session.addEventListener("end", onSessionEnded);
await renderer.xr.setSession(session);
button.textContent = "EXIT VR";
currentSession = session;
}
function onSessionEnded() {
currentSession.removeEventListener("end", onSessionEnded);
button.textContent = "ENTER VR";
currentSession = null;
}
button.style.display = "";
button.style.cursor = "pointer";
button.style.left = "calc(50% - 50px)";
button.style.width = "100px";
button.textContent = "ENTER VR";
button.onmouseenter = () => {
button.style.opacity = "1.0";
};
button.onmouseleave = () => {
button.style.opacity = "0.5";
};
button.onclick = () => {
var _a;
if (currentSession === null) {
const optionalFeatures = [sessionInit.optionalFeatures, "local-floor", "bounded-floor", "hand-tracking"].flat().filter(Boolean);
(_a = navigator.xr) == null ? void 0 : _a.requestSession("immersive-vr", { ...sessionInit, optionalFeatures }).then(onSessionStarted);
} else {
currentSession.end();
}
};
}
function disableButton() {
button.style.display = "";
button.style.cursor = "auto";
button.style.left = "calc(50% - 75px)";
button.style.width = "150px";
button.onmouseenter = null;
button.onmouseleave = null;
button.onclick = null;
}
function showWebXRNotFound() {
disableButton();
button.textContent = "VR NOT SUPPORTED";
}
function stylizeElement(element) {
element.style.position = "absolute";
element.style.bottom = "20px";
element.style.padding = "12px 6px";
element.style.border = "1px solid #fff";
element.style.borderRadius = "4px";
element.style.background = "rgba(0,0,0,0.1)";
element.style.color = "#fff";
element.style.font = "normal 13px sans-serif";
element.style.textAlign = "center";
element.style.opacity = "0.5";
element.style.outline = "none";
element.style.zIndex = "999";
}
if ("xr" in navigator) {
stylizeElement(button);
button.id = "VRButton";
button.style.display = "none";
navigator.xr.isSessionSupported("immersive-vr").then((supported) => {
supported ? showEnterVR() : showWebXRNotFound();
if (supported && _VRButton.xrSessionIsGranted) {
button.click();
}
});
return button;
} else {
const message = document.createElement("a");
if (window.isSecureContext === false) {
message.href = document.location.href.replace(/^http:/, "https:");
message.innerHTML = "WEBXR NEEDS HTTPS";
} else {
message.href = "https://immersiveweb.dev/";
message.innerHTML = "WEBXR NOT AVAILABLE";
}
message.style.left = "calc(50% - 90px)";
message.style.width = "180px";
message.style.textDecoration = "none";
stylizeElement(message);
return message;
}
}
static registerSessionGrantedListener() {
if (typeof navigator !== "undefined" && "xr" in navigator) {
navigator.xr.addEventListener("sessiongranted", () => {
_VRButton.xrSessionIsGranted = true;
});
}
}
};
let VRButton2 = _VRButton;
__publicField(VRButton2, "xrSessionIsGranted", false);
VRButton2.registerSessionGrantedListener();
return VRButton2;
})();
export {
VRButton
};
//# sourceMappingURL=VRButton.js.map
File diff suppressed because one or more lines are too long
+175
View File
@@ -0,0 +1,175 @@
"use strict";
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const THREE = require("three");
const GLTFLoader = require("../loaders/GLTFLoader.cjs");
const MotionControllers = require("../libs/MotionControllers.cjs");
const DEFAULT_PROFILES_PATH = "https://cdn.jsdelivr.net/npm/@webxr-input-profiles/assets@1.0/dist/profiles";
const DEFAULT_PROFILE = "generic-trigger";
const applyEnvironmentMap = (envMap, obj) => {
obj.traverse((child) => {
if (child instanceof THREE.Mesh && "envMap" in child.material) {
child.material.envMap = envMap;
child.material.needsUpdate = true;
}
});
};
class XRControllerModel extends THREE.Object3D {
constructor() {
super();
__publicField(this, "envMap");
__publicField(this, "motionController");
this.motionController = null;
this.envMap = null;
}
setEnvironmentMap(envMap) {
if (this.envMap == envMap) {
return this;
}
this.envMap = envMap;
applyEnvironmentMap(this.envMap, this);
return this;
}
/**
* Polls data from the XRInputSource and updates the model's components to match
* the real world data
*/
updateMatrixWorld(force) {
super.updateMatrixWorld(force);
if (!this.motionController)
return;
this.motionController.updateFromGamepad();
Object.values(this.motionController.components).forEach((component) => {
Object.values(component.visualResponses).forEach((visualResponse) => {
const { valueNode, minNode, maxNode, value, valueNodeProperty } = visualResponse;
if (!valueNode)
return;
if (valueNodeProperty === MotionControllers.MotionControllerConstants.VisualResponseProperty.VISIBILITY && typeof value === "boolean") {
valueNode.visible = value;
} else if (valueNodeProperty === MotionControllers.MotionControllerConstants.VisualResponseProperty.TRANSFORM && minNode && maxNode && typeof value === "number") {
valueNode.quaternion.slerpQuaternions(minNode.quaternion, maxNode.quaternion, value);
valueNode.position.lerpVectors(minNode.position, maxNode.position, value);
}
});
});
}
}
function findNodes(motionController, scene) {
Object.values(motionController.components).forEach((component) => {
const { type, touchPointNodeName, visualResponses } = component;
if (type === MotionControllers.MotionControllerConstants.ComponentType.TOUCHPAD && touchPointNodeName) {
component.touchPointNode = scene.getObjectByName(touchPointNodeName);
if (component.touchPointNode) {
const sphereGeometry = new THREE.SphereGeometry(1e-3);
const material = new THREE.MeshBasicMaterial({ color: 255 });
const sphere = new THREE.Mesh(sphereGeometry, material);
component.touchPointNode.add(sphere);
} else {
console.warn(`Could not find touch dot, ${component.touchPointNodeName}, in touchpad component ${component.id}`);
}
}
Object.values(visualResponses).forEach((visualResponse) => {
const { valueNodeName, minNodeName, maxNodeName, valueNodeProperty } = visualResponse;
if (valueNodeProperty === MotionControllers.MotionControllerConstants.VisualResponseProperty.TRANSFORM && minNodeName && maxNodeName) {
visualResponse.minNode = scene.getObjectByName(minNodeName);
visualResponse.maxNode = scene.getObjectByName(maxNodeName);
if (!visualResponse.minNode) {
console.warn(`Could not find ${minNodeName} in the model`);
return;
}
if (!visualResponse.maxNode) {
console.warn(`Could not find ${maxNodeName} in the model`);
return;
}
}
visualResponse.valueNode = scene.getObjectByName(valueNodeName);
if (!visualResponse.valueNode) {
console.warn(`Could not find ${valueNodeName} in the model`);
}
});
});
}
function addAssetSceneToControllerModel(controllerModel, scene) {
findNodes(controllerModel.motionController, scene);
if (controllerModel.envMap) {
applyEnvironmentMap(controllerModel.envMap, scene);
}
controllerModel.add(scene);
}
class XRControllerModelFactory {
constructor(gltfLoader = null) {
__publicField(this, "gltfLoader");
__publicField(this, "path");
__publicField(this, "_assetCache");
this.gltfLoader = gltfLoader;
this.path = DEFAULT_PROFILES_PATH;
this._assetCache = {};
if (!this.gltfLoader) {
this.gltfLoader = new GLTFLoader.GLTFLoader();
}
}
createControllerModel(controller) {
const controllerModel = new XRControllerModel();
let scene = null;
const onConnected = (event) => {
const xrInputSource = event.data;
if (xrInputSource.targetRayMode !== "tracked-pointer" || !xrInputSource.gamepad)
return;
MotionControllers.fetchProfile(xrInputSource, this.path, DEFAULT_PROFILE).then(({ profile, assetPath }) => {
if (!assetPath) {
throw new Error("no asset path");
}
controllerModel.motionController = new MotionControllers.MotionController(xrInputSource, profile, assetPath);
const assetUrl = controllerModel.motionController.assetUrl;
const cachedAsset = this._assetCache[assetUrl];
if (cachedAsset) {
scene = cachedAsset.scene.clone();
addAssetSceneToControllerModel(controllerModel, scene);
} else {
if (!this.gltfLoader) {
throw new Error("GLTFLoader not set.");
}
this.gltfLoader.setPath("");
this.gltfLoader.load(
controllerModel.motionController.assetUrl,
(asset) => {
if (!controllerModel.motionController) {
console.warn("motionController gone while gltf load, bailing...");
return;
}
this._assetCache[assetUrl] = asset;
scene = asset.scene.clone();
addAssetSceneToControllerModel(controllerModel, scene);
},
() => {
},
() => {
throw new Error(`Asset ${assetUrl} missing or malformed.`);
}
);
}
}).catch((err) => {
console.warn(err);
});
};
controller.addEventListener("connected", onConnected);
const onDisconnected = () => {
controller.removeEventListener("connected", onConnected);
controller.removeEventListener("disconnected", onDisconnected);
controllerModel.motionController = null;
if (scene) {
controllerModel.remove(scene);
}
scene = null;
};
controller.addEventListener("disconnected", onDisconnected);
return controllerModel;
}
}
exports.XRControllerModelFactory = XRControllerModelFactory;
//# sourceMappingURL=XRControllerModelFactory.cjs.map
File diff suppressed because one or more lines are too long
+23
View File
@@ -0,0 +1,23 @@
import { Object3D } from 'three';
import type { Texture, Group } from 'three';
import { GLTFLoader } from '../loaders/GLTFLoader';
import { MotionController } from '../libs/MotionControllers';
declare class XRControllerModel extends Object3D {
envMap: Texture | null;
motionController: MotionController | null;
constructor();
setEnvironmentMap(envMap: Texture): XRControllerModel;
/**
* Polls data from the XRInputSource and updates the model's components to match
* the real world data
*/
updateMatrixWorld(force: boolean): void;
}
declare class XRControllerModelFactory {
gltfLoader: GLTFLoader;
path: string;
private _assetCache;
constructor(gltfLoader?: GLTFLoader);
createControllerModel(controller: Group): XRControllerModel;
}
export { XRControllerModelFactory };
+175
View File
@@ -0,0 +1,175 @@
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
import { Object3D, Mesh, SphereGeometry, MeshBasicMaterial } from "three";
import { GLTFLoader } from "../loaders/GLTFLoader.js";
import { MotionControllerConstants, fetchProfile, MotionController } from "../libs/MotionControllers.js";
const DEFAULT_PROFILES_PATH = "https://cdn.jsdelivr.net/npm/@webxr-input-profiles/assets@1.0/dist/profiles";
const DEFAULT_PROFILE = "generic-trigger";
const applyEnvironmentMap = (envMap, obj) => {
obj.traverse((child) => {
if (child instanceof Mesh && "envMap" in child.material) {
child.material.envMap = envMap;
child.material.needsUpdate = true;
}
});
};
class XRControllerModel extends Object3D {
constructor() {
super();
__publicField(this, "envMap");
__publicField(this, "motionController");
this.motionController = null;
this.envMap = null;
}
setEnvironmentMap(envMap) {
if (this.envMap == envMap) {
return this;
}
this.envMap = envMap;
applyEnvironmentMap(this.envMap, this);
return this;
}
/**
* Polls data from the XRInputSource and updates the model's components to match
* the real world data
*/
updateMatrixWorld(force) {
super.updateMatrixWorld(force);
if (!this.motionController)
return;
this.motionController.updateFromGamepad();
Object.values(this.motionController.components).forEach((component) => {
Object.values(component.visualResponses).forEach((visualResponse) => {
const { valueNode, minNode, maxNode, value, valueNodeProperty } = visualResponse;
if (!valueNode)
return;
if (valueNodeProperty === MotionControllerConstants.VisualResponseProperty.VISIBILITY && typeof value === "boolean") {
valueNode.visible = value;
} else if (valueNodeProperty === MotionControllerConstants.VisualResponseProperty.TRANSFORM && minNode && maxNode && typeof value === "number") {
valueNode.quaternion.slerpQuaternions(minNode.quaternion, maxNode.quaternion, value);
valueNode.position.lerpVectors(minNode.position, maxNode.position, value);
}
});
});
}
}
function findNodes(motionController, scene) {
Object.values(motionController.components).forEach((component) => {
const { type, touchPointNodeName, visualResponses } = component;
if (type === MotionControllerConstants.ComponentType.TOUCHPAD && touchPointNodeName) {
component.touchPointNode = scene.getObjectByName(touchPointNodeName);
if (component.touchPointNode) {
const sphereGeometry = new SphereGeometry(1e-3);
const material = new MeshBasicMaterial({ color: 255 });
const sphere = new Mesh(sphereGeometry, material);
component.touchPointNode.add(sphere);
} else {
console.warn(`Could not find touch dot, ${component.touchPointNodeName}, in touchpad component ${component.id}`);
}
}
Object.values(visualResponses).forEach((visualResponse) => {
const { valueNodeName, minNodeName, maxNodeName, valueNodeProperty } = visualResponse;
if (valueNodeProperty === MotionControllerConstants.VisualResponseProperty.TRANSFORM && minNodeName && maxNodeName) {
visualResponse.minNode = scene.getObjectByName(minNodeName);
visualResponse.maxNode = scene.getObjectByName(maxNodeName);
if (!visualResponse.minNode) {
console.warn(`Could not find ${minNodeName} in the model`);
return;
}
if (!visualResponse.maxNode) {
console.warn(`Could not find ${maxNodeName} in the model`);
return;
}
}
visualResponse.valueNode = scene.getObjectByName(valueNodeName);
if (!visualResponse.valueNode) {
console.warn(`Could not find ${valueNodeName} in the model`);
}
});
});
}
function addAssetSceneToControllerModel(controllerModel, scene) {
findNodes(controllerModel.motionController, scene);
if (controllerModel.envMap) {
applyEnvironmentMap(controllerModel.envMap, scene);
}
controllerModel.add(scene);
}
class XRControllerModelFactory {
constructor(gltfLoader = null) {
__publicField(this, "gltfLoader");
__publicField(this, "path");
__publicField(this, "_assetCache");
this.gltfLoader = gltfLoader;
this.path = DEFAULT_PROFILES_PATH;
this._assetCache = {};
if (!this.gltfLoader) {
this.gltfLoader = new GLTFLoader();
}
}
createControllerModel(controller) {
const controllerModel = new XRControllerModel();
let scene = null;
const onConnected = (event) => {
const xrInputSource = event.data;
if (xrInputSource.targetRayMode !== "tracked-pointer" || !xrInputSource.gamepad)
return;
fetchProfile(xrInputSource, this.path, DEFAULT_PROFILE).then(({ profile, assetPath }) => {
if (!assetPath) {
throw new Error("no asset path");
}
controllerModel.motionController = new MotionController(xrInputSource, profile, assetPath);
const assetUrl = controllerModel.motionController.assetUrl;
const cachedAsset = this._assetCache[assetUrl];
if (cachedAsset) {
scene = cachedAsset.scene.clone();
addAssetSceneToControllerModel(controllerModel, scene);
} else {
if (!this.gltfLoader) {
throw new Error("GLTFLoader not set.");
}
this.gltfLoader.setPath("");
this.gltfLoader.load(
controllerModel.motionController.assetUrl,
(asset) => {
if (!controllerModel.motionController) {
console.warn("motionController gone while gltf load, bailing...");
return;
}
this._assetCache[assetUrl] = asset;
scene = asset.scene.clone();
addAssetSceneToControllerModel(controllerModel, scene);
},
() => {
},
() => {
throw new Error(`Asset ${assetUrl} missing or malformed.`);
}
);
}
}).catch((err) => {
console.warn(err);
});
};
controller.addEventListener("connected", onConnected);
const onDisconnected = () => {
controller.removeEventListener("connected", onConnected);
controller.removeEventListener("disconnected", onDisconnected);
controllerModel.motionController = null;
if (scene) {
controllerModel.remove(scene);
}
scene = null;
};
controller.addEventListener("disconnected", onDisconnected);
return controllerModel;
}
}
export {
XRControllerModelFactory
};
//# sourceMappingURL=XRControllerModelFactory.js.map
File diff suppressed because one or more lines are too long
+127
View File
@@ -0,0 +1,127 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const THREE = require("three");
class SessionLightProbe {
constructor(xrLight, renderer, lightProbe, environmentEstimation, estimationStartCallback) {
this.xrLight = xrLight;
this.renderer = renderer;
this.lightProbe = lightProbe;
this.xrWebGLBinding = null;
this.estimationStartCallback = estimationStartCallback;
this.frameCallback = this.onXRFrame.bind(this);
const session = renderer.xr.getSession();
if (environmentEstimation && "XRWebGLBinding" in window) {
const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(16);
xrLight.environment = cubeRenderTarget.texture;
const gl = renderer.getContext();
switch (session.preferredReflectionFormat) {
case "srgba8":
gl.getExtension("EXT_sRGB");
break;
case "rgba16f":
gl.getExtension("OES_texture_half_float");
break;
}
this.xrWebGLBinding = new XRWebGLBinding(session, gl);
this.lightProbe.addEventListener("reflectionchange", () => {
this.updateReflection();
});
}
session.requestAnimationFrame(this.frameCallback);
}
updateReflection() {
const textureProperties = this.renderer.properties.get(this.xrLight.environment);
if (textureProperties) {
const cubeMap = this.xrWebGLBinding.getReflectionCubeMap(this.lightProbe);
if (cubeMap) {
textureProperties.__webglTexture = cubeMap;
this.xrLight.environment.needsPMREMUpdate = true;
}
}
}
onXRFrame(time, xrFrame) {
if (!this.xrLight) {
return;
}
const session = xrFrame.session;
session.requestAnimationFrame(this.frameCallback);
const lightEstimate = xrFrame.getLightEstimate(this.lightProbe);
if (lightEstimate) {
this.xrLight.lightProbe.sh.fromArray(lightEstimate.sphericalHarmonicsCoefficients);
this.xrLight.lightProbe.intensity = 1;
const intensityScalar = Math.max(
1,
Math.max(
lightEstimate.primaryLightIntensity.x,
Math.max(lightEstimate.primaryLightIntensity.y, lightEstimate.primaryLightIntensity.z)
)
);
this.xrLight.directionalLight.color.setRGB(
lightEstimate.primaryLightIntensity.x / intensityScalar,
lightEstimate.primaryLightIntensity.y / intensityScalar,
lightEstimate.primaryLightIntensity.z / intensityScalar
);
this.xrLight.directionalLight.intensity = intensityScalar;
this.xrLight.directionalLight.position.copy(lightEstimate.primaryLightDirection);
if (this.estimationStartCallback) {
this.estimationStartCallback();
this.estimationStartCallback = null;
}
}
}
dispose() {
this.xrLight = null;
this.renderer = null;
this.lightProbe = null;
this.xrWebGLBinding = null;
}
}
class XREstimatedLight extends THREE.Group {
constructor(renderer, environmentEstimation = true) {
super();
this.lightProbe = new THREE.LightProbe();
this.lightProbe.intensity = 0;
this.add(this.lightProbe);
this.directionalLight = new THREE.DirectionalLight();
this.directionalLight.intensity = 0;
this.add(this.directionalLight);
this.environment = null;
let sessionLightProbe = null;
let estimationStarted = false;
renderer.xr.addEventListener("sessionstart", () => {
const session = renderer.xr.getSession();
if ("requestLightProbe" in session) {
session.requestLightProbe({
reflectionFormat: session.preferredReflectionFormat
}).then((probe) => {
sessionLightProbe = new SessionLightProbe(this, renderer, probe, environmentEstimation, () => {
estimationStarted = true;
this.dispatchEvent({ type: "estimationstart" });
});
});
}
});
renderer.xr.addEventListener("sessionend", () => {
if (sessionLightProbe) {
sessionLightProbe.dispose();
sessionLightProbe = null;
}
if (estimationStarted) {
this.dispatchEvent({ type: "estimationend" });
}
});
this.dispose = () => {
if (sessionLightProbe) {
sessionLightProbe.dispose();
sessionLightProbe = null;
}
this.remove(this.lightProbe);
this.lightProbe = null;
this.remove(this.directionalLight);
this.directionalLight = null;
this.environment = null;
};
}
}
exports.XREstimatedLight = XREstimatedLight;
//# sourceMappingURL=XREstimatedLight.cjs.map
File diff suppressed because one or more lines are too long
+33
View File
@@ -0,0 +1,33 @@
/// <reference types="webxr" />
import { DirectionalLight, Group, LightProbe, WebGLRenderer, Texture } from 'three'
export class SessionLightProbe {
xrLight: XREstimatedLight
renderer: WebGLRenderer
lightProbe: unknown
xrWebGLBinding: unknown | null
estimationStartCallback: () => void
frameCallback: (this: SessionLightProbe, time: number, xrFrame: XRFrame) => void
constructor(
xrLight: XREstimatedLight,
renderer: WebGLRenderer,
lightProbe: unknown,
environmentEstimation: boolean,
estimationStartCallback: () => void,
)
updateReflection: () => void
onXRFrame: (time: number, xrFrame: XRFrame) => void
dispose: () => void
}
export class XREstimatedLight extends Group {
lightProbe: LightProbe
directionalLight: DirectionalLight
environment: Texture
constructor(renderer: WebGLRenderer, environmentEstimation?: boolean)
}
+127
View File
@@ -0,0 +1,127 @@
import { Group, LightProbe, DirectionalLight, WebGLCubeRenderTarget } from "three";
class SessionLightProbe {
constructor(xrLight, renderer, lightProbe, environmentEstimation, estimationStartCallback) {
this.xrLight = xrLight;
this.renderer = renderer;
this.lightProbe = lightProbe;
this.xrWebGLBinding = null;
this.estimationStartCallback = estimationStartCallback;
this.frameCallback = this.onXRFrame.bind(this);
const session = renderer.xr.getSession();
if (environmentEstimation && "XRWebGLBinding" in window) {
const cubeRenderTarget = new WebGLCubeRenderTarget(16);
xrLight.environment = cubeRenderTarget.texture;
const gl = renderer.getContext();
switch (session.preferredReflectionFormat) {
case "srgba8":
gl.getExtension("EXT_sRGB");
break;
case "rgba16f":
gl.getExtension("OES_texture_half_float");
break;
}
this.xrWebGLBinding = new XRWebGLBinding(session, gl);
this.lightProbe.addEventListener("reflectionchange", () => {
this.updateReflection();
});
}
session.requestAnimationFrame(this.frameCallback);
}
updateReflection() {
const textureProperties = this.renderer.properties.get(this.xrLight.environment);
if (textureProperties) {
const cubeMap = this.xrWebGLBinding.getReflectionCubeMap(this.lightProbe);
if (cubeMap) {
textureProperties.__webglTexture = cubeMap;
this.xrLight.environment.needsPMREMUpdate = true;
}
}
}
onXRFrame(time, xrFrame) {
if (!this.xrLight) {
return;
}
const session = xrFrame.session;
session.requestAnimationFrame(this.frameCallback);
const lightEstimate = xrFrame.getLightEstimate(this.lightProbe);
if (lightEstimate) {
this.xrLight.lightProbe.sh.fromArray(lightEstimate.sphericalHarmonicsCoefficients);
this.xrLight.lightProbe.intensity = 1;
const intensityScalar = Math.max(
1,
Math.max(
lightEstimate.primaryLightIntensity.x,
Math.max(lightEstimate.primaryLightIntensity.y, lightEstimate.primaryLightIntensity.z)
)
);
this.xrLight.directionalLight.color.setRGB(
lightEstimate.primaryLightIntensity.x / intensityScalar,
lightEstimate.primaryLightIntensity.y / intensityScalar,
lightEstimate.primaryLightIntensity.z / intensityScalar
);
this.xrLight.directionalLight.intensity = intensityScalar;
this.xrLight.directionalLight.position.copy(lightEstimate.primaryLightDirection);
if (this.estimationStartCallback) {
this.estimationStartCallback();
this.estimationStartCallback = null;
}
}
}
dispose() {
this.xrLight = null;
this.renderer = null;
this.lightProbe = null;
this.xrWebGLBinding = null;
}
}
class XREstimatedLight extends Group {
constructor(renderer, environmentEstimation = true) {
super();
this.lightProbe = new LightProbe();
this.lightProbe.intensity = 0;
this.add(this.lightProbe);
this.directionalLight = new DirectionalLight();
this.directionalLight.intensity = 0;
this.add(this.directionalLight);
this.environment = null;
let sessionLightProbe = null;
let estimationStarted = false;
renderer.xr.addEventListener("sessionstart", () => {
const session = renderer.xr.getSession();
if ("requestLightProbe" in session) {
session.requestLightProbe({
reflectionFormat: session.preferredReflectionFormat
}).then((probe) => {
sessionLightProbe = new SessionLightProbe(this, renderer, probe, environmentEstimation, () => {
estimationStarted = true;
this.dispatchEvent({ type: "estimationstart" });
});
});
}
});
renderer.xr.addEventListener("sessionend", () => {
if (sessionLightProbe) {
sessionLightProbe.dispose();
sessionLightProbe = null;
}
if (estimationStarted) {
this.dispatchEvent({ type: "estimationend" });
}
});
this.dispose = () => {
if (sessionLightProbe) {
sessionLightProbe.dispose();
sessionLightProbe = null;
}
this.remove(this.lightProbe);
this.lightProbe = null;
this.remove(this.directionalLight);
this.directionalLight = null;
this.environment = null;
};
}
}
export {
XREstimatedLight
};
//# sourceMappingURL=XREstimatedLight.js.map
File diff suppressed because one or more lines are too long
+83
View File
@@ -0,0 +1,83 @@
"use strict";
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const GLTFLoader = require("../loaders/GLTFLoader.cjs");
const DEFAULT_HAND_PROFILE_PATH = "https://cdn.jsdelivr.net/npm/@webxr-input-profiles/assets@1.0/dist/profiles/generic-hand/";
class XRHandMeshModel {
constructor(handModel, controller, path = DEFAULT_HAND_PROFILE_PATH, handedness, customModelPath) {
__publicField(this, "controller");
__publicField(this, "handModel");
__publicField(this, "bones");
this.controller = controller;
this.handModel = handModel;
this.bones = [];
const loader = new GLTFLoader.GLTFLoader();
if (!customModelPath)
loader.setPath(path);
loader.load(customModelPath != null ? customModelPath : `${handedness}.glb`, (gltf) => {
const object = gltf.scene.children[0];
this.handModel.add(object);
const mesh = object.getObjectByProperty("type", "SkinnedMesh");
mesh.frustumCulled = false;
mesh.castShadow = true;
mesh.receiveShadow = true;
const joints = [
"wrist",
"thumb-metacarpal",
"thumb-phalanx-proximal",
"thumb-phalanx-distal",
"thumb-tip",
"index-finger-metacarpal",
"index-finger-phalanx-proximal",
"index-finger-phalanx-intermediate",
"index-finger-phalanx-distal",
"index-finger-tip",
"middle-finger-metacarpal",
"middle-finger-phalanx-proximal",
"middle-finger-phalanx-intermediate",
"middle-finger-phalanx-distal",
"middle-finger-tip",
"ring-finger-metacarpal",
"ring-finger-phalanx-proximal",
"ring-finger-phalanx-intermediate",
"ring-finger-phalanx-distal",
"ring-finger-tip",
"pinky-finger-metacarpal",
"pinky-finger-phalanx-proximal",
"pinky-finger-phalanx-intermediate",
"pinky-finger-phalanx-distal",
"pinky-finger-tip"
];
joints.forEach((jointName) => {
const bone = object.getObjectByName(jointName);
if (bone !== void 0) {
bone.jointName = jointName;
} else {
console.warn(`Couldn't find ${jointName} in ${handedness} hand mesh`);
}
this.bones.push(bone);
});
});
}
updateMesh() {
const XRJoints = this.controller.joints;
for (let i = 0; i < this.bones.length; i++) {
const bone = this.bones[i];
if (bone) {
const XRJoint = XRJoints[bone.jointName];
if (XRJoint.visible) {
const position = XRJoint.position;
bone.position.copy(position);
bone.quaternion.copy(XRJoint.quaternion);
}
}
}
}
}
exports.XRHandMeshModel = XRHandMeshModel;
//# sourceMappingURL=XRHandMeshModel.cjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"XRHandMeshModel.cjs","sources":["../../src/webxr/XRHandMeshModel.ts"],"sourcesContent":["import { Object3D } from 'three'\nimport { GLTFLoader } from '../loaders/GLTFLoader'\n\nconst DEFAULT_HAND_PROFILE_PATH =\n 'https://cdn.jsdelivr.net/npm/@webxr-input-profiles/assets@1.0/dist/profiles/generic-hand/'\n\nclass XRHandMeshModel {\n controller: Object3D\n handModel: Object3D\n bones: Object3D[]\n\n constructor(\n handModel: Object3D,\n controller: Object3D,\n path: string = DEFAULT_HAND_PROFILE_PATH,\n handedness: string,\n customModelPath?: string,\n ) {\n this.controller = controller\n this.handModel = handModel\n\n this.bones = []\n\n const loader = new GLTFLoader()\n if (!customModelPath) loader.setPath(path)\n loader.load(customModelPath ?? `${handedness}.glb`, (gltf: { scene: Object3D }) => {\n const object = gltf.scene.children[0]\n this.handModel.add(object)\n\n const mesh = object.getObjectByProperty('type', 'SkinnedMesh')!\n mesh.frustumCulled = false\n mesh.castShadow = true\n mesh.receiveShadow = true\n\n const joints = [\n 'wrist',\n 'thumb-metacarpal',\n 'thumb-phalanx-proximal',\n 'thumb-phalanx-distal',\n 'thumb-tip',\n 'index-finger-metacarpal',\n 'index-finger-phalanx-proximal',\n 'index-finger-phalanx-intermediate',\n 'index-finger-phalanx-distal',\n 'index-finger-tip',\n 'middle-finger-metacarpal',\n 'middle-finger-phalanx-proximal',\n 'middle-finger-phalanx-intermediate',\n 'middle-finger-phalanx-distal',\n 'middle-finger-tip',\n 'ring-finger-metacarpal',\n 'ring-finger-phalanx-proximal',\n 'ring-finger-phalanx-intermediate',\n 'ring-finger-phalanx-distal',\n 'ring-finger-tip',\n 'pinky-finger-metacarpal',\n 'pinky-finger-phalanx-proximal',\n 'pinky-finger-phalanx-intermediate',\n 'pinky-finger-phalanx-distal',\n 'pinky-finger-tip',\n ]\n\n joints.forEach((jointName) => {\n const bone = object.getObjectByName(jointName) as any\n\n if (bone !== undefined) {\n bone.jointName = jointName\n } else {\n console.warn(`Couldn't find ${jointName} in ${handedness} hand mesh`)\n }\n\n this.bones.push(bone)\n })\n })\n }\n\n updateMesh(): void {\n // XR Joints\n const XRJoints = (this.controller as any).joints\n\n for (let i = 0; i < this.bones.length; i++) {\n const bone = this.bones[i]\n\n if (bone) {\n const XRJoint = XRJoints[(bone as any).jointName]\n\n if (XRJoint.visible) {\n const position = XRJoint.position\n\n bone.position.copy(position)\n bone.quaternion.copy(XRJoint.quaternion)\n // bone.scale.setScalar( XRJoint.jointRadius || defaultRadius );\n }\n }\n }\n }\n}\n\nexport { XRHandMeshModel }\n"],"names":["GLTFLoader"],"mappings":";;;;;;;;;AAGA,MAAM,4BACJ;AAEF,MAAM,gBAAgB;AAAA,EAKpB,YACE,WACA,YACA,OAAe,2BACf,YACA,iBACA;AAVF;AACA;AACA;AASE,SAAK,aAAa;AAClB,SAAK,YAAY;AAEjB,SAAK,QAAQ;AAEP,UAAA,SAAS,IAAIA,WAAAA;AACnB,QAAI,CAAC;AAAiB,aAAO,QAAQ,IAAI;AACzC,WAAO,KAAK,4CAAmB,GAAG,kBAAkB,CAAC,SAA8B;AACjF,YAAM,SAAS,KAAK,MAAM,SAAS,CAAC;AAC/B,WAAA,UAAU,IAAI,MAAM;AAEzB,YAAM,OAAO,OAAO,oBAAoB,QAAQ,aAAa;AAC7D,WAAK,gBAAgB;AACrB,WAAK,aAAa;AAClB,WAAK,gBAAgB;AAErB,YAAM,SAAS;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAGK,aAAA,QAAQ,CAAC,cAAc;AACtB,cAAA,OAAO,OAAO,gBAAgB,SAAS;AAE7C,YAAI,SAAS,QAAW;AACtB,eAAK,YAAY;AAAA,QAAA,OACZ;AACG,kBAAA,KAAK,iBAAiB,gBAAgB,sBAAsB;AAAA,QACtE;AAEK,aAAA,MAAM,KAAK,IAAI;AAAA,MAAA,CACrB;AAAA,IAAA,CACF;AAAA,EACH;AAAA,EAEA,aAAmB;AAEX,UAAA,WAAY,KAAK,WAAmB;AAE1C,aAAS,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAAK;AACpC,YAAA,OAAO,KAAK,MAAM,CAAC;AAEzB,UAAI,MAAM;AACF,cAAA,UAAU,SAAU,KAAa,SAAS;AAEhD,YAAI,QAAQ,SAAS;AACnB,gBAAM,WAAW,QAAQ;AAEpB,eAAA,SAAS,KAAK,QAAQ;AACtB,eAAA,WAAW,KAAK,QAAQ,UAAU;AAAA,QAEzC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;"}
+9
View File
@@ -0,0 +1,9 @@
import { Object3D } from 'three';
declare class XRHandMeshModel {
controller: Object3D;
handModel: Object3D;
bones: Object3D[];
constructor(handModel: Object3D, controller: Object3D, path: string | undefined, handedness: string, customModelPath?: string);
updateMesh(): void;
}
export { XRHandMeshModel };
+83
View File
@@ -0,0 +1,83 @@
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
import { GLTFLoader } from "../loaders/GLTFLoader.js";
const DEFAULT_HAND_PROFILE_PATH = "https://cdn.jsdelivr.net/npm/@webxr-input-profiles/assets@1.0/dist/profiles/generic-hand/";
class XRHandMeshModel {
constructor(handModel, controller, path = DEFAULT_HAND_PROFILE_PATH, handedness, customModelPath) {
__publicField(this, "controller");
__publicField(this, "handModel");
__publicField(this, "bones");
this.controller = controller;
this.handModel = handModel;
this.bones = [];
const loader = new GLTFLoader();
if (!customModelPath)
loader.setPath(path);
loader.load(customModelPath != null ? customModelPath : `${handedness}.glb`, (gltf) => {
const object = gltf.scene.children[0];
this.handModel.add(object);
const mesh = object.getObjectByProperty("type", "SkinnedMesh");
mesh.frustumCulled = false;
mesh.castShadow = true;
mesh.receiveShadow = true;
const joints = [
"wrist",
"thumb-metacarpal",
"thumb-phalanx-proximal",
"thumb-phalanx-distal",
"thumb-tip",
"index-finger-metacarpal",
"index-finger-phalanx-proximal",
"index-finger-phalanx-intermediate",
"index-finger-phalanx-distal",
"index-finger-tip",
"middle-finger-metacarpal",
"middle-finger-phalanx-proximal",
"middle-finger-phalanx-intermediate",
"middle-finger-phalanx-distal",
"middle-finger-tip",
"ring-finger-metacarpal",
"ring-finger-phalanx-proximal",
"ring-finger-phalanx-intermediate",
"ring-finger-phalanx-distal",
"ring-finger-tip",
"pinky-finger-metacarpal",
"pinky-finger-phalanx-proximal",
"pinky-finger-phalanx-intermediate",
"pinky-finger-phalanx-distal",
"pinky-finger-tip"
];
joints.forEach((jointName) => {
const bone = object.getObjectByName(jointName);
if (bone !== void 0) {
bone.jointName = jointName;
} else {
console.warn(`Couldn't find ${jointName} in ${handedness} hand mesh`);
}
this.bones.push(bone);
});
});
}
updateMesh() {
const XRJoints = this.controller.joints;
for (let i = 0; i < this.bones.length; i++) {
const bone = this.bones[i];
if (bone) {
const XRJoint = XRJoints[bone.jointName];
if (XRJoint.visible) {
const position = XRJoint.position;
bone.position.copy(position);
bone.quaternion.copy(XRJoint.quaternion);
}
}
}
}
}
export {
XRHandMeshModel
};
//# sourceMappingURL=XRHandMeshModel.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"XRHandMeshModel.js","sources":["../../src/webxr/XRHandMeshModel.ts"],"sourcesContent":["import { Object3D } from 'three'\nimport { GLTFLoader } from '../loaders/GLTFLoader'\n\nconst DEFAULT_HAND_PROFILE_PATH =\n 'https://cdn.jsdelivr.net/npm/@webxr-input-profiles/assets@1.0/dist/profiles/generic-hand/'\n\nclass XRHandMeshModel {\n controller: Object3D\n handModel: Object3D\n bones: Object3D[]\n\n constructor(\n handModel: Object3D,\n controller: Object3D,\n path: string = DEFAULT_HAND_PROFILE_PATH,\n handedness: string,\n customModelPath?: string,\n ) {\n this.controller = controller\n this.handModel = handModel\n\n this.bones = []\n\n const loader = new GLTFLoader()\n if (!customModelPath) loader.setPath(path)\n loader.load(customModelPath ?? `${handedness}.glb`, (gltf: { scene: Object3D }) => {\n const object = gltf.scene.children[0]\n this.handModel.add(object)\n\n const mesh = object.getObjectByProperty('type', 'SkinnedMesh')!\n mesh.frustumCulled = false\n mesh.castShadow = true\n mesh.receiveShadow = true\n\n const joints = [\n 'wrist',\n 'thumb-metacarpal',\n 'thumb-phalanx-proximal',\n 'thumb-phalanx-distal',\n 'thumb-tip',\n 'index-finger-metacarpal',\n 'index-finger-phalanx-proximal',\n 'index-finger-phalanx-intermediate',\n 'index-finger-phalanx-distal',\n 'index-finger-tip',\n 'middle-finger-metacarpal',\n 'middle-finger-phalanx-proximal',\n 'middle-finger-phalanx-intermediate',\n 'middle-finger-phalanx-distal',\n 'middle-finger-tip',\n 'ring-finger-metacarpal',\n 'ring-finger-phalanx-proximal',\n 'ring-finger-phalanx-intermediate',\n 'ring-finger-phalanx-distal',\n 'ring-finger-tip',\n 'pinky-finger-metacarpal',\n 'pinky-finger-phalanx-proximal',\n 'pinky-finger-phalanx-intermediate',\n 'pinky-finger-phalanx-distal',\n 'pinky-finger-tip',\n ]\n\n joints.forEach((jointName) => {\n const bone = object.getObjectByName(jointName) as any\n\n if (bone !== undefined) {\n bone.jointName = jointName\n } else {\n console.warn(`Couldn't find ${jointName} in ${handedness} hand mesh`)\n }\n\n this.bones.push(bone)\n })\n })\n }\n\n updateMesh(): void {\n // XR Joints\n const XRJoints = (this.controller as any).joints\n\n for (let i = 0; i < this.bones.length; i++) {\n const bone = this.bones[i]\n\n if (bone) {\n const XRJoint = XRJoints[(bone as any).jointName]\n\n if (XRJoint.visible) {\n const position = XRJoint.position\n\n bone.position.copy(position)\n bone.quaternion.copy(XRJoint.quaternion)\n // bone.scale.setScalar( XRJoint.jointRadius || defaultRadius );\n }\n }\n }\n }\n}\n\nexport { XRHandMeshModel }\n"],"names":[],"mappings":";;;;;;;AAGA,MAAM,4BACJ;AAEF,MAAM,gBAAgB;AAAA,EAKpB,YACE,WACA,YACA,OAAe,2BACf,YACA,iBACA;AAVF;AACA;AACA;AASE,SAAK,aAAa;AAClB,SAAK,YAAY;AAEjB,SAAK,QAAQ;AAEP,UAAA,SAAS,IAAI;AACnB,QAAI,CAAC;AAAiB,aAAO,QAAQ,IAAI;AACzC,WAAO,KAAK,4CAAmB,GAAG,kBAAkB,CAAC,SAA8B;AACjF,YAAM,SAAS,KAAK,MAAM,SAAS,CAAC;AAC/B,WAAA,UAAU,IAAI,MAAM;AAEzB,YAAM,OAAO,OAAO,oBAAoB,QAAQ,aAAa;AAC7D,WAAK,gBAAgB;AACrB,WAAK,aAAa;AAClB,WAAK,gBAAgB;AAErB,YAAM,SAAS;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAGK,aAAA,QAAQ,CAAC,cAAc;AACtB,cAAA,OAAO,OAAO,gBAAgB,SAAS;AAE7C,YAAI,SAAS,QAAW;AACtB,eAAK,YAAY;AAAA,QAAA,OACZ;AACG,kBAAA,KAAK,iBAAiB,gBAAgB,sBAAsB;AAAA,QACtE;AAEK,aAAA,MAAM,KAAK,IAAI;AAAA,MAAA,CACrB;AAAA,IAAA,CACF;AAAA,EACH;AAAA,EAEA,aAAmB;AAEX,UAAA,WAAY,KAAK,WAAmB;AAE1C,aAAS,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAAK;AACpC,YAAA,OAAO,KAAK,MAAM,CAAC;AAEzB,UAAI,MAAM;AACF,cAAA,UAAU,SAAU,KAAa,SAAS;AAEhD,YAAI,QAAQ,SAAS;AACnB,gBAAM,WAAW,QAAQ;AAEpB,eAAA,SAAS,KAAK,QAAQ;AACtB,eAAA,WAAW,KAAK,QAAQ,UAAU;AAAA,QAEzC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;"}
+62
View File
@@ -0,0 +1,62 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const THREE = require("three");
const XRHandPrimitiveModel = require("./XRHandPrimitiveModel.cjs");
const XRHandMeshModel = require("./XRHandMeshModel.cjs");
class XRHandModel extends THREE.Object3D {
constructor(controller) {
super();
this.controller = controller;
this.motionController = null;
this.envMap = null;
this.mesh = null;
}
updateMatrixWorld(force) {
super.updateMatrixWorld(force);
if (this.motionController) {
this.motionController.updateMesh();
}
}
}
class XRHandModelFactory {
constructor() {
this.path = null;
}
setPath(path) {
this.path = path;
return this;
}
createHandModel(controller, profile) {
const handModel = new XRHandModel(controller);
controller.addEventListener("connected", (event) => {
const xrInputSource = event.data;
if (xrInputSource.hand && !handModel.motionController) {
handModel.xrInputSource = xrInputSource;
if (profile === void 0 || profile === "spheres") {
handModel.motionController = new XRHandPrimitiveModel.XRHandPrimitiveModel(
handModel,
controller,
this.path,
xrInputSource.handedness,
{ primitive: "sphere" }
);
} else if (profile === "boxes") {
handModel.motionController = new XRHandPrimitiveModel.XRHandPrimitiveModel(
handModel,
controller,
this.path,
xrInputSource.handedness,
{ primitive: "box" }
);
} else if (profile === "mesh") {
handModel.motionController = new XRHandMeshModel.XRHandMeshModel(handModel, controller, this.path, xrInputSource.handedness);
}
}
});
controller.addEventListener("disconnected", () => {
});
return handModel;
}
}
exports.XRHandModelFactory = XRHandModelFactory;
//# sourceMappingURL=XRHandModelFactory.cjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"XRHandModelFactory.cjs","sources":["../../src/webxr/XRHandModelFactory.js"],"sourcesContent":["import { Object3D } from 'three'\nimport { XRHandPrimitiveModel } from './XRHandPrimitiveModel'\nimport { XRHandMeshModel } from './XRHandMeshModel'\n\nclass XRHandModel extends Object3D {\n constructor(controller) {\n super()\n\n this.controller = controller\n this.motionController = null\n this.envMap = null\n\n this.mesh = null\n }\n\n updateMatrixWorld(force) {\n super.updateMatrixWorld(force)\n\n if (this.motionController) {\n this.motionController.updateMesh()\n }\n }\n}\n\nclass XRHandModelFactory {\n constructor() {\n this.path = null\n }\n\n setPath(path) {\n this.path = path\n\n return this\n }\n\n createHandModel(controller, profile) {\n const handModel = new XRHandModel(controller)\n\n controller.addEventListener('connected', (event) => {\n const xrInputSource = event.data\n\n if (xrInputSource.hand && !handModel.motionController) {\n handModel.xrInputSource = xrInputSource\n\n // @todo Detect profile if not provided\n if (profile === undefined || profile === 'spheres') {\n handModel.motionController = new XRHandPrimitiveModel(\n handModel,\n controller,\n this.path,\n xrInputSource.handedness,\n { primitive: 'sphere' },\n )\n } else if (profile === 'boxes') {\n handModel.motionController = new XRHandPrimitiveModel(\n handModel,\n controller,\n this.path,\n xrInputSource.handedness,\n { primitive: 'box' },\n )\n } else if (profile === 'mesh') {\n handModel.motionController = new XRHandMeshModel(handModel, controller, this.path, xrInputSource.handedness)\n }\n }\n })\n\n controller.addEventListener('disconnected', () => {\n // handModel.motionController = null;\n // handModel.remove( scene );\n // scene = null;\n })\n\n return handModel\n }\n}\n\nexport { XRHandModelFactory }\n"],"names":["Object3D","XRHandPrimitiveModel","XRHandMeshModel"],"mappings":";;;;;AAIA,MAAM,oBAAoBA,MAAAA,SAAS;AAAA,EACjC,YAAY,YAAY;AACtB,UAAO;AAEP,SAAK,aAAa;AAClB,SAAK,mBAAmB;AACxB,SAAK,SAAS;AAEd,SAAK,OAAO;AAAA,EACb;AAAA,EAED,kBAAkB,OAAO;AACvB,UAAM,kBAAkB,KAAK;AAE7B,QAAI,KAAK,kBAAkB;AACzB,WAAK,iBAAiB,WAAY;AAAA,IACnC;AAAA,EACF;AACH;AAEA,MAAM,mBAAmB;AAAA,EACvB,cAAc;AACZ,SAAK,OAAO;AAAA,EACb;AAAA,EAED,QAAQ,MAAM;AACZ,SAAK,OAAO;AAEZ,WAAO;AAAA,EACR;AAAA,EAED,gBAAgB,YAAY,SAAS;AACnC,UAAM,YAAY,IAAI,YAAY,UAAU;AAE5C,eAAW,iBAAiB,aAAa,CAAC,UAAU;AAClD,YAAM,gBAAgB,MAAM;AAE5B,UAAI,cAAc,QAAQ,CAAC,UAAU,kBAAkB;AACrD,kBAAU,gBAAgB;AAG1B,YAAI,YAAY,UAAa,YAAY,WAAW;AAClD,oBAAU,mBAAmB,IAAIC,qBAAoB;AAAA,YACnD;AAAA,YACA;AAAA,YACA,KAAK;AAAA,YACL,cAAc;AAAA,YACd,EAAE,WAAW,SAAU;AAAA,UACxB;AAAA,QACX,WAAmB,YAAY,SAAS;AAC9B,oBAAU,mBAAmB,IAAIA,qBAAoB;AAAA,YACnD;AAAA,YACA;AAAA,YACA,KAAK;AAAA,YACL,cAAc;AAAA,YACd,EAAE,WAAW,MAAO;AAAA,UACrB;AAAA,QACX,WAAmB,YAAY,QAAQ;AAC7B,oBAAU,mBAAmB,IAAIC,gBAAAA,gBAAgB,WAAW,YAAY,KAAK,MAAM,cAAc,UAAU;AAAA,QAC5G;AAAA,MACF;AAAA,IACP,CAAK;AAED,eAAW,iBAAiB,gBAAgB,MAAM;AAAA,IAItD,CAAK;AAED,WAAO;AAAA,EACR;AACH;;"}
+25
View File
@@ -0,0 +1,25 @@
import { Group, Object3D } from 'three'
import { XRHandPrimitiveModel, XRHandPrimitiveModelOptions } from './XRHandPrimitiveModel'
import { XRHandMeshModel } from './XRHandMeshModel'
export type XRHandModelHandedness = 'left' | 'right'
export class XRHandModel extends Object3D {
constructor()
motionController: XRHandPrimitiveModel | XRHandMeshModel
}
export class XRHandModelFactory {
constructor()
path: string
setPath(path: string): XRHandModelFactory
createHandModel(
controller: Group,
profile?: 'spheres' | 'boxes' | 'oculus',
options?: XRHandPrimitiveModelOptions,
): XRHandModel
}
+62
View File
@@ -0,0 +1,62 @@
import { Object3D } from "three";
import { XRHandPrimitiveModel } from "./XRHandPrimitiveModel.js";
import { XRHandMeshModel } from "./XRHandMeshModel.js";
class XRHandModel extends Object3D {
constructor(controller) {
super();
this.controller = controller;
this.motionController = null;
this.envMap = null;
this.mesh = null;
}
updateMatrixWorld(force) {
super.updateMatrixWorld(force);
if (this.motionController) {
this.motionController.updateMesh();
}
}
}
class XRHandModelFactory {
constructor() {
this.path = null;
}
setPath(path) {
this.path = path;
return this;
}
createHandModel(controller, profile) {
const handModel = new XRHandModel(controller);
controller.addEventListener("connected", (event) => {
const xrInputSource = event.data;
if (xrInputSource.hand && !handModel.motionController) {
handModel.xrInputSource = xrInputSource;
if (profile === void 0 || profile === "spheres") {
handModel.motionController = new XRHandPrimitiveModel(
handModel,
controller,
this.path,
xrInputSource.handedness,
{ primitive: "sphere" }
);
} else if (profile === "boxes") {
handModel.motionController = new XRHandPrimitiveModel(
handModel,
controller,
this.path,
xrInputSource.handedness,
{ primitive: "box" }
);
} else if (profile === "mesh") {
handModel.motionController = new XRHandMeshModel(handModel, controller, this.path, xrInputSource.handedness);
}
}
});
controller.addEventListener("disconnected", () => {
});
return handModel;
}
}
export {
XRHandModelFactory
};
//# sourceMappingURL=XRHandModelFactory.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"XRHandModelFactory.js","sources":["../../src/webxr/XRHandModelFactory.js"],"sourcesContent":["import { Object3D } from 'three'\nimport { XRHandPrimitiveModel } from './XRHandPrimitiveModel'\nimport { XRHandMeshModel } from './XRHandMeshModel'\n\nclass XRHandModel extends Object3D {\n constructor(controller) {\n super()\n\n this.controller = controller\n this.motionController = null\n this.envMap = null\n\n this.mesh = null\n }\n\n updateMatrixWorld(force) {\n super.updateMatrixWorld(force)\n\n if (this.motionController) {\n this.motionController.updateMesh()\n }\n }\n}\n\nclass XRHandModelFactory {\n constructor() {\n this.path = null\n }\n\n setPath(path) {\n this.path = path\n\n return this\n }\n\n createHandModel(controller, profile) {\n const handModel = new XRHandModel(controller)\n\n controller.addEventListener('connected', (event) => {\n const xrInputSource = event.data\n\n if (xrInputSource.hand && !handModel.motionController) {\n handModel.xrInputSource = xrInputSource\n\n // @todo Detect profile if not provided\n if (profile === undefined || profile === 'spheres') {\n handModel.motionController = new XRHandPrimitiveModel(\n handModel,\n controller,\n this.path,\n xrInputSource.handedness,\n { primitive: 'sphere' },\n )\n } else if (profile === 'boxes') {\n handModel.motionController = new XRHandPrimitiveModel(\n handModel,\n controller,\n this.path,\n xrInputSource.handedness,\n { primitive: 'box' },\n )\n } else if (profile === 'mesh') {\n handModel.motionController = new XRHandMeshModel(handModel, controller, this.path, xrInputSource.handedness)\n }\n }\n })\n\n controller.addEventListener('disconnected', () => {\n // handModel.motionController = null;\n // handModel.remove( scene );\n // scene = null;\n })\n\n return handModel\n }\n}\n\nexport { XRHandModelFactory }\n"],"names":[],"mappings":";;;AAIA,MAAM,oBAAoB,SAAS;AAAA,EACjC,YAAY,YAAY;AACtB,UAAO;AAEP,SAAK,aAAa;AAClB,SAAK,mBAAmB;AACxB,SAAK,SAAS;AAEd,SAAK,OAAO;AAAA,EACb;AAAA,EAED,kBAAkB,OAAO;AACvB,UAAM,kBAAkB,KAAK;AAE7B,QAAI,KAAK,kBAAkB;AACzB,WAAK,iBAAiB,WAAY;AAAA,IACnC;AAAA,EACF;AACH;AAEA,MAAM,mBAAmB;AAAA,EACvB,cAAc;AACZ,SAAK,OAAO;AAAA,EACb;AAAA,EAED,QAAQ,MAAM;AACZ,SAAK,OAAO;AAEZ,WAAO;AAAA,EACR;AAAA,EAED,gBAAgB,YAAY,SAAS;AACnC,UAAM,YAAY,IAAI,YAAY,UAAU;AAE5C,eAAW,iBAAiB,aAAa,CAAC,UAAU;AAClD,YAAM,gBAAgB,MAAM;AAE5B,UAAI,cAAc,QAAQ,CAAC,UAAU,kBAAkB;AACrD,kBAAU,gBAAgB;AAG1B,YAAI,YAAY,UAAa,YAAY,WAAW;AAClD,oBAAU,mBAAmB,IAAI;AAAA,YAC/B;AAAA,YACA;AAAA,YACA,KAAK;AAAA,YACL,cAAc;AAAA,YACd,EAAE,WAAW,SAAU;AAAA,UACxB;AAAA,QACX,WAAmB,YAAY,SAAS;AAC9B,oBAAU,mBAAmB,IAAI;AAAA,YAC/B;AAAA,YACA;AAAA,YACA,KAAK;AAAA,YACL,cAAc;AAAA,YACd,EAAE,WAAW,MAAO;AAAA,UACrB;AAAA,QACX,WAAmB,YAAY,QAAQ;AAC7B,oBAAU,mBAAmB,IAAI,gBAAgB,WAAW,YAAY,KAAK,MAAM,cAAc,UAAU;AAAA,QAC5G;AAAA,MACF;AAAA,IACP,CAAK;AAED,eAAW,iBAAiB,gBAAgB,MAAM;AAAA,IAItD,CAAK;AAED,WAAO;AAAA,EACR;AACH;"}
+69
View File
@@ -0,0 +1,69 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const THREE = require("three");
const _matrix = /* @__PURE__ */ new THREE.Matrix4();
const _vector = /* @__PURE__ */ new THREE.Vector3();
class XRHandPrimitiveModel {
constructor(handModel, controller, path, handedness, options) {
this.controller = controller;
this.handModel = handModel;
this.envMap = null;
let geometry;
if (!options || !options.primitive || options.primitive === "sphere") {
geometry = new THREE.SphereGeometry(1, 10, 10);
} else if (options.primitive === "box") {
geometry = new THREE.BoxGeometry(1, 1, 1);
}
const material = new THREE.MeshStandardMaterial();
this.handMesh = new THREE.InstancedMesh(geometry, material, 30);
this.handMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
this.handMesh.castShadow = true;
this.handMesh.receiveShadow = true;
this.handModel.add(this.handMesh);
this.joints = [
"wrist",
"thumb-metacarpal",
"thumb-phalanx-proximal",
"thumb-phalanx-distal",
"thumb-tip",
"index-finger-metacarpal",
"index-finger-phalanx-proximal",
"index-finger-phalanx-intermediate",
"index-finger-phalanx-distal",
"index-finger-tip",
"middle-finger-metacarpal",
"middle-finger-phalanx-proximal",
"middle-finger-phalanx-intermediate",
"middle-finger-phalanx-distal",
"middle-finger-tip",
"ring-finger-metacarpal",
"ring-finger-phalanx-proximal",
"ring-finger-phalanx-intermediate",
"ring-finger-phalanx-distal",
"ring-finger-tip",
"pinky-finger-metacarpal",
"pinky-finger-phalanx-proximal",
"pinky-finger-phalanx-intermediate",
"pinky-finger-phalanx-distal",
"pinky-finger-tip"
];
}
updateMesh() {
const defaultRadius = 8e-3;
const joints = this.controller.joints;
let count = 0;
for (let i = 0; i < this.joints.length; i++) {
const joint = joints[this.joints[i]];
if (joint.visible) {
_vector.setScalar(joint.jointRadius || defaultRadius);
_matrix.compose(joint.position, joint.quaternion, _vector);
this.handMesh.setMatrixAt(i, _matrix);
count++;
}
}
this.handMesh.count = count;
this.handMesh.instanceMatrix.needsUpdate = true;
}
}
exports.XRHandPrimitiveModel = XRHandPrimitiveModel;
//# sourceMappingURL=XRHandPrimitiveModel.cjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"XRHandPrimitiveModel.cjs","sources":["../../src/webxr/XRHandPrimitiveModel.js"],"sourcesContent":["import {\n DynamicDrawUsage,\n SphereGeometry,\n BoxGeometry,\n MeshStandardMaterial,\n InstancedMesh,\n Matrix4,\n Vector3,\n} from 'three'\n\nconst _matrix = /* @__PURE__ */ new Matrix4()\nconst _vector = /* @__PURE__ */ new Vector3()\n\nclass XRHandPrimitiveModel {\n constructor(handModel, controller, path, handedness, options) {\n this.controller = controller\n this.handModel = handModel\n this.envMap = null\n\n let geometry\n\n if (!options || !options.primitive || options.primitive === 'sphere') {\n geometry = new SphereGeometry(1, 10, 10)\n } else if (options.primitive === 'box') {\n geometry = new BoxGeometry(1, 1, 1)\n }\n\n const material = new MeshStandardMaterial()\n\n this.handMesh = new InstancedMesh(geometry, material, 30)\n this.handMesh.instanceMatrix.setUsage(DynamicDrawUsage) // will be updated every frame\n this.handMesh.castShadow = true\n this.handMesh.receiveShadow = true\n this.handModel.add(this.handMesh)\n\n this.joints = [\n 'wrist',\n 'thumb-metacarpal',\n 'thumb-phalanx-proximal',\n 'thumb-phalanx-distal',\n 'thumb-tip',\n 'index-finger-metacarpal',\n 'index-finger-phalanx-proximal',\n 'index-finger-phalanx-intermediate',\n 'index-finger-phalanx-distal',\n 'index-finger-tip',\n 'middle-finger-metacarpal',\n 'middle-finger-phalanx-proximal',\n 'middle-finger-phalanx-intermediate',\n 'middle-finger-phalanx-distal',\n 'middle-finger-tip',\n 'ring-finger-metacarpal',\n 'ring-finger-phalanx-proximal',\n 'ring-finger-phalanx-intermediate',\n 'ring-finger-phalanx-distal',\n 'ring-finger-tip',\n 'pinky-finger-metacarpal',\n 'pinky-finger-phalanx-proximal',\n 'pinky-finger-phalanx-intermediate',\n 'pinky-finger-phalanx-distal',\n 'pinky-finger-tip',\n ]\n }\n\n updateMesh() {\n const defaultRadius = 0.008\n const joints = this.controller.joints\n\n let count = 0\n\n for (let i = 0; i < this.joints.length; i++) {\n const joint = joints[this.joints[i]]\n\n if (joint.visible) {\n _vector.setScalar(joint.jointRadius || defaultRadius)\n _matrix.compose(joint.position, joint.quaternion, _vector)\n this.handMesh.setMatrixAt(i, _matrix)\n\n count++\n }\n }\n\n this.handMesh.count = count\n this.handMesh.instanceMatrix.needsUpdate = true\n }\n}\n\nexport { XRHandPrimitiveModel }\n"],"names":["Matrix4","Vector3","SphereGeometry","BoxGeometry","MeshStandardMaterial","InstancedMesh","DynamicDrawUsage"],"mappings":";;;AAUA,MAAM,UAA0B,oBAAIA,MAAAA,QAAS;AAC7C,MAAM,UAA0B,oBAAIC,MAAAA,QAAS;AAE7C,MAAM,qBAAqB;AAAA,EACzB,YAAY,WAAW,YAAY,MAAM,YAAY,SAAS;AAC5D,SAAK,aAAa;AAClB,SAAK,YAAY;AACjB,SAAK,SAAS;AAEd,QAAI;AAEJ,QAAI,CAAC,WAAW,CAAC,QAAQ,aAAa,QAAQ,cAAc,UAAU;AACpE,iBAAW,IAAIC,MAAc,eAAC,GAAG,IAAI,EAAE;AAAA,IAC7C,WAAe,QAAQ,cAAc,OAAO;AACtC,iBAAW,IAAIC,MAAW,YAAC,GAAG,GAAG,CAAC;AAAA,IACnC;AAED,UAAM,WAAW,IAAIC,2BAAsB;AAE3C,SAAK,WAAW,IAAIC,MAAAA,cAAc,UAAU,UAAU,EAAE;AACxD,SAAK,SAAS,eAAe,SAASC,MAAAA,gBAAgB;AACtD,SAAK,SAAS,aAAa;AAC3B,SAAK,SAAS,gBAAgB;AAC9B,SAAK,UAAU,IAAI,KAAK,QAAQ;AAEhC,SAAK,SAAS;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,EACF;AAAA,EAED,aAAa;AACX,UAAM,gBAAgB;AACtB,UAAM,SAAS,KAAK,WAAW;AAE/B,QAAI,QAAQ;AAEZ,aAAS,IAAI,GAAG,IAAI,KAAK,OAAO,QAAQ,KAAK;AAC3C,YAAM,QAAQ,OAAO,KAAK,OAAO,CAAC,CAAC;AAEnC,UAAI,MAAM,SAAS;AACjB,gBAAQ,UAAU,MAAM,eAAe,aAAa;AACpD,gBAAQ,QAAQ,MAAM,UAAU,MAAM,YAAY,OAAO;AACzD,aAAK,SAAS,YAAY,GAAG,OAAO;AAEpC;AAAA,MACD;AAAA,IACF;AAED,SAAK,SAAS,QAAQ;AACtB,SAAK,SAAS,eAAe,cAAc;AAAA,EAC5C;AACH;;"}
+24
View File
@@ -0,0 +1,24 @@
import { Group, Texture } from 'three'
import { XRHandModel, XRHandModelHandedness } from './XRHandModelFactory'
export interface XRHandPrimitiveModelOptions {
primitive?: 'sphere' | 'box' | undefined
}
export class XRHandPrimitiveModel {
controller: Group
handModel: XRHandModel
envMap: Texture | null
handMesh: Group
constructor(
handModel: XRHandModel,
controller: Group,
path: string,
handedness: XRHandModelHandedness,
options: XRHandPrimitiveModelOptions,
)
updateMesh: () => void
}
+69
View File
@@ -0,0 +1,69 @@
import { SphereGeometry, BoxGeometry, MeshStandardMaterial, InstancedMesh, DynamicDrawUsage, Matrix4, Vector3 } from "three";
const _matrix = /* @__PURE__ */ new Matrix4();
const _vector = /* @__PURE__ */ new Vector3();
class XRHandPrimitiveModel {
constructor(handModel, controller, path, handedness, options) {
this.controller = controller;
this.handModel = handModel;
this.envMap = null;
let geometry;
if (!options || !options.primitive || options.primitive === "sphere") {
geometry = new SphereGeometry(1, 10, 10);
} else if (options.primitive === "box") {
geometry = new BoxGeometry(1, 1, 1);
}
const material = new MeshStandardMaterial();
this.handMesh = new InstancedMesh(geometry, material, 30);
this.handMesh.instanceMatrix.setUsage(DynamicDrawUsage);
this.handMesh.castShadow = true;
this.handMesh.receiveShadow = true;
this.handModel.add(this.handMesh);
this.joints = [
"wrist",
"thumb-metacarpal",
"thumb-phalanx-proximal",
"thumb-phalanx-distal",
"thumb-tip",
"index-finger-metacarpal",
"index-finger-phalanx-proximal",
"index-finger-phalanx-intermediate",
"index-finger-phalanx-distal",
"index-finger-tip",
"middle-finger-metacarpal",
"middle-finger-phalanx-proximal",
"middle-finger-phalanx-intermediate",
"middle-finger-phalanx-distal",
"middle-finger-tip",
"ring-finger-metacarpal",
"ring-finger-phalanx-proximal",
"ring-finger-phalanx-intermediate",
"ring-finger-phalanx-distal",
"ring-finger-tip",
"pinky-finger-metacarpal",
"pinky-finger-phalanx-proximal",
"pinky-finger-phalanx-intermediate",
"pinky-finger-phalanx-distal",
"pinky-finger-tip"
];
}
updateMesh() {
const defaultRadius = 8e-3;
const joints = this.controller.joints;
let count = 0;
for (let i = 0; i < this.joints.length; i++) {
const joint = joints[this.joints[i]];
if (joint.visible) {
_vector.setScalar(joint.jointRadius || defaultRadius);
_matrix.compose(joint.position, joint.quaternion, _vector);
this.handMesh.setMatrixAt(i, _matrix);
count++;
}
}
this.handMesh.count = count;
this.handMesh.instanceMatrix.needsUpdate = true;
}
}
export {
XRHandPrimitiveModel
};
//# sourceMappingURL=XRHandPrimitiveModel.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"XRHandPrimitiveModel.js","sources":["../../src/webxr/XRHandPrimitiveModel.js"],"sourcesContent":["import {\n DynamicDrawUsage,\n SphereGeometry,\n BoxGeometry,\n MeshStandardMaterial,\n InstancedMesh,\n Matrix4,\n Vector3,\n} from 'three'\n\nconst _matrix = /* @__PURE__ */ new Matrix4()\nconst _vector = /* @__PURE__ */ new Vector3()\n\nclass XRHandPrimitiveModel {\n constructor(handModel, controller, path, handedness, options) {\n this.controller = controller\n this.handModel = handModel\n this.envMap = null\n\n let geometry\n\n if (!options || !options.primitive || options.primitive === 'sphere') {\n geometry = new SphereGeometry(1, 10, 10)\n } else if (options.primitive === 'box') {\n geometry = new BoxGeometry(1, 1, 1)\n }\n\n const material = new MeshStandardMaterial()\n\n this.handMesh = new InstancedMesh(geometry, material, 30)\n this.handMesh.instanceMatrix.setUsage(DynamicDrawUsage) // will be updated every frame\n this.handMesh.castShadow = true\n this.handMesh.receiveShadow = true\n this.handModel.add(this.handMesh)\n\n this.joints = [\n 'wrist',\n 'thumb-metacarpal',\n 'thumb-phalanx-proximal',\n 'thumb-phalanx-distal',\n 'thumb-tip',\n 'index-finger-metacarpal',\n 'index-finger-phalanx-proximal',\n 'index-finger-phalanx-intermediate',\n 'index-finger-phalanx-distal',\n 'index-finger-tip',\n 'middle-finger-metacarpal',\n 'middle-finger-phalanx-proximal',\n 'middle-finger-phalanx-intermediate',\n 'middle-finger-phalanx-distal',\n 'middle-finger-tip',\n 'ring-finger-metacarpal',\n 'ring-finger-phalanx-proximal',\n 'ring-finger-phalanx-intermediate',\n 'ring-finger-phalanx-distal',\n 'ring-finger-tip',\n 'pinky-finger-metacarpal',\n 'pinky-finger-phalanx-proximal',\n 'pinky-finger-phalanx-intermediate',\n 'pinky-finger-phalanx-distal',\n 'pinky-finger-tip',\n ]\n }\n\n updateMesh() {\n const defaultRadius = 0.008\n const joints = this.controller.joints\n\n let count = 0\n\n for (let i = 0; i < this.joints.length; i++) {\n const joint = joints[this.joints[i]]\n\n if (joint.visible) {\n _vector.setScalar(joint.jointRadius || defaultRadius)\n _matrix.compose(joint.position, joint.quaternion, _vector)\n this.handMesh.setMatrixAt(i, _matrix)\n\n count++\n }\n }\n\n this.handMesh.count = count\n this.handMesh.instanceMatrix.needsUpdate = true\n }\n}\n\nexport { XRHandPrimitiveModel }\n"],"names":[],"mappings":";AAUA,MAAM,UAA0B,oBAAI,QAAS;AAC7C,MAAM,UAA0B,oBAAI,QAAS;AAE7C,MAAM,qBAAqB;AAAA,EACzB,YAAY,WAAW,YAAY,MAAM,YAAY,SAAS;AAC5D,SAAK,aAAa;AAClB,SAAK,YAAY;AACjB,SAAK,SAAS;AAEd,QAAI;AAEJ,QAAI,CAAC,WAAW,CAAC,QAAQ,aAAa,QAAQ,cAAc,UAAU;AACpE,iBAAW,IAAI,eAAe,GAAG,IAAI,EAAE;AAAA,IAC7C,WAAe,QAAQ,cAAc,OAAO;AACtC,iBAAW,IAAI,YAAY,GAAG,GAAG,CAAC;AAAA,IACnC;AAED,UAAM,WAAW,IAAI,qBAAsB;AAE3C,SAAK,WAAW,IAAI,cAAc,UAAU,UAAU,EAAE;AACxD,SAAK,SAAS,eAAe,SAAS,gBAAgB;AACtD,SAAK,SAAS,aAAa;AAC3B,SAAK,SAAS,gBAAgB;AAC9B,SAAK,UAAU,IAAI,KAAK,QAAQ;AAEhC,SAAK,SAAS;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,EACF;AAAA,EAED,aAAa;AACX,UAAM,gBAAgB;AACtB,UAAM,SAAS,KAAK,WAAW;AAE/B,QAAI,QAAQ;AAEZ,aAAS,IAAI,GAAG,IAAI,KAAK,OAAO,QAAQ,KAAK;AAC3C,YAAM,QAAQ,OAAO,KAAK,OAAO,CAAC,CAAC;AAEnC,UAAI,MAAM,SAAS;AACjB,gBAAQ,UAAU,MAAM,eAAe,aAAa;AACpD,gBAAQ,QAAQ,MAAM,UAAU,MAAM,YAAY,OAAO;AACzD,aAAK,SAAS,YAAY,GAAG,OAAO;AAEpC;AAAA,MACD;AAAA,IACF;AAED,SAAK,SAAS,QAAQ;AACtB,SAAK,SAAS,eAAe,cAAc;AAAA,EAC5C;AACH;"}