Threejs Sprite raycasting not consistent - three.js

I'm experiencing very poor raycasting results on sprites. I would say that it works best when the sprites are centred but then activation is not consistent on subsequent events.
var touchCounter=0;
function onDocumentMouseDown( event )
{
touchCounter ++;
$('#footer .social h2').text("event " + touchCounter);
// update the mouse variable
mouse.x = ( ( event.clientX - renderer.domElement.offsetLeft ) / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( ( event.clientY - renderer.domElement.offsetTop ) / renderer.domElement.height ) * 2 + 1;
mouse.z = 0.75;
Raycasting();
}
function onDocumentTouchStart( event )
{
touchCounter ++;
$('#footer .social h2').text("event " + touchCounter);
if ( event.touches.length == 1 ) {
event.preventDefault();
mouse.x = ( ( event.targetTouches[0].pageX - renderer.domElement.offsetLeft ) / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( ( event.targetTouches[0].pageY - renderer.domElement.offsetTop ) / renderer.domElement.height ) * 2 + 1;
mouse.z = 0.75;
Raycasting();
}
}
function Raycasting() {
var ray = new THREE.Raycaster( camera.position, mouse.sub( camera.position ).normalize() );
var intersects = ray.intersectObjects( targetList );
if ( intersects.length > 0 )
{
if ( intersects[ 0 ].object != INTERSECTED )
{
if ( INTERSECTED )
INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
$('#footer .social h2').text("event " + touchCounter + " and hit");
controls.autoRotate = false;
modal.show();
INTERSECTED = intersects[ 0 ].object;
INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
INTERSECTED.material.color.setHex( 0xffff00 );
}
}
else // there are no intersections
{
controls.autoRotate = true;
modal.hide();
if ( INTERSECTED )
INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
INTERSECTED = null;
}
}
The targetList array the current sprites...
var spriteList = new Array(
"solar",
"wind",
"water");
var spriteMaterial;
for (var i = 0; i < spriteList.length; i++) {
spriteMaterial = THREE.ImageUtils.loadTexture( "assets/models/home/sprites/" + spriteList[i] + ".png", undefined, createHUDSprites );
}
var ind = 0;
function createHUDSprites ( spriteMaterial ) {
var material = new THREE.SpriteMaterial( { map: spriteMaterial } );
var sprite = new THREE.Sprite( material );
var multi = 0.12;
sprite.scale.set( material.map.image.width*multi, material.map.image.height*multi, 1 );
switch(ind) {
case 0:
sprite.position.set(-60,20,-18); //solar
targetList.push(sprite);
break;
case 1:
sprite.position.set(-20,-42,-35); //wind
targetList.push(sprite);
break;
case 2:
sprite.position.set(-20,42,50); //water
targetList.push(sprite);
break;
case 3:
sprite.position.set(50,14,45); //shelter
targetList.push(sprite);
break;
case 4:
sprite.position.set(50,22,-25); //utilities
targetList.push(sprite);
break;
case 5:
sprite.position.set(45,-24,50); //telescope
multi = 0.05;
sprite.scale.set( material.map.image.width*multi, material.map.image.height*multi, 1 );
targetList.push(sprite);
break;
case 6:
sprite.position.set(40,42,-20); //utilities
multi = 0.05;
sprite.scale.set( material.map.image.width*multi, material.map.image.height*multi, 1 );
targetList.push(sprite);
break;
case 7:
sprite.position.set(-40,-5,-50); //utilities
multi = 0.05;
sprite.scale.set( material.map.image.width*multi, material.map.image.height*multi, 1 );
targetList.push(sprite);
break;
default:
sprite.position.set(-20,-42,-35);
console.log("Problem loading sprites");
}
ind++;
scene.add( sprite );
} //end createHudSprites()
Maybe an issue with scaling? or how Im instantiating the THREE.Raycaster object?
You can see an example of my site here http://earthscool.com.au
Threejs R72

Set the ray as global
var ray = new THREE.Raycaster();
Then set the ray as found in the docs
function Raycasting() {
ray.setFromCamera( mouse, camera );
...
Also add proper event handlers as per doc... this one for touch devices
mouse.x = ( event.targetTouches[0].pageX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.targetTouches[0].pageY / window.innerHeight ) * 2 + 1;
mouse.z = 0.5;

Related

How to move Sphere using mouse in three.js (Uncaught TypeError: THREE.OrbitControls is not a constructor)

I was making a sphere that can be moved by a mouse using three.js but the output is just a black screen as shown.
The code I used is:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 0; }
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set( 0, 0, 50 );
camera.lookAt( 0, 0, 0 );
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const geometry = new THREE.SphereGeometry( 15, 32, 16 );
const texture = new THREE.TextureLoader().load( 'https://i.imgur.com/kFoWvzw.jpg' );
const material = new THREE.MeshBasicMaterial( { map: texture } );
const sphere = new THREE.Mesh( geometry, material );
const controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.update();
scene.add( sphere );
function animate()
{
requestAnimationFrame( animate );
sphere.rotation.x += 0.01;
sphere.rotation.y += 0.01;
renderer.render( scene, camera );
};
animate();
</script>
</body>
</html>
The OrbitControls.js I used to supposedly move the Sphere looked like this: The code itself has no problems because it came straight from the three.js documentation.
( function () {
// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
//
// Orbit - left mouse / touch: one-finger move
// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
// Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
const _changeEvent = {
type: 'change'
};
const _startEvent = {
type: 'start'
};
const _endEvent = {
type: 'end'
};
class OrbitControls extends THREE.EventDispatcher {
constructor( object, domElement ) {
super();
if ( domElement === undefined ) console.warn( 'THREE.OrbitControls: The second parameter "domElement" is now mandatory.' );
if ( domElement === document ) console.error( 'THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
this.object = object;
this.domElement = domElement;
this.domElement.style.touchAction = 'none'; // disable touch scroll
// Set to false to disable this control
this.enabled = true; // "target" sets the location of focus, where the object orbits around
this.target = new THREE.Vector3(); // How far you can dolly in and out ( PerspectiveCamera only )
this.minDistance = 0;
this.maxDistance = Infinity; // How far you can zoom in and out ( OrthographicCamera only )
this.minZoom = 0;
this.maxZoom = Infinity; // How far you can orbit vertically, upper and lower limits.
// Range is 0 to Math.PI radians.
this.minPolarAngle = 0; // radians
this.maxPolarAngle = Math.PI; // radians
// How far you can orbit horizontally, upper and lower limits.
// If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI )
this.minAzimuthAngle = - Infinity; // radians
this.maxAzimuthAngle = Infinity; // radians
// Set to true to enable damping (inertia)
// If damping is enabled, you must call controls.update() in your animation loop
this.enableDamping = false;
this.dampingFactor = 0.05; // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
// Set to false to disable zooming
this.enableZoom = true;
this.zoomSpeed = 1.0; // Set to false to disable rotating
this.enableRotate = true;
this.rotateSpeed = 1.0; // Set to false to disable panning
this.enablePan = true;
this.panSpeed = 1.0;
this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up
this.keyPanSpeed = 7.0; // pixels moved per arrow key push
// Set to true to automatically rotate around the target
// If auto-rotate is enabled, you must call controls.update() in your animation loop
this.autoRotate = false;
this.autoRotateSpeed = 2.0; // 30 seconds per orbit when fps is 60
// The four arrow keys
this.keys = {
LEFT: 'ArrowLeft',
UP: 'ArrowUp',
RIGHT: 'ArrowRight',
BOTTOM: 'ArrowDown'
}; // Mouse buttons
this.mouseButtons = {
LEFT: THREE.MOUSE.ROTATE,
MIDDLE: THREE.MOUSE.DOLLY,
RIGHT: THREE.MOUSE.PAN
}; // Touch fingers
this.touches = {
ONE: THREE.TOUCH.ROTATE,
TWO: THREE.TOUCH.DOLLY_PAN
}; // for reset
this.target0 = this.target.clone();
this.position0 = this.object.position.clone();
this.zoom0 = this.object.zoom; // the target DOM element for key events
this._domElementKeyEvents = null; //
// public methods
//
this.getPolarAngle = function () {
return spherical.phi;
};
this.getAzimuthalAngle = function () {
return spherical.theta;
};
this.getDistance = function () {
return this.object.position.distanceTo( this.target );
};
this.listenToKeyEvents = function ( domElement ) {
domElement.addEventListener( 'keydown', onKeyDown );
this._domElementKeyEvents = domElement;
};
this.saveState = function () {
scope.target0.copy( scope.target );
scope.position0.copy( scope.object.position );
scope.zoom0 = scope.object.zoom;
};
this.reset = function () {
scope.target.copy( scope.target0 );
scope.object.position.copy( scope.position0 );
scope.object.zoom = scope.zoom0;
scope.object.updateProjectionMatrix();
scope.dispatchEvent( _changeEvent );
scope.update();
state = STATE.NONE;
}; // this method is exposed, but perhaps it would be better if we can make it private...
this.update = function () {
const offset = new THREE.Vector3(); // so camera.up is the orbit axis
const quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
const quatInverse = quat.clone().invert();
const lastPosition = new THREE.Vector3();
const lastQuaternion = new THREE.Quaternion();
const twoPI = 2 * Math.PI;
return function update() {
const position = scope.object.position;
offset.copy( position ).sub( scope.target ); // rotate offset to "y-axis-is-up" space
offset.applyQuaternion( quat ); // angle from z-axis around y-axis
spherical.setFromVector3( offset );
if ( scope.autoRotate && state === STATE.NONE ) {
rotateLeft( getAutoRotationAngle() );
}
if ( scope.enableDamping ) {
spherical.theta += sphericalDelta.theta * scope.dampingFactor;
spherical.phi += sphericalDelta.phi * scope.dampingFactor;
} else {
spherical.theta += sphericalDelta.theta;
spherical.phi += sphericalDelta.phi;
} // restrict theta to be between desired limits
let min = scope.minAzimuthAngle;
let max = scope.maxAzimuthAngle;
if ( isFinite( min ) && isFinite( max ) ) {
if ( min < - Math.PI ) min += twoPI; else if ( min > Math.PI ) min -= twoPI;
if ( max < - Math.PI ) max += twoPI; else if ( max > Math.PI ) max -= twoPI;
if ( min <= max ) {
spherical.theta = Math.max( min, Math.min( max, spherical.theta ) );
} else {
spherical.theta = spherical.theta > ( min + max ) / 2 ? Math.max( min, spherical.theta ) : Math.min( max, spherical.theta );
}
} // restrict phi to be between desired limits
spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
spherical.makeSafe();
spherical.radius *= scale; // restrict radius to be between desired limits
spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); // move target to panned location
if ( scope.enableDamping === true ) {
scope.target.addScaledVector( panOffset, scope.dampingFactor );
} else {
scope.target.add( panOffset );
}
offset.setFromSpherical( spherical ); // rotate offset back to "camera-up-vector-is-up" space
offset.applyQuaternion( quatInverse );
position.copy( scope.target ).add( offset );
scope.object.lookAt( scope.target );
if ( scope.enableDamping === true ) {
sphericalDelta.theta *= 1 - scope.dampingFactor;
sphericalDelta.phi *= 1 - scope.dampingFactor;
panOffset.multiplyScalar( 1 - scope.dampingFactor );
} else {
sphericalDelta.set( 0, 0, 0 );
panOffset.set( 0, 0, 0 );
}
scale = 1; // update condition is:
// min(camera displacement, camera rotation in radians)^2 > EPS
// using small-angle approximation cos(x/2) = 1 - x^2 / 8
if ( zoomChanged || lastPosition.distanceToSquared( scope.object.position ) > EPS || 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
scope.dispatchEvent( _changeEvent );
lastPosition.copy( scope.object.position );
lastQuaternion.copy( scope.object.quaternion );
zoomChanged = false;
return true;
}
return false;
};
}();
this.dispose = function () {
scope.domElement.removeEventListener( 'contextmenu', onContextMenu );
scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
scope.domElement.removeEventListener( 'pointercancel', onPointerCancel );
scope.domElement.removeEventListener( 'wheel', onMouseWheel );
scope.domElement.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.removeEventListener( 'pointerup', onPointerUp );
if ( scope._domElementKeyEvents !== null ) {
scope._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown );
} //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
}; //
// internals
//
const scope = this;
const STATE = {
NONE: - 1,
ROTATE: 0,
DOLLY: 1,
PAN: 2,
TOUCH_ROTATE: 3,
TOUCH_PAN: 4,
TOUCH_DOLLY_PAN: 5,
TOUCH_DOLLY_ROTATE: 6
};
let state = STATE.NONE;
const EPS = 0.000001; // current position in spherical coordinates
const spherical = new THREE.Spherical();
const sphericalDelta = new THREE.Spherical();
let scale = 1;
const panOffset = new THREE.Vector3();
let zoomChanged = false;
const rotateStart = new THREE.Vector2();
const rotateEnd = new THREE.Vector2();
const rotateDelta = new THREE.Vector2();
const panStart = new THREE.Vector2();
const panEnd = new THREE.Vector2();
const panDelta = new THREE.Vector2();
const dollyStart = new THREE.Vector2();
const dollyEnd = new THREE.Vector2();
const dollyDelta = new THREE.Vector2();
const pointers = [];
const pointerPositions = {};
function getAutoRotationAngle() {
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
}
function getZoomScale() {
return Math.pow( 0.95, scope.zoomSpeed );
}
function rotateLeft( angle ) {
sphericalDelta.theta -= angle;
}
function rotateUp( angle ) {
sphericalDelta.phi -= angle;
}
const panLeft = function () {
const v = new THREE.Vector3();
return function panLeft( distance, objectMatrix ) {
v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
v.multiplyScalar( - distance );
panOffset.add( v );
};
}();
const panUp = function () {
const v = new THREE.Vector3();
return function panUp( distance, objectMatrix ) {
if ( scope.screenSpacePanning === true ) {
v.setFromMatrixColumn( objectMatrix, 1 );
} else {
v.setFromMatrixColumn( objectMatrix, 0 );
v.crossVectors( scope.object.up, v );
}
v.multiplyScalar( distance );
panOffset.add( v );
};
}(); // deltaX and deltaY are in pixels; right and down are positive
const pan = function () {
const offset = new THREE.Vector3();
return function pan( deltaX, deltaY ) {
const element = scope.domElement;
if ( scope.object.isPerspectiveCamera ) {
// perspective
const position = scope.object.position;
offset.copy( position ).sub( scope.target );
let targetDistance = offset.length(); // half of the fov is center to top of screen
targetDistance *= Math.tan( scope.object.fov / 2 * Math.PI / 180.0 ); // we use only clientHeight here so aspect ratio does not distort speed
panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
} else if ( scope.object.isOrthographicCamera ) {
// orthographic
panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
} else {
// camera neither orthographic nor perspective
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
scope.enablePan = false;
}
};
}();
function dollyOut( dollyScale ) {
if ( scope.object.isPerspectiveCamera ) {
scale /= dollyScale;
} else if ( scope.object.isOrthographicCamera ) {
scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
scope.object.updateProjectionMatrix();
zoomChanged = true;
} else {
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
scope.enableZoom = false;
}
}
function dollyIn( dollyScale ) {
if ( scope.object.isPerspectiveCamera ) {
scale *= dollyScale;
} else if ( scope.object.isOrthographicCamera ) {
scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
scope.object.updateProjectionMatrix();
zoomChanged = true;
} else {
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
scope.enableZoom = false;
}
} //
// event callbacks - update the object state
//
function handleMouseDownRotate( event ) {
rotateStart.set( event.clientX, event.clientY );
}
function handleMouseDownDolly( event ) {
dollyStart.set( event.clientX, event.clientY );
}
function handleMouseDownPan( event ) {
panStart.set( event.clientX, event.clientY );
}
function handleMouseMoveRotate( event ) {
rotateEnd.set( event.clientX, event.clientY );
rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
const element = scope.domElement;
rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
rotateStart.copy( rotateEnd );
scope.update();
}
function handleMouseMoveDolly( event ) {
dollyEnd.set( event.clientX, event.clientY );
dollyDelta.subVectors( dollyEnd, dollyStart );
if ( dollyDelta.y > 0 ) {
dollyOut( getZoomScale() );
} else if ( dollyDelta.y < 0 ) {
dollyIn( getZoomScale() );
}
dollyStart.copy( dollyEnd );
scope.update();
}
function handleMouseMovePan( event ) {
panEnd.set( event.clientX, event.clientY );
panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
pan( panDelta.x, panDelta.y );
panStart.copy( panEnd );
scope.update();
}
function handleMouseWheel( event ) {
if ( event.deltaY < 0 ) {
dollyIn( getZoomScale() );
} else if ( event.deltaY > 0 ) {
dollyOut( getZoomScale() );
}
scope.update();
}
function handleKeyDown( event ) {
let needsUpdate = false;
switch ( event.code ) {
case scope.keys.UP:
pan( 0, scope.keyPanSpeed );
needsUpdate = true;
break;
case scope.keys.BOTTOM:
pan( 0, - scope.keyPanSpeed );
needsUpdate = true;
break;
case scope.keys.LEFT:
pan( scope.keyPanSpeed, 0 );
needsUpdate = true;
break;
case scope.keys.RIGHT:
pan( - scope.keyPanSpeed, 0 );
needsUpdate = true;
break;
}
if ( needsUpdate ) {
// prevent the browser from scrolling on cursor keys
event.preventDefault();
scope.update();
}
}
function handleTouchStartRotate() {
if ( pointers.length === 1 ) {
rotateStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY );
} else {
const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX );
const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY );
rotateStart.set( x, y );
}
}
function handleTouchStartPan() {
if ( pointers.length === 1 ) {
panStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY );
} else {
const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX );
const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY );
panStart.set( x, y );
}
}
function handleTouchStartDolly() {
const dx = pointers[ 0 ].pageX - pointers[ 1 ].pageX;
const dy = pointers[ 0 ].pageY - pointers[ 1 ].pageY;
const distance = Math.sqrt( dx * dx + dy * dy );
dollyStart.set( 0, distance );
}
function handleTouchStartDollyPan() {
if ( scope.enableZoom ) handleTouchStartDolly();
if ( scope.enablePan ) handleTouchStartPan();
}
function handleTouchStartDollyRotate() {
if ( scope.enableZoom ) handleTouchStartDolly();
if ( scope.enableRotate ) handleTouchStartRotate();
}
function handleTouchMoveRotate( event ) {
if ( pointers.length == 1 ) {
rotateEnd.set( event.pageX, event.pageY );
} else {
const position = getSecondPointerPosition( event );
const x = 0.5 * ( event.pageX + position.x );
const y = 0.5 * ( event.pageY + position.y );
rotateEnd.set( x, y );
}
rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
const element = scope.domElement;
rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
rotateStart.copy( rotateEnd );
}
function handleTouchMovePan( event ) {
if ( pointers.length === 1 ) {
panEnd.set( event.pageX, event.pageY );
} else {
const position = getSecondPointerPosition( event );
const x = 0.5 * ( event.pageX + position.x );
const y = 0.5 * ( event.pageY + position.y );
panEnd.set( x, y );
}
panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
pan( panDelta.x, panDelta.y );
panStart.copy( panEnd );
}
function handleTouchMoveDolly( event ) {
const position = getSecondPointerPosition( event );
const dx = event.pageX - position.x;
const dy = event.pageY - position.y;
const distance = Math.sqrt( dx * dx + dy * dy );
dollyEnd.set( 0, distance );
dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );
dollyOut( dollyDelta.y );
dollyStart.copy( dollyEnd );
}
function handleTouchMoveDollyPan( event ) {
if ( scope.enableZoom ) handleTouchMoveDolly( event );
if ( scope.enablePan ) handleTouchMovePan( event );
}
function handleTouchMoveDollyRotate( event ) {
if ( scope.enableZoom ) handleTouchMoveDolly( event );
if ( scope.enableRotate ) handleTouchMoveRotate( event );
} //
// event handlers - FSM: listen for events and reset state
//
function onPointerDown( event ) {
if ( scope.enabled === false ) return;
if ( pointers.length === 0 ) {
scope.domElement.setPointerCapture( event.pointerId );
scope.domElement.addEventListener( 'pointermove', onPointerMove );
scope.domElement.addEventListener( 'pointerup', onPointerUp );
} //
addPointer( event );
if ( event.pointerType === 'touch' ) {
onTouchStart( event );
} else {
onMouseDown( event );
}
}
function onPointerMove( event ) {
if ( scope.enabled === false ) return;
if ( event.pointerType === 'touch' ) {
onTouchMove( event );
} else {
onMouseMove( event );
}
}
function onPointerUp( event ) {
removePointer( event );
if ( pointers.length === 0 ) {
scope.domElement.releasePointerCapture( event.pointerId );
scope.domElement.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.removeEventListener( 'pointerup', onPointerUp );
}
scope.dispatchEvent( _endEvent );
state = STATE.NONE;
}
function onPointerCancel( event ) {
removePointer( event );
}
function onMouseDown( event ) {
let mouseAction;
switch ( event.button ) {
case 0:
mouseAction = scope.mouseButtons.LEFT;
break;
case 1:
mouseAction = scope.mouseButtons.MIDDLE;
break;
case 2:
mouseAction = scope.mouseButtons.RIGHT;
break;
default:
mouseAction = - 1;
}
switch ( mouseAction ) {
case THREE.MOUSE.DOLLY:
if ( scope.enableZoom === false ) return;
handleMouseDownDolly( event );
state = STATE.DOLLY;
break;
case THREE.MOUSE.ROTATE:
if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
if ( scope.enablePan === false ) return;
handleMouseDownPan( event );
state = STATE.PAN;
} else {
if ( scope.enableRotate === false ) return;
handleMouseDownRotate( event );
state = STATE.ROTATE;
}
break;
case THREE.MOUSE.PAN:
if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
if ( scope.enableRotate === false ) return;
handleMouseDownRotate( event );
state = STATE.ROTATE;
} else {
if ( scope.enablePan === false ) return;
handleMouseDownPan( event );
state = STATE.PAN;
}
break;
default:
state = STATE.NONE;
}
if ( state !== STATE.NONE ) {
scope.dispatchEvent( _startEvent );
}
}
function onMouseMove( event ) {
switch ( state ) {
case STATE.ROTATE:
if ( scope.enableRotate === false ) return;
handleMouseMoveRotate( event );
break;
case STATE.DOLLY:
if ( scope.enableZoom === false ) return;
handleMouseMoveDolly( event );
break;
case STATE.PAN:
if ( scope.enablePan === false ) return;
handleMouseMovePan( event );
break;
}
}
function onMouseWheel( event ) {
if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return;
event.preventDefault();
scope.dispatchEvent( _startEvent );
handleMouseWheel( event );
scope.dispatchEvent( _endEvent );
}
function onKeyDown( event ) {
if ( scope.enabled === false || scope.enablePan === false ) return;
handleKeyDown( event );
}
function onTouchStart( event ) {
trackPointer( event );
switch ( pointers.length ) {
case 1:
switch ( scope.touches.ONE ) {
case THREE.TOUCH.ROTATE:
if ( scope.enableRotate === false ) return;
handleTouchStartRotate();
state = STATE.TOUCH_ROTATE;
break;
case THREE.TOUCH.PAN:
if ( scope.enablePan === false ) return;
handleTouchStartPan();
state = STATE.TOUCH_PAN;
break;
default:
state = STATE.NONE;
}
break;
case 2:
switch ( scope.touches.TWO ) {
case THREE.TOUCH.DOLLY_PAN:
if ( scope.enableZoom === false && scope.enablePan === false ) return;
handleTouchStartDollyPan();
state = STATE.TOUCH_DOLLY_PAN;
break;
case THREE.TOUCH.DOLLY_ROTATE:
if ( scope.enableZoom === false && scope.enableRotate === false ) return;
handleTouchStartDollyRotate();
state = STATE.TOUCH_DOLLY_ROTATE;
break;
default:
state = STATE.NONE;
}
break;
default:
state = STATE.NONE;
}
if ( state !== STATE.NONE ) {
scope.dispatchEvent( _startEvent );
}
}
function onTouchMove( event ) {
trackPointer( event );
switch ( state ) {
case STATE.TOUCH_ROTATE:
if ( scope.enableRotate === false ) return;
handleTouchMoveRotate( event );
scope.update();
break;
case STATE.TOUCH_PAN:
if ( scope.enablePan === false ) return;
handleTouchMovePan( event );
scope.update();
break;
case STATE.TOUCH_DOLLY_PAN:
if ( scope.enableZoom === false && scope.enablePan === false ) return;
handleTouchMoveDollyPan( event );
scope.update();
break;
case STATE.TOUCH_DOLLY_ROTATE:
if ( scope.enableZoom === false && scope.enableRotate === false ) return;
handleTouchMoveDollyRotate( event );
scope.update();
break;
default:
state = STATE.NONE;
}
}
function onContextMenu( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
}
function addPointer( event ) {
pointers.push( event );
}
function removePointer( event ) {
delete pointerPositions[ event.pointerId ];
for ( let i = 0; i < pointers.length; i ++ ) {
if ( pointers[ i ].pointerId == event.pointerId ) {
pointers.splice( i, 1 );
return;
}
}
}
function trackPointer( event ) {
let position = pointerPositions[ event.pointerId ];
if ( position === undefined ) {
position = new THREE.Vector2();
pointerPositions[ event.pointerId ] = position;
}
position.set( event.pageX, event.pageY );
}
function getSecondPointerPosition( event ) {
const pointer = event.pointerId === pointers[ 0 ].pointerId ? pointers[ 1 ] : pointers[ 0 ];
return pointerPositions[ pointer.pointerId ];
} //
scope.domElement.addEventListener( 'contextmenu', onContextMenu );
scope.domElement.addEventListener( 'pointerdown', onPointerDown );
scope.domElement.addEventListener( 'pointercancel', onPointerCancel );
scope.domElement.addEventListener( 'wheel', onMouseWheel, {
passive: false
} ); // force an update at start
this.update();
}
} // This set of controls performs orbiting, dollying (zooming), and panning.
// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
// This is very similar to OrbitControls, another set of touch behavior
//
// Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate
// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
// Pan - left mouse, or arrow keys / touch: one-finger move
class MapControls extends OrbitControls {
constructor( object, domElement ) {
super( object, domElement );
this.screenSpacePanning = false; // pan orthogonal to world-space direction camera.up
this.mouseButtons.LEFT = THREE.MOUSE.PAN;
this.mouseButtons.RIGHT = THREE.MOUSE.ROTATE;
this.touches.ONE = THREE.TOUCH.PAN;
this.touches.TWO = THREE.TOUCH.DOLLY_ROTATE;
}
}
THREE.MapControls = MapControls;
THREE.OrbitControls = OrbitControls;
} )();
I couldn't explain why the code was unable to render the sphere. What went wrong? I'm at loss on how to move this Sphere.
This is the correct code so that the camera can be moved around the Sphere.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 0; }
</style>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/three#0.143/build/three.min.js"></script>
<script src=".\three.js-master\three.js-master\examples\js\controls\OrbitControls.js"></script>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set( 0, 0, 50 );
camera.lookAt( 0, 0, 0 );
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const geometry = new THREE.SphereGeometry( 15, 32, 16 );
const texture = new THREE.TextureLoader().load( 'https://i.imgur.com/kFoWvzw.jpg' );
const material = new THREE.MeshBasicMaterial( { map: texture } );
const sphere = new THREE.Mesh( geometry, material );
const controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.update();
scene.add( sphere );
function animate()
{
requestAnimationFrame( animate );
sphere.rotation.x += 0.00;
sphere.rotation.y += 0.01;
renderer.render( scene, camera );
};
animate();
</script>
</body>
</html>
Enjoy!

Three.js raycasting does not work on DIV which is not full screen

I wrote a simple web page displaying a 3D bone and using Three.js raycasting to draw dots and lines on it and it worked perfectly well. result of using raycast on full screen web page
But when I ported it to another web page having multiple windows, the process did not work.
I followed the instructions from fiddle http://jsfiddle.net/cn7ecoaa/ but still failed.
Here is the CSS:
.column2 {
float: right;
width: 80%;
position: static;
}
#w3-container {
width: 1522px;
height: 1001px;
position: static;
}
I loaded the model and examined it and it certainly contains mesh and buffer geometry:
result of console.log after loading the 3D model
Here is the loader code :
gltfLoader.load (
inputModel,
function(gltf)
{
scene.add(gltf.scene);
model = gltf.scene;
mesh = gltf.scene.children[0];
mesh.name = "tibia_glTF_01";
model.traverse
( function ( child )
{
if ( child.isMesh && child.geometry.isBufferGeometry)
{
const material = new THREE.MeshPhongMaterial( {color:0x3c3c3c, emissive: 0x000000 } );
child.material = material;
const bg = child.geometry;
console.log("Do not change geometry.");
}
}
);
getItem(); // Refresh the item list to be displayed
objects.push( mesh );
});
Here is the code for handling the onmousedown event :
function onMouseDown( event ) {
if ( !DRAWLINE ) return;
var raycaster = new THREE.Raycaster();
event.preventDefault();
mouse.x = (( event.clientX - renderer.domElement.offsetLeft) / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - (( event.clientY - renderer.domElement.offsetTop) / renderer.domElement.clientHeight ) * 2 + 1;
console.log("mouse.x", mouse.x, "mouse.y", mouse.y);
console.log("Objects", objects);
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 ) {
alert("HIT!");
dot.visible = true;
// Copy the intersection points to be used in functions addPoint()
mouse.copy( intersects[0].point );
intersects.length = 0; // reset the variable
addPoint();
} else {
console.log( "<<NO Intersection!!>>" );
}
}
No matter what combinations I have used to cater for the offset, the result of raycasting is still "<<NO Intersection!!>>".
Any idea on how to fix this would be much appreciated!
Thanks for your help!
Try computing your mouse coordinates like so:
const rect = renderer.domElement.getBoundingClientRect();
mouse.x = ( ( event.clientX - rect.left ) / ( rect. right - rect.left ) ) * 2 - 1;
mouse.y = - ( ( event.clientY - rect.top ) / ( rect.bottom - rect.top) ) * 2 + 1;

Screen goes blank when moving camera

I'm attempting to create something in three.js and I'm moderately successful in building the geometry from text files. To start off, I manage to get a game level file to display complete with textures. Then I try to load the models from that game and suddenly the screen would go blank as soon as I try to move around. I can look around, but moving kills the screen.
I then remove the part about loading the models, but still keep their position in memory, and the same thing happens.
After taking a good look, it turns out that after I set the camera's position to the player's position, then try to move, that is when the screen goes blank.
Some of the code snippet involving how the camera is set up:
var getPlayers = document.createElement("select");
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.001, 10000 );
var LOADING_COMPLETE = false;
A whole bunch of loading functions follow...
function CompleteTheLoading()
{
document.getElementById("mainmenu").innerHTML = "<b>Jump to </b>";
document.getElementById("mainmenu").appendChild(getPlayers);
yawObject.position.x = jklthings[getPlayers.value][3];
yawObject.position.y = jklthings[getPlayers.value][4];
yawObject.position.z = jklthings[getPlayers.value][5];
yawObject.rotation.z = jklthings[getPlayers.value][7]*Math.PI/180;
LOADING_COMPLETE = true;
animate();
}
getPlayers.onchange = function()
{
velocity.x = 0;
velocity.y = 0;
velocity.z = 0;
yawObject.position.x = jklthings[getPlayers.value][3];
yawObject.position.y = jklthings[getPlayers.value][4];
yawObject.position.z = jklthings[getPlayers.value][5];
yawObject.rotation.z = jklthings[getPlayers.value][7]*Math.PI/180;
if (document.activeElement != document.body) document.activeElement.blur();
}
var mouseX = 0;
var mouseY = 0;
var mouseK = 0;
var havePointerLock = 'pointerLockElement' in document ||
'mozPointerLockElement' in document ||
'webkitPointerLockElement' in document;
var moveForward = false;
var moveBackward = false;
var moveLeft = false;
var moveRight = false;
var canJump = false;
var prevTime = performance.now();
var velocity = new THREE.Vector3();
var direction = new THREE.Vector3();
var onKeyDown = function ( event ) {
switch ( event.keyCode ) {
case 38: // up
case 87: // w
moveForward = true;
break;
case 37: // left
case 65: // a
moveLeft = true;
break;
case 40: // down
case 83: // s
moveBackward = true;
break;
case 39: // right
case 68: // d
moveRight = true;
break;
}
};
var onKeyUp = function ( event ) {
switch( event.keyCode ) {
case 38: // up
case 87: // w
moveForward = false;
break;
case 37: // left
case 65: // a
moveLeft = false;
break;
case 40: // down
case 83: // s
moveBackward = false;
break;
case 39: // right
case 68: // d
moveRight = false;
break;
case 78: // n
if(getPlayers.selectedIndex==getPlayers.length-1)
getPlayers.selectedIndex=0;
else
getPlayers.selectedIndex=getPlayers.selectedIndex+1;
getPlayers.onchange();
break;
case 80: // p
if(getPlayers.selectedIndex==0)
getPlayers.selectedIndex=getPlayers.length-1;
else
getPlayers.selectedIndex=getPlayers.selectedIndex-1;
getPlayers.onchange();
break;
case 13: // Enter
getPlayers.onchange();
break;
}
};
document.addEventListener( 'keydown', onKeyDown, false );
document.addEventListener( 'keyup', onKeyUp, false );
var pitchObject = new THREE.Object3D();
pitchObject.add( camera );
var yawObject = new THREE.Object3D();
yawObject.add( pitchObject );
scene.add(yawObject);
var PI_2 = Math.PI / 2;
camera.rotation.x = 90*Math.PI/180;
function animate()
{
var time = performance.now();
var delta = ( time - prevTime ) / 1000;
velocity.x -= velocity.x * 1.0 * delta;
velocity.y -= velocity.y * 1.0 * delta;
velocity.z -= velocity.z * 1.0 * delta;
direction.z = Number( moveForward ) - Number( moveBackward );
direction.x = Number( moveLeft ) - Number( moveRight );
direction.normalize(); // this ensures consistent movements in all directions
if ( moveForward || moveBackward )
{
velocity.y -= direction.z * 1.0 * -delta;
velocity.z -= direction.z * pitchObject.rotation.x * 1.0 * -delta;;
}
if ( moveLeft || moveRight )
velocity.x -= direction.x * 1.0 * delta;
if(mouseK==1)
{
yawObject.rotation.z -= mouseX * 0.01;
pitchObject.rotation.x -= mouseY * 0.01;
pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) );
}
yawObject.translateX( velocity.x * delta );
yawObject.translateY( velocity.y * delta );
yawObject.translateZ( velocity.z * delta );
renderer.render( scene, camera );
prevTime = time;
requestAnimationFrame( animate );
}
function mouseMovement(e)
{
mouseX = e.movementX || e.mozMovementX || e.webkitMovementX || 0;
mouseY = e.movementY || e.mozMovementY || e.webkitMovementY || 0;
}
renderer.domElement.addEventListener( 'mousedown', function()
{
if(LOADING_COMPLETE==true)
{
mouseK = 1;
document.addEventListener("mousemove", mouseMovement, false);
}
},false);
renderer.domElement.addEventListener( 'mouseup', function()
{
// Ask the browser to release the pointer
document.removeEventListener("mousemove", mouseMovement, false);
mouseX = 0;
mouseY = 0;
mouseK = 0;
},false);
Now, you are probably noticing that the camera is set at an angle. This is due to that the game is Z-up while three.js is Y-up. Rotating and recalculating the whole level is no fun, so I just jumble the camera instead.
Also, for the most part when I change getPlayers it still renders, but after I move I can't get it back even after getPlayers.
Making snippet, and oddly enough that works when setting the position of the yawObject. Except it won't run in this snippet. But here it is anyways.
<html>
<head>
<title>test</title>
</head>
<body>
<script src="https://threejs.org/build/three.js"></script>
<script>
var LOADING_COMPLETE=true;
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.001, 10000 );
// A whole lot of Loading Functions removed...
// All that remains is three's Materials Example:
var geometry = new THREE.TorusKnotGeometry( 10, 3, 100, 16 );
var mesh = new THREE.Mesh( geometry, new THREE.MeshNormalMaterial({}) );
scene.add( mesh );
var mouseX = 0;
var mouseY = 0;
var mouseK = 0;
var havePointerLock = 'pointerLockElement' in document ||
'mozPointerLockElement' in document ||
'webkitPointerLockElement' in document;
var moveForward = false;
var moveBackward = false;
var moveLeft = false;
var moveRight = false;
var canJump = false;
var prevTime = performance.now();
var velocity = new THREE.Vector3();
var direction = new THREE.Vector3();
var onKeyDown = function ( event ) {
switch ( event.keyCode ) {
case 38: // up
case 87: // w
moveForward = true;
break;
case 37: // left
case 65: // a
moveLeft = true;
break;
case 40: // down
case 83: // s
moveBackward = true;
break;
case 39: // right
case 68: // d
moveRight = true;
break;
}
};
var onKeyUp = function ( event ) {
switch( event.keyCode ) {
case 38: // up
case 87: // w
moveForward = false;
break;
case 37: // left
case 65: // a
moveLeft = false;
break;
case 40: // down
case 83: // s
moveBackward = false;
break;
case 39: // right
case 68: // d
moveRight = false;
break;
case 78: // n
if(getPlayers.selectedIndex==getPlayers.length-1)
getPlayers.selectedIndex=0;
else
getPlayers.selectedIndex=getPlayers.selectedIndex+1;
getPlayers.onchange();
break;
case 80: // p
if(getPlayers.selectedIndex==0)
getPlayers.selectedIndex=getPlayers.length-1;
else
getPlayers.selectedIndex=getPlayers.selectedIndex-1;
getPlayers.onchange();
break;
case 13: // Enter
getPlayers.onchange();
break;
}
};
document.addEventListener( 'keydown', onKeyDown, false );
document.addEventListener( 'keyup', onKeyUp, false );
var pitchObject = new THREE.Object3D();
pitchObject.add( camera );
var yawObject = new THREE.Object3D();
yawObject.add( pitchObject );
scene.add(yawObject);
var PI_2 = Math.PI / 2;
camera.rotation.x = 90*Math.PI/180;
yawObject.position.x = 10.7;
yawObject.position.y = 20.2;
yawObject.position.z = 0;
yawObject.rotation.z = 135*Math.PI/180;
animate();
function animate()
{
var time = performance.now();
var delta = ( time - prevTime ) / 1000;
velocity.x -= velocity.x * 1.0 * delta;
velocity.y -= velocity.y * 1.0 * delta;
velocity.z -= velocity.z * 1.0 * delta;
direction.z = Number( moveForward ) - Number( moveBackward );
direction.x = Number( moveLeft ) - Number( moveRight );
direction.normalize(); // this ensures consistent movements in all directions
if ( moveForward || moveBackward )
{
velocity.y -= direction.z * 1.0 * -delta;
velocity.z -= direction.z * pitchObject.rotation.x * 1.0 * -delta;;
}
if ( moveLeft || moveRight )
velocity.x -= direction.x * 1.0 * delta;
if(mouseK==1)
{
yawObject.rotation.z -= mouseX * 0.01;
pitchObject.rotation.x -= mouseY * 0.01;
pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) );
}
yawObject.translateX( velocity.x * delta );
yawObject.translateY( velocity.y * delta );
yawObject.translateZ( velocity.z * delta );
renderer.render( scene, camera );
prevTime = time;
requestAnimationFrame( animate );
}
function mouseMovement(e)
{
mouseX = e.movementX || e.mozMovementX || e.webkitMovementX || 0;
mouseY = e.movementY || e.mozMovementY || e.webkitMovementY || 0;
}
renderer.domElement.addEventListener( 'mousedown', function()
{
if(LOADING_COMPLETE==true)
{
mouseK = 1;
document.addEventListener("mousemove", mouseMovement, false);
}
},false);
renderer.domElement.addEventListener( 'mouseup', function()
{
document.removeEventListener("mousemove", mouseMovement, false);
mouseX = 0;
mouseY = 0;
mouseK = 0;
},false);
window.onresize = function()
{
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
</script>
</body>
</html>
EDIT:
Following Jim's advice, I've reconstructed the code bit by bit, keeping the "teleportation" part, and calling it to some arbitrary numbers. Everything seems to work fine, even after the final touches of loading jklthings. As soon as I change the arbitrary numbers to the coordinates of the jklthings that represent a player, it breaks like above.
Using a console.log(jklthings[getPlayers.value][3] + "\t" + jklthings[getPlayers.value][4] + "\t" + jklthings[getPlayers.value][5] + "\t" + jklthings[getPlayers.value][7]*Math.PI/180); I get the values -4.9 5.9 .1 0. Feeding these in manually, everything works.

Unable to intersect Points

I unable to intersect Three.Points but i am able to intersect THREE.Mesh, below is my code: Can someone please help me to understand what mistake i am making. When i intersect the Points all i get is 0.
function init()
{
geometry = new THREE.Geometry();
vertex = new THREE.Vector3();
vertex.x=( [i + 3 ] * 140 ) - 1330;
vertex.y = - ( [i + 3 ] * 180 ) + 990;
vertex.z = 100;
geometry.vertices.push(vertex);
var particleTexture = THREE.ImageUtils.loadTexture(imgstr);
var materials = new THREE.PointsMaterial({
map:particleTexture,
size: 150
});
particles = new THREE.Points(geometry, materials);
scene.add(particles);
targetList.push(particles);
}
function onDocumentMouseMove( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function onDocumentMouseDown(event){
var projector = new THREE.Projector();
var vector = new THREE.Vector3(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
0.5);
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster(camera.position,vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects(targetList);
console.log(intersects.length);
}
function onDocumentMouseDown( event ) {
event.preventDefault();
if(event.which == 1) {
var raycaster = new THREE.Raycaster();
raycaster.setFromCamera( mouse, camera );
raycaster.params.Points.threshold = 100000; // bigger number = bigger sensitivity
var intersects = raycaster.intersectObjects( myobjectsarray,false); // myobject array is array of THREE.Points object
if ( intersects.length > 0 ) {console.log('tadaah',intersects);}
}
}

Reset camera using OrbitControls.js

I'm using OrbitControls.js to allow mouse interaction. I'm adding a button into the scene that allows to "reset" the camera to it's state where it was before any mouse interactions.
I have tried to save camera.position and camera.rotation before any interactions:
camera_initial_position = camera.position;
camera_initial_rotation = camera.rotation;
And after the "reset" button is pressed, the initial position and rotation is set:
camera.position = camera_initial_position;
camera.rotation = camera_initial_rotation;
It works well if pan is not used. If user pans using mouse right button, then the above code cannot "reset" camera.
What is the right method to "reset" the camera to its previous state?
Revision of three.js is r58 and this is the OrbitControls.js:
/**
* #author qiao / https://github.com/qiao
* #author mrdoob / http://mrdoob.com
* #author alteredq / http://alteredqualia.com/
* #author WestLangley / http://github.com/WestLangley
*/
THREE.OrbitControls = function ( object, domElement ) {
this.object = object;
this.domElement = ( domElement !== undefined ) ? domElement : document;
// API
this.enabled = true;
this.center = new THREE.Vector3();
this.userZoom = true;
this.userZoomSpeed = 1.0;
this.userRotate = true;
this.userRotateSpeed = 1.0;
this.userPan = true;
this.userPanSpeed = 2.0;
this.autoRotate = false;
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
this.minPolarAngle = 0; // radians
this.maxPolarAngle = Math.PI; // radians
this.minDistance = 0;
this.maxDistance = Infinity;
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
// internals
var scope = this;
var EPS = 0.000001;
var PIXELS_PER_ROUND = 1800;
var rotateStart = new THREE.Vector2();
var rotateEnd = new THREE.Vector2();
var rotateDelta = new THREE.Vector2();
var zoomStart = new THREE.Vector2();
var zoomEnd = new THREE.Vector2();
var zoomDelta = new THREE.Vector2();
var phiDelta = 0;
var thetaDelta = 0;
var scale = 1;
var lastPosition = new THREE.Vector3();
var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2 };
var state = STATE.NONE;
// events
var changeEvent = { type: 'change' };
this.rotateLeft = function ( angle ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
}
thetaDelta -= angle;
};
this.rotateRight = function ( angle ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
}
thetaDelta += angle;
};
this.rotateUp = function ( angle ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
}
phiDelta -= angle;
};
this.rotateDown = function ( angle ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
}
phiDelta += angle;
};
this.zoomIn = function ( zoomScale ) {
if ( zoomScale === undefined ) {
zoomScale = getZoomScale();
}
scale /= zoomScale;
};
this.zoomOut = function ( zoomScale ) {
if ( zoomScale === undefined ) {
zoomScale = getZoomScale();
}
scale *= zoomScale;
};
this.pan = function ( distance ) {
distance.transformDirection( this.object.matrix );
distance.multiplyScalar( scope.userPanSpeed );
this.object.position.add( distance );
this.center.add( distance );
};
this.update = function () {
var position = this.object.position;
var offset = position.clone().sub( this.center );
// angle from z-axis around y-axis
var theta = Math.atan2( offset.x, offset.z );
// angle from y-axis
var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
if ( this.autoRotate ) {
this.rotateLeft( getAutoRotationAngle() );
}
theta += thetaDelta;
phi += phiDelta;
// restrict phi to be between desired limits
phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
// restrict phi to be betwee EPS and PI-EPS
phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
var radius = offset.length() * scale;
// restrict radius to be between desired limits
radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
offset.x = radius * Math.sin( phi ) * Math.sin( theta );
offset.y = radius * Math.cos( phi );
offset.z = radius * Math.sin( phi ) * Math.cos( theta );
position.copy( this.center ).add( offset );
this.object.lookAt( this.center );
thetaDelta = 0;
phiDelta = 0;
scale = 1;
if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
this.dispatchEvent( changeEvent );
lastPosition.copy( this.object.position );
}
};
function getAutoRotationAngle() {
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
}
function getZoomScale() {
return Math.pow( 0.95, scope.userZoomSpeed );
}
function onMouseDown( event ) {
if ( scope.enabled === false ) return;
if ( scope.userRotate === false ) return;
event.preventDefault();
if ( event.button === 0 ) {
state = STATE.ROTATE;
rotateStart.set( event.clientX, event.clientY );
} else if ( event.button === 1 ) {
state = STATE.ZOOM;
zoomStart.set( event.clientX, event.clientY );
} else if ( event.button === 2 ) {
state = STATE.PAN;
}
document.addEventListener( 'mousemove', onMouseMove, false );
document.addEventListener( 'mouseup', onMouseUp, false );
}
function onMouseMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
if ( state === STATE.ROTATE ) {
rotateEnd.set( event.clientX, event.clientY );
rotateDelta.subVectors( rotateEnd, rotateStart );
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * scope.userRotateSpeed );
scope.rotateUp( 2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * scope.userRotateSpeed );
rotateStart.copy( rotateEnd );
} else if ( state === STATE.ZOOM ) {
zoomEnd.set( event.clientX, event.clientY );
zoomDelta.subVectors( zoomEnd, zoomStart );
if ( zoomDelta.y > 0 ) {
scope.zoomIn();
} else {
scope.zoomOut();
}
zoomStart.copy( zoomEnd );
} else if ( state === STATE.PAN ) {
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
scope.pan( new THREE.Vector3( - movementX, movementY, 0 ) );
}
}
function onMouseUp( event ) {
if ( scope.enabled === false ) return;
if ( scope.userRotate === false ) return;
document.removeEventListener( 'mousemove', onMouseMove, false );
document.removeEventListener( 'mouseup', onMouseUp, false );
state = STATE.NONE;
}
function onMouseWheel( event ) {
if ( scope.enabled === false ) return;
if ( scope.userZoom === false ) return;
var delta = 0;
if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
delta = event.wheelDelta;
} else if ( event.detail ) { // Firefox
delta = - event.detail;
}
if ( delta > 0 ) {
scope.zoomOut();
} else {
scope.zoomIn();
}
}
function onKeyDown( event ) {
if ( scope.enabled === false ) return;
if ( scope.userPan === false ) return;
switch ( event.keyCode ) {
case scope.keys.UP:
scope.pan( new THREE.Vector3( 0, 1, 0 ) );
break;
case scope.keys.BOTTOM:
scope.pan( new THREE.Vector3( 0, - 1, 0 ) );
break;
case scope.keys.LEFT:
scope.pan( new THREE.Vector3( - 1, 0, 0 ) );
break;
case scope.keys.RIGHT:
scope.pan( new THREE.Vector3( 1, 0, 0 ) );
break;
}
}
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
this.domElement.addEventListener( 'mousedown', onMouseDown, false );
this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
this.domElement.addEventListener( 'keydown', onKeyDown, false );
};
THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
You can reset the camera when using OrbitControls like so:
controls.reset();
three.js r.71
Pan operation is updating vector called this.center , you need to reset it to see pan method ,
this.center.add( distance );
set this method to:
this.resetCamera = function ( ) {
this.object.position.x= camera_initial_position.xPosition;
this.object.position.y = camera_initial_position.yPosition;
this.object.position.z = camera_initial_position.zPosition;
this.center.x= camera_initial_target.x;
this.center.y= camera_initial_target.y;
this.center.z= camera_initial_target.z;
};
and then the update method will keep the camera looking at the center vector.
ah.adel is correct Pan operation will update the center of the camera controller. Therefore if you need to reset/restore the camera to a predefined camera, you need to set camera controller center also.
Following code is a simple code to store camera position, rotation and control center
var camToSave = {};
camToSave.position = camera.position.clone();
camToSave.rotation = camera.rotation.clone();
camToSave.controlCenter = controls.target.clone();
Use this function to restore camera later.
function restoreCamera(position, rotation, controlCenter){
camera.position.set(position.x, position.y, position.z);
camera.rotation.set(rotation.x, rotation.y, rotation.z);
controls.target.set(controlCenter.x, controlCenter.y, controlCenter.z);
controls.update();
render();
}
Call restoreCamera function to restore saved camera.
restoreCamera(camToSave.position, camToSave.rotation, camToSave.controlCenter);
Hope this will help to anyone who having this problem

Resources