Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
<!DOCTYPE html>
<html lang="en">
<head>
<title>XR</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
<style type="text/css">
body {
font-family: sans-serif;
margin: 0px;
background-color: #000000;
overflow: hidden;
text-align: center;
}
#info {
color: #fff;
position: absolute;
top: 10px;
width: 100%;
}
.ontop {
position:absolute;
top:20px;
right:20px;
z-index:10;
}
</style>
</head>
<body>
<div id="info">ThreeJS simple curved plane - Use PointerLock and WSAD, click to enter ! (<a href="https://github.com/eviltik/threejs-boilerplate-webpack5/blob/master/src/static/curved-rectangle/index.html">Source</a>).</div>
<video id="video" style="display:none"></video>
<script type="module">
import * as THREE from 'https://cdn.skypack.dev/three';
import Stats from 'https://cdn.skypack.dev/three/examples/jsm/libs/stats.module.js';
import { OrbitControls } from 'https://cdn.skypack.dev/three/examples/jsm/controls/OrbitControls.js';
import { PointerLockControls } from 'https://cdn.skypack.dev/three/examples/jsm/controls/PointerLockControls.js';
import { GUI } from 'https://cdn.skypack.dev/three/examples/jsm/libs/dat.gui.module.js';
import { Sky } from 'https://cdn.skypack.dev/three/examples/jsm/objects/Sky.js';
const cameraWorldPosition = new THREE.Vector3();
const cameraWorldDirection = new THREE.Vector3();
const dollyVelocity = new THREE.Vector3();
const dollyDirection = new THREE.Vector3();
let camera, scene, renderer, stats, controls, loader, raycaster;
let prevTime = performance.now(), time, delta, video, curvedRectangle;
let moveForward = false;
let moveBackward = false;
let moveLeft = false;
let moveRight = false;
let canJump = false;
init();
animate();
function createScene() {
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 0.1, 1000 );
scene = new THREE.Scene();
scene.background = new THREE.Color(0x88ccff);
scene.add( camera );
scene.add( new THREE.AxesHelper(5));
}
function createLights() {
const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x000000, 1 );
scene.add( hemiLight );
const dirLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
dirLight.position.set( 20, 20, 20 );
scene.add( dirLight );
}
function createGrid() {
const helper = new THREE.GridHelper( 20, 20);
helper.position.y = -0.0001;
//helper.rotateX(Math.PI/2);
scene.add( helper );
}
function createSky() {
const sky = new Sky();
sky.scale.setScalar( 450000 );
const sun = new THREE.Vector3();
const effectController = {
turbidity: 10,
rayleigh: 3,
mieCoefficient: 0.005,
mieDirectionalG: 0.7,
elevation: 0.1,
azimuth: 180,
exposure: renderer.toneMappingExposure
};
const uniforms = sky.material.uniforms;
uniforms[ 'turbidity' ].value = effectController.turbidity;
uniforms[ 'rayleigh' ].value = effectController.rayleigh;
uniforms[ 'mieCoefficient' ].value = effectController.mieCoefficient;
uniforms[ 'mieDirectionalG' ].value = effectController.mieDirectionalG;
const phi = THREE.MathUtils.degToRad( 90 - effectController.elevation );
const theta = THREE.MathUtils.degToRad( effectController.azimuth );
sun.setFromSphericalCoords( 1, phi, theta );
uniforms[ 'sunPosition' ].value.copy( sun );
scene.add(sky);
}
function createRenderer() {
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function createControls() {
/*
controls = new OrbitControls( camera, renderer.domElement );
controls.minDistance = 1;
controls.maxDistance = 200;
controls.enableDamping = true;
*/
controls = new PointerLockControls(camera, document.body);
renderer.domElement.addEventListener( 'click', () => {
if ( controls.isLocked) return;
controls.lock();
} );
document.addEventListener( 'keydown', onKeyDown, true );
document.addEventListener( 'keyup', onKeyUp, true );
camera.position.set(0, 1.8, 8);
camera.getWorldPosition(cameraWorldPosition);
camera.getWorldDirection(cameraWorldDirection);
}
function createStats() {
stats = new Stats();
document.body.appendChild( stats.dom );
}
function createRaycaster() {
raycaster = new THREE.Raycaster(cameraWorldPosition, cameraWorldDirection);
//raycaster = new THREE.Raycaster( new THREE.Vector3(), new THREE.Vector3( 0, - 1, 0 ), 0, 10 );
raycaster.set(cameraWorldPosition, cameraWorldDirection);
}
function onWebcamInput(stream) {
video.srcObject = stream;
video.play();
}
function onWebcamInputError(err) {
alert( 'Unable to access the camera/webcam: ' + err.message );
}
function initWebcamInput() {
if ( !navigator.mediaDevices || !navigator.mediaDevices.getUserMedia ) {
alert( 'MediaDevices interface not available.' );
return;
}
const constraints = {
video: {
width: 1280,
height: 720,
facingMode: 'user'
}
};
navigator
.mediaDevices
.getUserMedia( constraints )
.then( onWebcamInput )
.catch(onWebcamInputError );
}
function planeCurve(g, z){
if (!g instanceof THREE.PlaneGeometry) {
throw new Error('first argument of planeCurve() MUST be an instance of PlaneGeometry');
return;
}
let p = g.parameters;
let hw = p.width * 0.5;
let a = new THREE.Vector2(-hw, 0);
let b = new THREE.Vector2(0, z);
let c = new THREE.Vector2(hw, 0);
let ab = new THREE.Vector2().subVectors(a, b);
let bc = new THREE.Vector2().subVectors(b, c);
let ac = new THREE.Vector2().subVectors(a, c);
let r = (ab.length() * bc.length() * ac.length()) / (2 * Math.abs(ab.cross(ac)));
let center = new THREE.Vector2(0, z - r);
let baseV = new THREE.Vector2().subVectors(a, center);
let baseAngle = baseV.angle() - (Math.PI * 0.5);
let arc = baseAngle * 2;
let uv = g.attributes.uv;
let pos = g.attributes.position;
let mainV = new THREE.Vector2();
for (let i = 0; i < uv.count; i++) {
let uvRatio = 1 - uv.getX(i);
let y = pos.getY(i);
mainV.copy(c).rotateAround(center, (arc * uvRatio));
pos.setXYZ(i, mainV.x, y, -mainV.y);
}
pos.needsUpdate = true;
}
function createCurvedPlane() {
video = document.getElementById( 'video' );
const texture = new THREE.VideoTexture( video );
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.format = THREE.RGBFormat;
const geometry = new THREE.PlaneGeometry(16/10, 9/10, 8, 1);
const positions = geometry.attributes.position;
planeCurve(geometry, 0.1);
const material = new THREE.MeshBasicMaterial( { map: texture } );
material.side = THREE.DoubleSide;
const mesh = new THREE.Mesh( geometry, material );
mesh.position.y = 1.8;
scene.add(mesh);
const helper = new THREE.BoxHelper( mesh, 0xffff00 );
scene.add(helper);
curvedRectangle = mesh;
}
function installListeners() {
window.addEventListener( 'resize', onWindowResize );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onKeyDown( event ) {
switch ( event.code ) {
case 'ArrowUp':
case 'KeyW':
moveForward = true;
break;
case 'ArrowLeft':
case 'KeyA':
moveLeft = true;
break;
case 'ArrowDown':
case 'KeyS':
moveBackward = true;
break;
case 'ArrowRight':
case 'KeyD':
moveRight = true;
break;
case 'Space':
if ( canJump === true ) dollyVelocity.y += 150;
canJump = false;
break;
}
}
function onKeyUp( event ) {
switch ( event.code ) {
case 'ArrowUp':
case 'KeyW':
moveForward = false;
break;
case 'ArrowLeft':
case 'KeyA':
moveLeft = false;
break;
case 'ArrowDown':
case 'KeyS':
moveBackward = false;
break;
case 'ArrowRight':
case 'KeyD':
moveRight = false;
break;
}
};
function renderControls() {
if ( !controls.isLocked) return;
time = performance.now();
camera.getWorldPosition(cameraWorldPosition);
camera.getWorldDirection(cameraWorldDirection);
raycaster.set(cameraWorldPosition, cameraWorldDirection);
const intersections = raycaster.intersectObjects( scene.children );
const onObject = intersections.length > 0;
delta = ( time - prevTime ) / 1000;
dollyVelocity.x -= dollyVelocity.x * 10.0 * delta;
dollyVelocity.z -= dollyVelocity.z * 10.0 * delta;
dollyVelocity.y -= 9.8 * 100.0 * delta; // 100.0 = mass
dollyDirection.z = Number( moveForward ) - Number( moveBackward );
dollyDirection.x = Number( moveRight ) - Number( moveLeft );
dollyDirection.normalize(); // this ensures consistent movements in all directions
if ( moveForward || moveBackward ) dollyVelocity.z -= dollyDirection.z * 50.0 * delta;
if ( moveLeft || moveRight ) dollyVelocity.x -= dollyDirection.x * 50.0 * delta;
if ( onObject === true ) {
dollyVelocity.y = Math.max( 0, dollyVelocity.y );
canJump = true;
}
controls.moveRight( - dollyVelocity.x * delta );
controls.moveForward( - dollyVelocity.z * delta );
controls.getObject().position.y += ( dollyVelocity.y * delta/10 ); // new behavior
if ( controls.getObject().position.y < 1.8 ) {
dollyVelocity.y = 0;
controls.getObject().position.y = 1.8;
canJump = true;
}
prevTime = time;
}
function init() {
createScene();
createLights();
createGrid();
createRenderer();
createSky();
createControls();
createRaycaster();
createStats();
createCurvedPlane();
installListeners();
initWebcamInput();
}
function animate() {
requestAnimationFrame( animate );
if (controls && controls.update) controls.update(); // to support damping
renderControls();
renderer.render( scene, camera );
stats.update();
}
</script>
</body>
</html>