| @@ -0,0 +1,46 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Device Orientation Controls Plugin</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| <script type="importmap"> | |||
| { | |||
| "imports": { | |||
| "threepipe": "./../../dist/index.mjs", | |||
| "@threepipe/plugin-tweakpane": "./../../plugins/tweakpane/dist/index.mjs" | |||
| } | |||
| } | |||
| </script> | |||
| <style id="example-style"> | |||
| html, body, #canvas-container, #mcanvas { | |||
| width: 100%; | |||
| height: 100%; | |||
| margin: 0; | |||
| overflow: hidden; | |||
| } | |||
| #deviceOrientationOverlay{ | |||
| position: absolute; top: 0; left: 0; right: 0; bottom: 0; height: 100%; width: 100%; | |||
| display: flex; justify-content: center; align-items: center; cursor: pointer; | |||
| font-size: 1.5rem; background: rgba(240,240,240,0.5); color: #333333; | |||
| backdrop-filter: blur(16px); | |||
| } | |||
| </style> | |||
| <script type="module" src="../examples-utils/simple-code-preview.mjs"></script> | |||
| <script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script> | |||
| </head> | |||
| <body> | |||
| <div id="canvas-container"> | |||
| <canvas id="mcanvas"></canvas> | |||
| <div id="deviceOrientationOverlay"> | |||
| Tap the screen to enable device orientation controls <br/> | |||
| (works only on devices with gyroscope) | |||
| </div> | |||
| </div> | |||
| </body> | |||
| @@ -0,0 +1,30 @@ | |||
| import {_testFinish, DeviceOrientationControlsPlugin, IObject3D, ThreeViewer} from 'threepipe' | |||
| import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane' | |||
| async function init() { | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| msaa: true, | |||
| plugins: [DeviceOrientationControlsPlugin], | |||
| }) | |||
| const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true)) | |||
| ui.appendChild(viewer.scene.mainCamera.uiConfig) | |||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||
| await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', { | |||
| autoCenter: true, | |||
| autoScale: true, | |||
| }) | |||
| const overlayEl = document.getElementById('deviceOrientationOverlay') as HTMLDivElement | |||
| overlayEl.addEventListener('click', () => { | |||
| viewer.scene.mainCamera.controlsMode = 'deviceOrientation' | |||
| overlayEl.style.display = 'none' | |||
| }) | |||
| } | |||
| init().then(_testFinish) | |||
| @@ -243,6 +243,9 @@ | |||
| <li><a href="./transform-controls-plugin/">Transform Controls Plugin </a></li> | |||
| <li><a href="./editor-view-widget-plugin/">Editor View Widget Plugin </a></li> | |||
| <li><a href="./fullscreen-plugin/">FullScreen Plugin </a></li> | |||
| <li><a href="./device-orientation-controls-plugin/">Device Orientation Controls Plugin (Gyroscope) </a></li> | |||
| <li><a href="./pointer-lock-controls-plugin/">Pointer Lock(FPS) Controls Plugin </a></li> | |||
| <li><a href="./three-first-person-controls-plugin/">Three First Person(look around) Controls Plugin </a></li> | |||
| </ul> | |||
| <h2 class="category">Import</h2> | |||
| <ul> | |||
| @@ -0,0 +1,45 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Pointer Lock Controls Plugin</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| <script type="importmap"> | |||
| { | |||
| "imports": { | |||
| "threepipe": "./../../dist/index.mjs", | |||
| "@threepipe/plugin-tweakpane": "./../../plugins/tweakpane/dist/index.mjs" | |||
| } | |||
| } | |||
| </script> | |||
| <style id="example-style"> | |||
| html, body, #canvas-container, #mcanvas { | |||
| width: 100%; | |||
| height: 100%; | |||
| margin: 0; | |||
| overflow: hidden; | |||
| } | |||
| #pointerLockOverlay{ | |||
| position: absolute; top: 0; left: 0; right: 0; bottom: 0; height: 100%; width: 100%; | |||
| display: flex; justify-content: center; align-items: center; pointer-events: none; | |||
| font-size: 1.5rem; background: rgba(240,240,240,0.5); color: #333333; | |||
| backdrop-filter: blur(16px); | |||
| } | |||
| </style> | |||
| <script type="module" src="../examples-utils/simple-code-preview.mjs"></script> | |||
| <script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script> | |||
| </head> | |||
| <body> | |||
| <div id="canvas-container"> | |||
| <canvas id="mcanvas"></canvas> | |||
| <div id="pointerLockOverlay"> | |||
| Tap the screen to enable pointer lock controls | |||
| </div> | |||
| </div> | |||
| </body> | |||
| @@ -0,0 +1,29 @@ | |||
| import {_testFinish, IObject3D, PointerLockControlsPlugin, ThreeViewer} from 'threepipe' | |||
| import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane' | |||
| async function init() { | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| msaa: true, | |||
| plugins: [PointerLockControlsPlugin], | |||
| }) | |||
| const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true)) | |||
| ui.appendChild(viewer.scene.mainCamera.uiConfig) | |||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||
| await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', { | |||
| autoCenter: true, | |||
| autoScale: true, | |||
| }) | |||
| viewer.scene.mainCamera.controlsMode = 'pointerLock' | |||
| const overlayEl = document.getElementById('pointerLockOverlay') as HTMLDivElement | |||
| viewer.scene.mainCamera.controls?.addEventListener('lock', ()=> overlayEl.style.display = 'none') | |||
| viewer.scene.mainCamera.controls?.addEventListener('unlock', ()=> overlayEl.style.display = 'flex') | |||
| } | |||
| init().then(_testFinish) | |||
| @@ -0,0 +1,45 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Three First Person Controls Plugin</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| <script type="importmap"> | |||
| { | |||
| "imports": { | |||
| "threepipe": "./../../dist/index.mjs", | |||
| "@threepipe/plugin-tweakpane": "./../../plugins/tweakpane/dist/index.mjs" | |||
| } | |||
| } | |||
| </script> | |||
| <style id="example-style"> | |||
| html, body, #canvas-container, #mcanvas { | |||
| width: 100%; | |||
| height: 100%; | |||
| margin: 0; | |||
| overflow: hidden; | |||
| } | |||
| #firstPersonControlsOverlay{ | |||
| position: absolute; top: 0; left: 0; right: 0; bottom: 0; height: 100%; width: 100%; | |||
| display: flex; justify-content: center; align-items: center; cursor: pointer; | |||
| font-size: 1.5rem; background: rgba(240,240,240,0.5); color: #333333; | |||
| backdrop-filter: blur(16px); | |||
| } | |||
| </style> | |||
| <script type="module" src="../examples-utils/simple-code-preview.mjs"></script> | |||
| <script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script> | |||
| </head> | |||
| <body> | |||
| <div id="canvas-container"> | |||
| <canvas id="mcanvas"></canvas> | |||
| <div id="firstPersonControlsOverlay"> | |||
| Tap the screen to enable three first person(look around) controls | |||
| </div> | |||
| </div> | |||
| </body> | |||
| @@ -0,0 +1,30 @@ | |||
| import {_testFinish, IObject3D, ThreeFirstPersonControlsPlugin, ThreeViewer} from 'threepipe' | |||
| import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane' | |||
| async function init() { | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| msaa: true, | |||
| plugins: [ThreeFirstPersonControlsPlugin], | |||
| }) | |||
| const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true)) | |||
| ui.appendChild(viewer.scene.mainCamera.uiConfig) | |||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||
| await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', { | |||
| autoCenter: true, | |||
| autoScale: true, | |||
| }) | |||
| const overlayEl = document.getElementById('firstPersonControlsOverlay') as HTMLDivElement | |||
| overlayEl.addEventListener('click', () => { | |||
| viewer.scene.mainCamera.controlsMode = 'threeFirstPerson' | |||
| overlayEl.style.display = 'none' | |||
| }) | |||
| } | |||
| init().then(_testFinish) | |||
| @@ -7,6 +7,7 @@ import { | |||
| ContactShadowGroundPlugin, | |||
| CustomBumpMapPlugin, | |||
| DepthBufferPlugin, | |||
| DeviceOrientationControlsPlugin, | |||
| DropzonePlugin, | |||
| EditorViewWidgetPlugin, | |||
| FilmicGrainPlugin, | |||
| @@ -26,11 +27,13 @@ import { | |||
| Object3DWidgetsPlugin, | |||
| PickingPlugin, | |||
| PLYLoadPlugin, | |||
| PointerLockControlsPlugin, | |||
| ProgressivePlugin, | |||
| RenderTargetPreviewPlugin, | |||
| Rhino3dmLoadPlugin, | |||
| SceneUiConfigPlugin, | |||
| STLLoadPlugin, | |||
| ThreeFirstPersonControlsPlugin, | |||
| ThreeViewer, | |||
| TonemapPlugin, | |||
| TransformControlsPlugin, | |||
| @@ -102,6 +105,9 @@ async function init() { | |||
| GaussianSplattingPlugin, | |||
| ContactShadowGroundPlugin, | |||
| CanvasSnapshotPlugin, | |||
| DeviceOrientationControlsPlugin, | |||
| PointerLockControlsPlugin, | |||
| ThreeFirstPersonControlsPlugin, | |||
| ...extraImportPlugins, | |||
| ]) | |||
| @@ -114,7 +120,7 @@ async function init() { | |||
| editor.loadPlugins({ | |||
| ['Viewer']: [ViewerUiConfigPlugin, SceneUiConfigPlugin, DropzonePlugin, FullScreenPlugin, TweakpaneUiPlugin], | |||
| ['Scene']: [ContactShadowGroundPlugin], | |||
| ['Interaction']: [HierarchyUiPlugin, TransformControlsPlugin, PickingPlugin, Object3DGeneratorPlugin, GeometryGeneratorPlugin, EditorViewWidgetPlugin, Object3DWidgetsPlugin], | |||
| ['Interaction']: [HierarchyUiPlugin, TransformControlsPlugin, PickingPlugin, Object3DGeneratorPlugin, GeometryGeneratorPlugin, EditorViewWidgetPlugin, Object3DWidgetsPlugin, DeviceOrientationControlsPlugin, PointerLockControlsPlugin, ThreeFirstPersonControlsPlugin], | |||
| ['GBuffer']: [GBufferPlugin, DepthBufferPlugin, NormalBufferPlugin], | |||
| ['Post-processing']: [TonemapPlugin, ProgressivePlugin, FrameFadePlugin, VignettePlugin, ChromaticAberrationPlugin, FilmicGrainPlugin], | |||
| ['Export']: [CanvasSnapshotPlugin], | |||
| @@ -0,0 +1,26 @@ | |||
| import {AViewerPluginSync, ThreeViewer} from '../../viewer' | |||
| import {TControlsCtor} from '../../core' | |||
| export abstract class ACameraControlsPlugin extends AViewerPluginSync<''> { | |||
| readonly enabled = true | |||
| toJSON: any = undefined | |||
| protected abstract _controlsCtor: TControlsCtor | |||
| abstract readonly controlsKey: string | |||
| onAdded(viewer: ThreeViewer): void { | |||
| super.onAdded(viewer) | |||
| this._cameraChanged({camera: viewer.scene.mainCamera}) | |||
| viewer.scene.addEventListener('mainCameraChange', this._cameraChanged) | |||
| } | |||
| onRemove(viewer: ThreeViewer): void { | |||
| this._cameraChanged({lastCamera: viewer.scene.mainCamera}) | |||
| viewer.scene.removeEventListener('mainCameraChange', this._cameraChanged) | |||
| super.onRemove(viewer) | |||
| } | |||
| private _cameraChanged = (e: any) => { | |||
| e.lastCamera?.removeControlsCtor?.(this.controlsKey) | |||
| e.camera?.setControlsCtor?.(this.controlsKey, this._controlsCtor) | |||
| } | |||
| } | |||
| @@ -2,6 +2,7 @@ | |||
| export {PipelinePassPlugin} from './base/PipelinePassPlugin' | |||
| export {BaseImporterPlugin} from './base/BaseImporterPlugin' | |||
| export {BaseGroundPlugin} from './base/BaseGroundPlugin' | |||
| export {ACameraControlsPlugin} from './base/ACameraControlsPlugin' | |||
| // pipeline | |||
| export {ProgressivePlugin} from './pipeline/ProgressivePlugin' | |||
| @@ -26,6 +27,9 @@ export {FullScreenPlugin} from './interaction/FullScreenPlugin' | |||
| export {PickingPlugin} from './interaction/PickingPlugin' | |||
| export {TransformControlsPlugin} from './interaction/TransformControlsPlugin' | |||
| export {EditorViewWidgetPlugin} from './interaction/EditorViewWidgetPlugin' | |||
| export {DeviceOrientationControlsPlugin} from './interaction/DeviceOrientationControlsPlugin' | |||
| export {PointerLockControlsPlugin} from './interaction/PointerLockControlsPlugin' | |||
| export {ThreeFirstPersonControlsPlugin} from './interaction/ThreeFirstPersonControlsPlugin' | |||
| // import | |||
| export {Rhino3dmLoadPlugin} from './import/Rhino3dmLoadPlugin' | |||
| @@ -0,0 +1,10 @@ | |||
| import {ACameraControlsPlugin} from '../base/ACameraControlsPlugin' | |||
| import {TControlsCtor} from '../../core' | |||
| import {DeviceOrientationControls2} from '../../three' | |||
| export class DeviceOrientationControlsPlugin extends ACameraControlsPlugin { | |||
| public static readonly PluginType = 'DeviceOrientationControlsPlugin' | |||
| readonly controlsKey = 'deviceOrientation' | |||
| protected _controlsCtor: TControlsCtor = (object, _domElement)=> new DeviceOrientationControls2(object) | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| import {ACameraControlsPlugin} from '../base/ACameraControlsPlugin' | |||
| import {TControlsCtor} from '../../core' | |||
| import {PointerLockControls2} from '../../three' | |||
| export class PointerLockControlsPlugin extends ACameraControlsPlugin { | |||
| public static readonly PluginType = 'PointerLockControlsPlugin' | |||
| readonly controlsKey = 'pointerLock' | |||
| protected _controlsCtor: TControlsCtor = (object, domElement) => new PointerLockControls2(object, !domElement?.ownerDocument ? (domElement || document).documentElement : domElement) | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| import {ACameraControlsPlugin} from '../base/ACameraControlsPlugin' | |||
| import {TControlsCtor} from '../../core' | |||
| import {FirstPersonControls2} from '../../three' | |||
| export class ThreeFirstPersonControlsPlugin extends ACameraControlsPlugin { | |||
| public static readonly PluginType = 'ThreeFirstPersonControlsPlugin' | |||
| readonly controlsKey = 'threeFirstPerson' | |||
| protected _controlsCtor: TControlsCtor = (object, domElement) => new FirstPersonControls2(object, domElement || document.documentElement) | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| import {Euler, EulerOrder, EventDispatcher, MathUtils, Object3D, Quaternion, Vector3} from 'three' | |||
| import {IEvent, now, serialize} from 'ts-browser-helpers' | |||
| import {uiPanelContainer, uiSlider} from 'uiconfig.js' | |||
| import {uiButton, uiPanelContainer, uiSlider} from 'uiconfig.js' | |||
| import {ICameraControls} from '../../core' | |||
| // eslint-disable-next-line @typescript-eslint/naming-convention | |||
| @@ -63,7 +63,15 @@ export class DeviceOrientationControls2 extends EventDispatcher implements ICame | |||
| private _initQuaternion = new Quaternion() | |||
| private _initQuaternionInvert = new Quaternion() | |||
| private _initQuaternionDest = new Quaternion() | |||
| @uiButton('Reset View') | |||
| resetView() { | |||
| (this._initQuaternionDest as any).__init = false | |||
| } | |||
| @uiButton() | |||
| connect() { | |||
| if (this.enabled) return | |||
| this.onScreenOrientationChangeEvent() // run once on load | |||
| @@ -99,7 +107,9 @@ export class DeviceOrientationControls2 extends EventDispatcher implements ICame | |||
| } | |||
| @uiButton() | |||
| disconnect() { | |||
| if (!this.enabled) return | |||
| window.removeEventListener('orientationchange', this.onScreenOrientationChangeEvent) | |||
| window.removeEventListener('deviceorientation', this.onDeviceOrientationChangeEvent) | |||
| @@ -163,6 +173,7 @@ export class DeviceOrientationControls2 extends EventDispatcher implements ICame | |||
| _q2.multiply(_q0.setFromAxisAngle(_zee, -orient)) // adjust for screen orientation | |||
| // debugger | |||
| if (!(this._initQuaternionDest as any).__init) { | |||
| this._initQuaternionDest.copy(_q2).invert() | |||
| ;(this._initQuaternionDest as any).__init = true | |||
| @@ -171,9 +182,9 @@ export class DeviceOrientationControls2 extends EventDispatcher implements ICame | |||
| _q2.premultiply(this._initQuaternionDest) | |||
| const mTime = 1 / 60 | |||
| // this.object.quaternion.multiply(this._initQuaternionInvert) | |||
| this.object.quaternion.multiply(this._initQuaternionInvert) | |||
| this.object.quaternion.slerp(_q2, this.dampingFactor / (Math.min(1, time - this._lastTime) / mTime)) | |||
| // this.object.quaternion.multiply(this._initQuaternion) | |||
| this.object.quaternion.multiply(this._initQuaternion) | |||
| // console.log(time - this._lastTime, mTime) | |||
| this._lastTime = time | |||
| @@ -1,6 +1,7 @@ | |||
| import {MathUtils, Object3D, Spherical, Vector3} from 'three' | |||
| import {IEvent, now, serialize, SimpleEventDispatcher} from 'ts-browser-helpers' | |||
| import {EventDispatcher, MathUtils, Object3D, Spherical, Vector3} from 'three' | |||
| import {IEvent, now, serialize} from 'ts-browser-helpers' | |||
| import {uiFolderContainer, uiInput, uiToggle} from 'uiconfig.js' | |||
| import {ICameraControls} from '../../core' | |||
| // eslint-disable-next-line @typescript-eslint/naming-convention | |||
| const _lookDirection = new Vector3() | |||
| @@ -13,7 +14,7 @@ const _target = new Vector3() | |||
| const _changeEvent: IEvent<'change'> = {type: 'change'} | |||
| @uiFolderContainer('First Person Controls') | |||
| export class FirstPersonControls2 extends SimpleEventDispatcher<'change'> { | |||
| export class FirstPersonControls2 extends EventDispatcher implements ICameraControls<'change'> { | |||
| readonly object: Object3D | |||
| readonly domElement: HTMLElement | Document | |||