UNPKG

three-stdlib

Version:

stand-alone library of threejs examples

1 lines 12.4 kB
{"version":3,"file":"XRControllerModelFactory.cjs","sources":["../../src/webxr/XRControllerModelFactory.ts"],"sourcesContent":["import { Mesh, Object3D, SphereGeometry, MeshBasicMaterial } from 'three'\nimport type { Texture, Group } from 'three'\nimport { GLTFLoader } from '../loaders/GLTFLoader'\nimport { fetchProfile, MotionController, MotionControllerConstants } from '../libs/MotionControllers'\n\nconst DEFAULT_PROFILES_PATH = 'https://cdn.jsdelivr.net/npm/@webxr-input-profiles/assets@1.0/dist/profiles'\nconst DEFAULT_PROFILE = 'generic-trigger'\n\nconst applyEnvironmentMap = (envMap: Texture, obj: Object3D): void => {\n obj.traverse((child) => {\n if (child instanceof Mesh && 'envMap' in child.material) {\n child.material.envMap = envMap\n child.material.needsUpdate = true\n }\n })\n}\n\nclass XRControllerModel extends Object3D {\n envMap: Texture | null\n motionController: MotionController | null\n constructor() {\n super()\n\n this.motionController = null\n this.envMap = null\n }\n\n setEnvironmentMap(envMap: Texture): XRControllerModel {\n if (this.envMap == envMap) {\n return this\n }\n\n this.envMap = envMap\n applyEnvironmentMap(this.envMap, this)\n\n return this\n }\n\n /**\n * Polls data from the XRInputSource and updates the model's components to match\n * the real world data\n */\n updateMatrixWorld(force: boolean): void {\n super.updateMatrixWorld(force)\n\n if (!this.motionController) return\n\n // Cause the MotionController to poll the Gamepad for data\n this.motionController.updateFromGamepad()\n\n // Update the 3D model to reflect the button, thumbstick, and touchpad state\n Object.values(this.motionController.components).forEach((component) => {\n // Update node data based on the visual responses' current states\n Object.values(component.visualResponses).forEach((visualResponse) => {\n const { valueNode, minNode, maxNode, value, valueNodeProperty } = visualResponse\n\n // Skip if the visual response node is not found. No error is needed,\n // because it will have been reported at load time.\n if (!valueNode) return\n\n // Calculate the new properties based on the weight supplied\n if (\n valueNodeProperty === MotionControllerConstants.VisualResponseProperty.VISIBILITY &&\n typeof value === 'boolean'\n ) {\n valueNode.visible = value\n } else if (\n valueNodeProperty === MotionControllerConstants.VisualResponseProperty.TRANSFORM &&\n minNode &&\n maxNode &&\n typeof value === 'number'\n ) {\n valueNode.quaternion.slerpQuaternions(minNode.quaternion, maxNode.quaternion, value)\n\n valueNode.position.lerpVectors(minNode.position, maxNode.position, value)\n }\n })\n })\n }\n}\n\n/**\n * Walks the model's tree to find the nodes needed to animate the components and\n * saves them to the motionContoller components for use in the frame loop. When\n * touchpads are found, attaches a touch dot to them.\n */\nfunction findNodes(motionController: MotionController, scene: Object3D): void {\n // Loop through the components and find the nodes needed for each components' visual responses\n Object.values(motionController.components).forEach((component) => {\n const { type, touchPointNodeName, visualResponses } = component\n\n if (type === MotionControllerConstants.ComponentType.TOUCHPAD && touchPointNodeName) {\n component.touchPointNode = scene.getObjectByName(touchPointNodeName)\n if (component.touchPointNode) {\n // Attach a touch dot to the touchpad.\n const sphereGeometry = new SphereGeometry(0.001)\n const material = new MeshBasicMaterial({ color: 0x0000ff })\n const sphere = new Mesh(sphereGeometry, material)\n component.touchPointNode.add(sphere)\n } else {\n console.warn(`Could not find touch dot, ${component.touchPointNodeName}, in touchpad component ${component.id}`)\n }\n }\n\n // Loop through all the visual responses to be applied to this component\n Object.values(visualResponses).forEach((visualResponse) => {\n const { valueNodeName, minNodeName, maxNodeName, valueNodeProperty } = visualResponse\n\n // If animating a transform, find the two nodes to be interpolated between.\n if (\n valueNodeProperty === MotionControllerConstants.VisualResponseProperty.TRANSFORM &&\n minNodeName &&\n maxNodeName\n ) {\n visualResponse.minNode = scene.getObjectByName(minNodeName)\n visualResponse.maxNode = scene.getObjectByName(maxNodeName)\n\n // If the extents cannot be found, skip this animation\n if (!visualResponse.minNode) {\n console.warn(`Could not find ${minNodeName} in the model`)\n return\n }\n\n if (!visualResponse.maxNode) {\n console.warn(`Could not find ${maxNodeName} in the model`)\n return\n }\n }\n\n // If the target node cannot be found, skip this animation\n visualResponse.valueNode = scene.getObjectByName(valueNodeName)\n if (!visualResponse.valueNode) {\n console.warn(`Could not find ${valueNodeName} in the model`)\n }\n })\n })\n}\n\nfunction addAssetSceneToControllerModel(controllerModel: XRControllerModel, scene: Object3D): void {\n // Find the nodes needed for animation and cache them on the motionController.\n findNodes(controllerModel.motionController!, scene)\n\n // Apply any environment map that the mesh already has set.\n if (controllerModel.envMap) {\n applyEnvironmentMap(controllerModel.envMap, scene)\n }\n\n // Add the glTF scene to the controllerModel.\n controllerModel.add(scene)\n}\n\nclass XRControllerModelFactory {\n gltfLoader: GLTFLoader\n path: string\n private _assetCache: Record<string, { scene: Object3D } | undefined>\n constructor(gltfLoader: GLTFLoader = null!) {\n this.gltfLoader = gltfLoader\n this.path = DEFAULT_PROFILES_PATH\n this._assetCache = {}\n\n // If a GLTFLoader wasn't supplied to the constructor create a new one.\n if (!this.gltfLoader) {\n this.gltfLoader = new GLTFLoader()\n }\n }\n\n createControllerModel(controller: Group): XRControllerModel {\n const controllerModel = new XRControllerModel()\n let scene: Object3D | null = null\n\n const onConnected = (event: any): void => {\n const xrInputSource = event.data\n\n if (xrInputSource.targetRayMode !== 'tracked-pointer' || !xrInputSource.gamepad) return\n\n fetchProfile(xrInputSource, this.path, DEFAULT_PROFILE)\n .then(({ profile, assetPath }) => {\n if (!assetPath) {\n throw new Error('no asset path')\n }\n\n controllerModel.motionController = new MotionController(xrInputSource, profile, assetPath)\n\n const assetUrl = controllerModel.motionController.assetUrl\n\n const cachedAsset = this._assetCache[assetUrl]\n if (cachedAsset) {\n scene = cachedAsset.scene.clone()\n\n addAssetSceneToControllerModel(controllerModel, scene)\n } else {\n if (!this.gltfLoader) {\n throw new Error('GLTFLoader not set.')\n }\n\n this.gltfLoader.setPath('')\n this.gltfLoader.load(\n controllerModel.motionController.assetUrl,\n (asset: { scene: Object3D }) => {\n if (!controllerModel.motionController) {\n console.warn('motionController gone while gltf load, bailing...')\n return\n }\n\n this._assetCache[assetUrl] = asset\n\n scene = asset.scene.clone()\n\n addAssetSceneToControllerModel(controllerModel, scene)\n },\n () => {},\n () => {\n throw new Error(`Asset ${assetUrl} missing or malformed.`)\n },\n )\n }\n })\n .catch((err) => {\n console.warn(err)\n })\n }\n\n controller.addEventListener('connected', onConnected)\n\n const onDisconnected = (): void => {\n controller.removeEventListener('connected', onConnected)\n controller.removeEventListener('disconnected', onDisconnected)\n controllerModel.motionController = null\n if (scene) {\n controllerModel.remove(scene)\n }\n scene = null\n }\n\n controller.addEventListener('disconnected', onDisconnected)\n\n return controllerModel\n }\n}\n\nexport { XRControllerModelFactory }\n"],"names":["Mesh","Object3D","MotionControllerConstants","SphereGeometry","MeshBasicMaterial","GLTFLoader","fetchProfile","MotionController"],"mappings":"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"}