未验证 提交 e9f910c8 编写于 作者: M Mr.doob 提交者: GitHub

Bring dat.gui inside vr (#21700)

* Bring dat.gui inside vr

* Example code clean up.

* Refactored interaction code.

* InteractiveGroup: Added pointer events support

* Improved webxr_vr_sandbox scene
上级 0a5ce113
import * as THREE from '../../build/three.module.js';
import { HTMLMesh } from './libs/three.html.js';
import { HTMLMesh } from '../../examples/jsm/interactive/HTMLMesh.js';
import { InteractiveGroup } from '../../examples/jsm/interactive/InteractiveGroup.js';
import { XRControllerModelFactory } from '../../examples/jsm/webxr/XRControllerModelFactory.js';
......@@ -29,31 +30,29 @@ class VR {
if ( group === null ) {
group = new THREE.Group();
group = new InteractiveGroup( renderer );
editor.sceneHelpers.add( group );
const mesh = new HTMLMesh( sidebar );
mesh.position.set( 1, 1.5, - 0.5 );
mesh.rotation.y = - 0.5;
mesh.scale.setScalar( 2 );
group.add( mesh );
intersectables.push( mesh );
// controllers
const controller1 = renderer.xr.getController( 0 );
controller1.addEventListener( 'select', onSelect );
group.add( controller1 );
const controller2 = renderer.xr.getController( 1 );
controller2.addEventListener( 'selectstart', onSelect );
group.add( controller2 );
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints( [ new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, - 5 ) ] );
const controller1 = renderer.xr.getController( 0 );
controller1.add( new THREE.Line( geometry ) );
group.add( controller1 );
const controller2 = renderer.xr.getController( 1 );
controller2.add( new THREE.Line( geometry ) );
group.add( controller2 );
//
......@@ -101,41 +100,6 @@ class VR {
};
//
function onSelect( event ) {
const controller = event.target;
const intersections = getIntersections( controller );
if ( intersections.length > 0 ) {
const intersection = intersections[ 0 ];
const object = intersection.object;
const uv = intersection.uv;
object.material.map.click( uv.x, 1 - uv.y );
}
}
const raycaster = new THREE.Raycaster();
const tempMatrix = new THREE.Matrix4();
function getIntersections( controller ) {
tempMatrix.identity().extractRotation( controller.matrixWorld );
raycaster.ray.origin.setFromMatrixPosition( controller.matrixWorld );
raycaster.ray.direction.set( 0, 0, - 1 ).applyMatrix4( tempMatrix );
return raycaster.intersectObjects( intersectables );
}
// signals
signals.toggleVR.add( () => {
......
......@@ -57,6 +57,9 @@ const assets = [
'../examples/jsm/curves/NURBSCurve.js',
'../examples/jsm/curves/NURBSUtils.js',
'../examples/jsm/interactive/HTMLMesh.js',
'../examples/jsm/interactive/InteractiveGroup.js',
'../examples/jsm/environments/RoomEnvironment.js',
'../examples/jsm/exporters/ColladaExporter.js',
......@@ -108,7 +111,6 @@ const assets = [
'./js/libs/tern-threejs/threejs.js',
'./js/libs/signals.min.js',
'./js/libs/three.html.js',
'./js/libs/ui.js',
'./js/libs/ui.three.js',
......
import * as THREE from '../../../build/three.module.js';
import {
CanvasTexture,
LinearFilter,
Mesh,
MeshBasicMaterial,
PlaneGeometry,
sRGBEncoding
} from '../../../build/three.module.js';
class HTMLMesh extends THREE.Mesh {
class HTMLMesh extends Mesh {
constructor( dom ) {
const texture = new HTMLTexture( dom );
const geometry = new THREE.PlaneGeometry( texture.image.width * 0.002, texture.image.height * 0.002 );
const material = new THREE.MeshBasicMaterial( { map: texture, toneMapped: false } );
const geometry = new PlaneGeometry( texture.image.width * 0.001, texture.image.height * 0.001 );
const material = new MeshBasicMaterial( { map: texture, toneMapped: false } );
super( geometry, material );
function onEvent( event ) {
material.map.dispatchEvent( event );
}
this.addEventListener( 'mousedown', onEvent );
this.addEventListener( 'mousemove', onEvent );
this.addEventListener( 'mouseup', onEvent );
this.addEventListener( 'click', onEvent );
}
}
class HTMLTexture extends THREE.CanvasTexture {
class HTMLTexture extends CanvasTexture {
constructor( dom ) {
......@@ -24,15 +42,15 @@ class HTMLTexture extends THREE.CanvasTexture {
this.dom = dom;
this.anisotropy = 16;
this.encoding = THREE.sRGBEncoding;
this.minFilter = THREE.LinearFilter;
this.magFilter = THREE.LinearFilter;
this.encoding = sRGBEncoding;
this.minFilter = LinearFilter;
this.magFilter = LinearFilter;
}
click( x, y ) {
dispatchEvent( event ) {
htmlclick( this.dom, x, y );
htmlevent( this.dom, event.type, event.data.x, event.data.y );
this.update();
......@@ -93,12 +111,14 @@ function html2canvas( element ) {
}
return {
add: function ( clip ) {
clips.push( clip );
doClip();
},
remove: function () {
clips.pop();
......@@ -235,26 +255,25 @@ function html2canvas( element ) {
var clipper = new Clipper( context );
console.time( 'drawElement' );
// console.time( 'drawElement' );
drawElement( element );
console.timeEnd( 'drawElement' );
// console.timeEnd( 'drawElement' );
return canvas;
}
function htmlclick( element, x, y ) {
function htmlevent( element, event, x, y ) {
/*
const mouseEventInit = {
clientX: ( x * element.offsetWidth ) + element.offsetLeft,
clientY: ( y * element.offsetHeight ) + element.offsetTop,
view: element.ownerDocument.defaultView
};
element.dispatchEvent( new MouseEvent( 'click', mouseEventInit ) );
*/
window.dispatchEvent( new MouseEvent( event, mouseEventInit ) );
const rect = element.getBoundingClientRect();
......@@ -269,7 +288,7 @@ function htmlclick( element, x, y ) {
if ( x > rect.left && x < rect.right && y > rect.top && y < rect.bottom ) {
element.click();
element.dispatchEvent( new MouseEvent( event, mouseEventInit ) );
}
......@@ -287,4 +306,4 @@ function htmlclick( element, x, y ) {
}
export { HTMLMesh, HTMLTexture };
export { HTMLMesh };
import {
Group,
Matrix4,
Raycaster,
Vector2
} from '../../../build/three.module.js';
const _pointer = new Vector2();
const _event = { type: '', data: _pointer };
class InteractiveGroup extends Group {
constructor( renderer, camera ) {
super();
const scope = this;
const raycaster = new Raycaster();
const tempMatrix = new Matrix4();
// Pointer Events
const element = renderer.domElement;
function onPointerEvent( event ) {
event.stopPropagation();
_pointer.x = ( event.clientX / element.clientWidth ) * 2 - 1;
_pointer.y = - ( event.clientY / element.clientHeight ) * 2 + 1;
raycaster.setFromCamera( _pointer, camera );
const intersects = raycaster.intersectObjects( scope.children );
if ( intersects.length > 0 ) {
const intersection = intersects[ 0 ];
const object = intersection.object;
const uv = intersection.uv;
_event.type = event.type;
_event.data.set( uv.x, 1 - uv.y );
object.dispatchEvent( _event );
}
}
element.addEventListener( 'pointerdown', onPointerEvent );
element.addEventListener( 'pointerup', onPointerEvent );
element.addEventListener( 'pointermove', onPointerEvent );
element.addEventListener( 'mousedown', onPointerEvent );
element.addEventListener( 'mouseup', onPointerEvent );
element.addEventListener( 'mousemove', onPointerEvent );
element.addEventListener( 'click', onPointerEvent );
// WebXR Controller Events
// TODO: Dispatch pointerevents too
const events = {
'move': 'mousemove',
'select': 'click',
'selectstart': 'mousedown',
'selectend': 'mouseup'
};
function onXRControllerEvent( event ) {
const controller = event.target;
tempMatrix.identity().extractRotation( controller.matrixWorld );
raycaster.ray.origin.setFromMatrixPosition( controller.matrixWorld );
raycaster.ray.direction.set( 0, 0, - 1 ).applyMatrix4( tempMatrix );
const intersections = raycaster.intersectObjects( scope.children );
if ( intersections.length > 0 ) {
const intersection = intersections[ 0 ];
const object = intersection.object;
const uv = intersection.uv;
_event.type = events[ event.type ];
_event.data.set( uv.x, 1 - uv.y );
object.dispatchEvent( _event );
}
}
const controller1 = renderer.xr.getController( 0 );
controller1.addEventListener( 'move', onXRControllerEvent );
controller1.addEventListener( 'select', onXRControllerEvent );
controller1.addEventListener( 'selectstart', onXRControllerEvent );
controller1.addEventListener( 'selectend', onXRControllerEvent );
const controller2 = renderer.xr.getController( 1 );
controller2.addEventListener( 'move', onXRControllerEvent );
controller2.addEventListener( 'select', onXRControllerEvent );
controller2.addEventListener( 'selectstart', onXRControllerEvent );
controller2.addEventListener( 'selectend', onXRControllerEvent );
}
}
export { InteractiveGroup };
......@@ -15,10 +15,24 @@
import { Reflector } from './jsm/objects/Reflector.js';
import { VRButton } from './jsm/webxr/VRButton.js';
let camera, scene, renderer;
import { HTMLMesh } from './jsm/interactive/HTMLMesh.js';
import { InteractiveGroup } from './jsm/interactive/InteractiveGroup.js';
import { XRControllerModelFactory } from './jsm/webxr/XRControllerModelFactory.js';
import { GUI } from './jsm/libs/dat.gui.module.js';
let camera, scene, renderer;
let reflector;
const parameters = {
radius: 0.5,
tube: 0.2,
tubularSegments: 150,
radialSegments: 20,
p: 2,
q: 3
};
init();
animate();
......@@ -32,25 +46,27 @@
scene.background = background;
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 10 );
camera.position.set( 0, 1.6, 2 );
camera.position.set( 0, 1.6, 1.5 );
//
const torusGeometry = new THREE.TorusKnotGeometry( 0.4, 0.15, 150, 20 );
const torusGeometry = new THREE.TorusKnotGeometry( ...Object.values( parameters ) );
const torusMaterial = new THREE.MeshStandardMaterial( { roughness: 0.01, metalness: 0.2, envMap: background } );
const torus = new THREE.Mesh( torusGeometry, torusMaterial );
torus.position.y = 0.75;
torus.name = 'torus';
torus.position.y = 1.25;
torus.position.z = - 2;
torus.castShadow = true;
torus.receiveShadow = true;
scene.add( torus );
const boxGeometry = new THREE.BoxGeometry( 1.5, 0.1, 1.5 );
const boxMaterial = new THREE.MeshPhongMaterial();
const box = new THREE.Mesh( boxGeometry, boxMaterial );
box.position.y = - 0.2;
box.position.z = - 2;
box.castShadow = true;
box.receiveShadow = true;
scene.add( box );
const cylinderGeometry = new THREE.CylinderGeometry( 1, 1, 0.1, 50 );
const cylinderMaterial = new THREE.MeshPhongMaterial();
const cylinder = new THREE.Mesh( cylinderGeometry, cylinderMaterial );
cylinder.position.z = - 2;
cylinder.castShadow = true;
cylinder.receiveShadow = true;
scene.add( cylinder );
const light1 = new THREE.DirectionalLight( 0x8800ff );
light1.position.set( - 1, 1.5, - 1.5 );
......@@ -90,17 +106,17 @@
//
reflector = new Reflector( new THREE.PlaneGeometry( 1.4, 1.4 ), {
reflector = new Reflector( new THREE.PlaneGeometry( 2, 2 ), {
textureWidth: window.innerWidth * window.devicePixelRatio,
textureHeight: window.innerHeight * window.devicePixelRatio
} );
reflector.position.x = 1;
reflector.position.y = 0.5;
reflector.position.y = 1.25;
reflector.position.z = - 3;
reflector.rotation.y = - Math.PI / 4;
scene.add( reflector );
const frameGeometry = new THREE.BoxGeometry( 1.5, 1.5, 0.1 );
const frameGeometry = new THREE.BoxGeometry( 2.1, 2.1, 0.1 );
const frameMaterial = new THREE.MeshPhongMaterial();
const frame = new THREE.Mesh( frameGeometry, frameMaterial );
frame.position.z = - 0.07;
......@@ -120,9 +136,61 @@
document.body.appendChild( VRButton.createButton( renderer ) );
window.addEventListener( 'resize', onWindowResize );
//
window.addEventListener( 'resize', onWindowResize );
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints( [ new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, - 5 ) ] );
const controller1 = renderer.xr.getController( 0 );
controller1.add( new THREE.Line( geometry ) );
scene.add( controller1 );
const controller2 = renderer.xr.getController( 1 );
controller2.add( new THREE.Line( geometry ) );
scene.add( controller2 );
//
const controllerModelFactory = new XRControllerModelFactory();
const controllerGrip1 = renderer.xr.getControllerGrip( 0 );
controllerGrip1.add( controllerModelFactory.createControllerModel( controllerGrip1 ) );
scene.add( controllerGrip1 );
const controllerGrip2 = renderer.xr.getControllerGrip( 1 );
controllerGrip2.add( controllerModelFactory.createControllerModel( controllerGrip2 ) );
scene.add( controllerGrip2 );
// GUI
function onChange() {
torus.geometry.dispose();
torus.geometry = new THREE.TorusKnotGeometry( ...Object.values( parameters ) );
}
const gui = new GUI( { width: 300 } );
gui.add( parameters, 'radius', 0.0, 1.0 ).onChange( onChange );
gui.add( parameters, 'tube', 0.0, 1.0 ).onChange( onChange );
gui.add( parameters, 'tubularSegments', 10, 150, 1 ).onChange( onChange );
gui.add( parameters, 'radialSegments', 2, 20, 1 ).onChange( onChange );
gui.add( parameters, 'p', 1, 10, 1 ).onChange( onChange );
gui.add( parameters, 'q', 0, 10, 1 ).onChange( onChange );
gui.domElement.style.visibility = 'hidden';
const group = new InteractiveGroup( renderer, camera );
scene.add( group );
const mesh = new HTMLMesh( gui.domElement );
mesh.position.x = - 0.75;
mesh.position.y = 1.5;
mesh.position.z = - 0.5;
mesh.rotation.y = Math.PI / 4;
mesh.scale.setScalar( 2 );
group.add( mesh );
}
......@@ -144,7 +212,7 @@
function render() {
const time = performance.now() * 0.0002;
const torus = scene.children[ 0 ];
const torus = scene.getObjectByName( 'torus' );
torus.rotation.x = time * 2;
torus.rotation.y = time * 5;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册