提交 312ee7b0 编写于 作者: M Mr.doob

Editor: Rescued VR mode.

上级 cb99e7e0
......@@ -26,6 +26,11 @@ function Editor() {
startPlayer: new Signal(),
stopPlayer: new Signal(),
// vr
toggleVR: new Signal(),
exitedVR: new Signal(),
// notifications
editorCleared: new Signal(),
......
......@@ -46,6 +46,31 @@ function MenubarView( editor ) {
} );
options.add( option );
// VR (Work in progress)
if ( 'xr' in navigator ) {
navigator.xr.isSessionSupported( 'immersive-vr' )
.then( function ( supported ) {
if ( supported ) {
var option = new UIRow();
option.setClass( 'option' );
option.setTextContent( 'VR' );
option.onClick( function () {
editor.signals.toggleVR.dispatch();
} );
options.add( option );
}
} );
}
return container;
}
......
import * as THREE from '../../build/three.module.js';
import { HTMLMesh } from './libs/three.html.js';
import { XRControllerModelFactory } from '../../examples/jsm/webxr/XRControllerModelFactory.js';
class VR {
constructor( editor ) {
const signals = editor.signals;
let group = null;
let renderer = null;
this.currentSession = null;
const onSessionStarted = async ( session ) => {
if ( group === null ) {
group = new THREE.Group();
editor.sceneHelpers.add( group );
const mesh = new HTMLMesh( document.getElementById( 'sidebar' ) );
mesh.position.set( 1, 1.5, 0 );
mesh.rotation.y = - 0.5;
group.add( mesh );
//
const controllerModelFactory = new XRControllerModelFactory();
const controllerGrip1 = renderer.xr.getControllerGrip( 0 );
controllerGrip1.add( controllerModelFactory.createControllerModel( controllerGrip1 ) );
group.add( controllerGrip1 );
const controllerGrip2 = renderer.xr.getControllerGrip( 1 );
controllerGrip2.add( controllerModelFactory.createControllerModel( controllerGrip2 ) );
group.add( controllerGrip2 );
}
group.visible = true;
this.currentSession = session;
this.currentSession.addEventListener( 'end', onSessionEnded );
await renderer.xr.setSession( this.currentSession );
}
const onSessionEnded = async () => {
group.visible = false;
this.currentSession.removeEventListener( 'end', onSessionEnded );
this.currentSession = null;
await renderer.xr.setSession( null );
signals.exitedVR.dispatch();
};
signals.toggleVR.add( () => {
if ( this.currentSession === null ) {
const sessionInit = { optionalFeatures: [ 'local-floor', 'bounded-floor' ] };
navigator.xr.requestSession( 'immersive-vr', sessionInit ).then( onSessionStarted );
} else {
this.currentSession.end();
}
} );
signals.rendererChanged.add( ( value ) => {
renderer = value;
renderer.xr.enabled = true;
} );
}
}
export { VR };
......@@ -9,6 +9,7 @@ import { EditorControls } from './EditorControls.js';
import { ViewportCamera } from './Viewport.Camera.js';
import { ViewportInfo } from './Viewport.Info.js';
import { ViewHelper } from './Viewport.ViewHelper.js';
import { VR } from './Viewport.VR.js';
import { SetPositionCommand } from './commands/SetPositionCommand.js';
import { SetRotationCommand } from './commands/SetRotationCommand.js';
......@@ -42,6 +43,7 @@ function Viewport( editor ) {
var grid = new THREE.GridHelper( 30, 30, 0x444444, 0x888888 );
var viewHelper = new ViewHelper( camera, container );
var vr = new VR( editor );
//
......@@ -335,6 +337,7 @@ function Viewport( editor ) {
if ( renderer !== null ) {
renderer.setAnimationLoop( null );
renderer.dispose();
pmremGenerator.dispose();
pmremTexture = null;
......@@ -345,6 +348,7 @@ function Viewport( editor ) {
renderer = newRenderer;
renderer.setAnimationLoop( animate );
renderer.setClearColor( 0xaaaaaa );
if ( window.matchMedia ) {
......@@ -659,6 +663,8 @@ function Viewport( editor ) {
} );
signals.exitedVR.add( render );
//
signals.windowResize.add( function () {
......@@ -695,8 +701,6 @@ function Viewport( editor ) {
function animate() {
requestAnimationFrame( animate );
var mixer = editor.mixer;
var delta = clock.getDelta();
......@@ -716,12 +720,16 @@ function Viewport( editor ) {
}
if ( vr.currentSession !== null ) {
needsUpdate = true;
}
if ( needsUpdate === true ) render();
}
requestAnimationFrame( animate );
//
var startTime = 0;
......@@ -743,7 +751,7 @@ function Viewport( editor ) {
renderer.autoClear = false;
if ( showSceneHelpers === true ) renderer.render( sceneHelpers, camera );
viewHelper.render( renderer );
if ( vr.currentSession === null ) viewHelper.render( renderer );
renderer.autoClear = true;
}
......
import {
CanvasTexture,
Mesh,
MeshBasicMaterial,
PlaneGeometry,
sRGBEncoding
} from '../../../build/three.module.js';
class HTMLMesh extends Mesh {
constructor( dom ) {
const texture = new HTMLTexture( dom );
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 );
}
}
class HTMLTexture extends CanvasTexture {
constructor( dom ) {
super( html2canvas( dom ) );
this.dom = dom;
this.encoding = sRGBEncoding;
this.anisotropy = 16;
}
update() {
this.image = html2canvas( this.dom );
this.needsUpdate = true;
}
}
//
function html2canvas( element ) {
var range = document.createRange();
function Clipper( context ) {
var clips = [];
var isClipping = false;
function doClip() {
if ( isClipping ) {
isClipping = false;
context.restore();
}
if ( clips.length === 0 ) return;
var minX = - Infinity, minY = - Infinity;
var maxX = Infinity, maxY = Infinity;
for ( var i = 0; i < clips.length; i ++ ) {
var clip = clips[ i ];
minX = Math.max( minX, clip.x );
minY = Math.max( minY, clip.y );
maxX = Math.min( maxX, clip.x + clip.width );
maxY = Math.min( maxY, clip.y + clip.height );
}
context.save();
context.beginPath();
context.rect( minX, minY, maxX - minX, maxY - minY );
context.clip();
isClipping = true;
}
return {
add: function ( clip ) {
clips.push( clip );
doClip();
},
remove: function () {
clips.pop();
doClip();
}
};
}
function drawText( style, x, y, string ) {
if ( string !== '' ) {
context.font = style.fontSize + ' ' + style.fontFamily;
context.textBaseline = 'top';
context.fillStyle = style.color;
context.fillText( string, x, y );
}
}
function drawBorder( style, which, x, y, width, height ) {
var borderWidth = style[ which + 'Width' ];
var borderStyle = style[ which + 'Style' ];
var borderColor = style[ which + 'Color' ];
if ( borderWidth !== '0px' && borderStyle !== 'none' && borderColor !== 'transparent' && borderColor !== 'rgba(0, 0, 0, 0)' ) {
context.strokeStyle = borderColor;
context.beginPath();
context.moveTo( x, y );
context.lineTo( x + width, y + height );
context.stroke();
}
}
function drawElement( element, style ) {
var x = 0, y = 0, width = 0, height = 0;
if ( element.nodeType === 3 ) {
// text
range.selectNode( element );
var rect = range.getBoundingClientRect();
x = rect.left - offset.left - 0.5;
y = rect.top - offset.top - 0.5;
width = rect.width;
height = rect.height;
drawText( style, x, y, element.nodeValue.trim() );
} else {
if ( element.style.display === 'none' ) return;
var rect = element.getBoundingClientRect();
x = rect.left - offset.left - 0.5;
y = rect.top - offset.top - 0.5;
width = rect.width;
height = rect.height;
style = window.getComputedStyle( element );
var backgroundColor = style.backgroundColor;
if ( backgroundColor !== 'transparent' && backgroundColor !== 'rgba(0, 0, 0, 0)' ) {
context.fillStyle = backgroundColor;
context.fillRect( x, y, width, height );
}
drawBorder( style, 'borderTop', x, y, width, 0 );
drawBorder( style, 'borderLeft', x, y, 0, height );
drawBorder( style, 'borderBottom', x, y + height, width, 0 );
drawBorder( style, 'borderRight', x + width, y, 0, height );
if ( element.type === 'color' || element.type === 'text' ) {
clipper.add( { x: x, y: y, width: width, height: height } );
drawText( style, x + parseInt( style.paddingLeft ), y + parseInt( style.paddingTop ), element.value );
clipper.remove();
}
}
/*
// debug
context.strokeStyle = '#' + Math.random().toString( 16 ).slice( - 3 );
context.strokeRect( x - 0.5, y - 0.5, width + 1, height + 1 );
*/
var isClipping = style.overflow === 'auto' || style.overflow === 'hidden';
if ( isClipping ) clipper.add( { x: x, y: y, width: width, height: height } );
for ( var i = 0; i < element.childNodes.length; i ++ ) {
drawElement( element.childNodes[ i ], style );
}
if ( isClipping ) clipper.remove();
}
var offset = element.getBoundingClientRect();
var canvas = document.createElement( 'canvas' );
canvas.width = offset.width;
canvas.height = offset.height;
var context = canvas.getContext( '2d'/*, { alpha: false }*/ );
var clipper = new Clipper( context );
console.time( 'drawElement' );
drawElement( element );
console.timeEnd( 'drawElement' );
return canvas;
}
export { HTMLMesh, HTMLTexture };
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册