pivot point lost after element move whit orbitcontrols - javascript
I have a problem whit three.js and OrbitControls.js., i have an sphere that's make a little tween from center to left,
this is the fiddle: http://jsfiddle.net/mirko_roma89/4ruLy96g/1/
var scene, renderer, camera, controls;
init();
animate();
function init()
{
renderer = new THREE.WebGLRenderer( {antialias:true} );
var width = window.innerWidth;
var height = window.innerHeight;
renderer.setSize (width, height);
renderer.setClearColor( 0x222244, 1);
document.body.appendChild (renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera (45, width/height, 1, 10000);
camera.position.y = 16;
camera.position.z = 250;
controls = new THREE.OrbitControls (camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
var gridXZ = new THREE.GridHelper(100, 10);
gridXZ.setColors( new THREE.Color(0xff0000), new THREE.Color(0xffffff) );
scene.add(gridXZ);
var loader = new THREE.TextureLoader();
loader.crossOrigin = "";
var earthTexture = loader.load( "http://api.usno.navy.mil/imagery/earth.png?view=full&date=11/24/2015&time=4:45", callback );
// globe
var radius = 100;
var sphere = new THREE.Mesh( new THREE.SphereGeometry( radius, 16,16), new THREE.MeshBasicMaterial( { map: earthTexture } ) );
scene.add( sphere );
var latitude = 38.43;
var longitude = -9.1;
var marker = new THREE.Mesh( new THREE.SphereGeometry( 1, 16, 16 ), new THREE.MeshBasicMaterial( { color: 0xFF0000 } ) );
sphere.add( marker );
var verticalOffset = 0.1;
function callback() {
new TWEEN.Tween( sphere.position )
.to( { x:-30 , y: 0 }, 2000 )
.start();
}
}
function animate()
{
requestAnimationFrame ( animate );
TWEEN.update();
controls.update();
renderer.render (scene, camera);
}
<script>
/**
* #author qiao / https://github.com/qiao
* #author mrdoob / http://mrdoob.com
* #author alteredq / http://alteredqualia.com/
* #author WestLangley / http://github.com/WestLangley
* #author erich666 / http://erichaines.com
*/
// This set of controls performs orbiting, dollying (zooming), and panning.
// 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 arrow keys / touch: three finter swipe
THREE.OrbitControls = function ( object, domElement ) {
this.object = object;
this.domElement = ( domElement !== undefined ) ? domElement : document;
// 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, must be a sub-interval of the interval [ - Math.PI, Math.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.25;
// 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.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 round when fps is 60
// Set to false to disable use of the keys
this.enableKeys = true;
// The four arrow keys
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
// Mouse buttons
this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
// for reset
this.target0 = this.target.clone();
this.position0 = this.object.position.clone();
this.zoom0 = this.object.zoom;
//
// public methods
//
this.getPolarAngle = function () {
return phi;
};
this.getAzimuthalAngle = function () {
return theta;
};
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() {
var offset = new THREE.Vector3();
// so camera.up is the orbit axis
var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
var quatInverse = quat.clone().inverse();
var lastPosition = new THREE.Vector3();
var lastQuaternion = new THREE.Quaternion();
return function () {
var 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
theta = Math.atan2( offset.x, offset.z );
// angle from y-axis
phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
if ( scope.autoRotate && state === STATE.NONE ) {
rotateLeft( getAutoRotationAngle() );
}
theta += thetaDelta;
phi += phiDelta;
// restrict theta to be between desired limits
theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, theta ) );
// restrict phi to be between desired limits
phi = Math.max( scope.minPolarAngle, Math.min( scope.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( scope.minDistance, Math.min( scope.maxDistance, radius ) );
// move target to panned location
scope.target.add( panOffset );
offset.x = radius * Math.sin( phi ) * Math.sin( theta );
offset.y = radius * Math.cos( phi );
offset.z = radius * Math.sin( phi ) * Math.cos( theta );
// 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 ) {
thetaDelta *= ( 1 - scope.dampingFactor );
phiDelta *= ( 1 - scope.dampingFactor );
} else {
thetaDelta = 0;
phiDelta = 0;
}
scale = 1;
panOffset.set( 0, 0, 0 );
// 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, false );
scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
scope.domElement.removeEventListener( 'mousewheel', onMouseWheel, false );
scope.domElement.removeEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
document.removeEventListener( 'mousemove', onMouseMove, false );
document.removeEventListener( 'mouseup', onMouseUp, false );
document.removeEventListener( 'mouseout', onMouseUp, false );
window.removeEventListener( 'keydown', onKeyDown, false );
//scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
};
//
// internals
//
var scope = this;
var changeEvent = { type: 'change' };
var startEvent = { type: 'start' };
var endEvent = { type: 'end' };
var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
var state = STATE.NONE;
var EPS = 0.000001;
// current position in spherical coordinates
var theta;
var phi;
var phiDelta = 0;
var thetaDelta = 0;
var scale = 1;
var panOffset = new THREE.Vector3();
var zoomChanged = false;
var rotateStart = new THREE.Vector2();
var rotateEnd = new THREE.Vector2();
var rotateDelta = new THREE.Vector2();
var panStart = new THREE.Vector2();
var panEnd = new THREE.Vector2();
var panDelta = new THREE.Vector2();
var dollyStart = new THREE.Vector2();
var dollyEnd = new THREE.Vector2();
var dollyDelta = new THREE.Vector2();
function getAutoRotationAngle() {
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
}
function getZoomScale() {
return Math.pow( 0.95, scope.zoomSpeed );
}
function rotateLeft( angle ) {
thetaDelta -= angle;
}
function rotateUp( angle ) {
phiDelta -= angle;
}
var panLeft = function() {
var v = new THREE.Vector3();
return function panLeft( distance, objectMatrix ) {
var te = objectMatrix.elements;
// get X column of objectMatrix
v.set( te[ 0 ], te[ 1 ], te[ 2 ] );
v.multiplyScalar( - distance );
panOffset.add( v );
};
}();
var panUp = function() {
var v = new THREE.Vector3();
return function panUp( distance, objectMatrix ) {
var te = objectMatrix.elements;
// get Y column of objectMatrix
v.set( te[ 4 ], te[ 5 ], te[ 6 ] );
v.multiplyScalar( distance );
panOffset.add( v );
};
}();
// deltaX and deltaY are in pixels; right and down are positive
var pan = function() {
var offset = new THREE.Vector3();
return function( deltaX, deltaY ) {
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
if ( scope.object instanceof THREE.PerspectiveCamera ) {
// perspective
var position = scope.object.position;
offset.copy( position ).sub( scope.target );
var 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 actually don't use screenWidth, since perspective camera is fixed to screen height
panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
} else if ( scope.object instanceof THREE.OrthographicCamera ) {
// orthographic
panLeft( deltaX * ( scope.object.right - scope.object.left ) / element.clientWidth, scope.object.matrix );
panUp( deltaY * ( scope.object.top - scope.object.bottom ) / 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 dollyIn( dollyScale ) {
if ( scope.object instanceof THREE.PerspectiveCamera ) {
scale /= dollyScale;
} else if ( scope.object instanceof THREE.OrthographicCamera ) {
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 dollyOut( dollyScale ) {
if ( scope.object instanceof THREE.PerspectiveCamera ) {
scale *= dollyScale;
} else if ( scope.object instanceof THREE.OrthographicCamera ) {
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 ) {
//console.log( 'handleMouseDownRotate' );
rotateStart.set( event.clientX, event.clientY );
}
function handleMouseDownDolly( event ) {
//console.log( 'handleMouseDownDolly' );
dollyStart.set( event.clientX, event.clientY );
}
function handleMouseDownPan( event ) {
//console.log( 'handleMouseDownPan' );
panStart.set( event.clientX, event.clientY );
}
function handleMouseMoveRotate( event ) {
//console.log( 'handleMouseMoveRotate' );
rotateEnd.set( event.clientX, event.clientY );
rotateDelta.subVectors( rotateEnd, rotateStart );
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
// rotating across whole screen goes 360 degrees around
rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
// rotating up and down along whole screen attempts to go 360, but limited to 180
rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
rotateStart.copy( rotateEnd );
scope.update();
}
function handleMouseMoveDolly( event ) {
//console.log( 'handleMouseMoveDolly' );
dollyEnd.set( event.clientX, event.clientY );
dollyDelta.subVectors( dollyEnd, dollyStart );
if ( dollyDelta.y > 0 ) {
dollyIn( getZoomScale() );
} else if ( dollyDelta.y < 0 ) {
dollyOut( getZoomScale() );
}
dollyStart.copy( dollyEnd );
scope.update();
}
function handleMouseMovePan( event ) {
//console.log( 'handleMouseMovePan' );
panEnd.set( event.clientX, event.clientY );
panDelta.subVectors( panEnd, panStart );
pan( panDelta.x, panDelta.y );
panStart.copy( panEnd );
scope.update();
}
function handleMouseUp( event ) {
//console.log( 'handleMouseUp' );
}
function handleMouseWheel( event ) {
//console.log( 'handleMouseWheel' );
var delta = 0;
if ( event.wheelDelta !== undefined ) {
// WebKit / Opera / Explorer 9
delta = event.wheelDelta;
} else if ( event.detail !== undefined ) {
// Firefox
delta = - event.detail;
}
if ( delta > 0 ) {
dollyOut( getZoomScale() );
} else if ( delta < 0 ) {
dollyIn( getZoomScale() );
}
scope.update();
}
function handleKeyDown( event ) {
//console.log( 'handleKeyDown' );
switch ( event.keyCode ) {
case scope.keys.UP:
pan( 0, scope.keyPanSpeed );
scope.update();
break;
case scope.keys.BOTTOM:
pan( 0, - scope.keyPanSpeed );
scope.update();
break;
case scope.keys.LEFT:
pan( scope.keyPanSpeed, 0 );
scope.update();
break;
case scope.keys.RIGHT:
pan( - scope.keyPanSpeed, 0 );
scope.update();
break;
}
}
function handleTouchStartRotate( event ) {
//console.log( 'handleTouchStartRotate' );
rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
}
function handleTouchStartDolly( event ) {
//console.log( 'handleTouchStartDolly' );
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
var distance = Math.sqrt( dx * dx + dy * dy );
dollyStart.set( 0, distance );
}
function handleTouchStartPan( event ) {
//console.log( 'handleTouchStartPan' );
panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
}
function handleTouchMoveRotate( event ) {
//console.log( 'handleTouchMoveRotate' );
rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
rotateDelta.subVectors( rotateEnd, rotateStart );
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
// rotating across whole screen goes 360 degrees around
rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
// rotating up and down along whole screen attempts to go 360, but limited to 180
rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
rotateStart.copy( rotateEnd );
scope.update();
}
function handleTouchMoveDolly( event ) {
//console.log( 'handleTouchMoveDolly' );
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
var distance = Math.sqrt( dx * dx + dy * dy );
dollyEnd.set( 0, distance );
dollyDelta.subVectors( dollyEnd, dollyStart );
if ( dollyDelta.y > 0 ) {
dollyOut( getZoomScale() );
} else if ( dollyDelta.y < 0 ) {
dollyIn( getZoomScale() );
}
dollyStart.copy( dollyEnd );
scope.update();
}
function handleTouchMovePan( event ) {
//console.log( 'handleTouchMovePan' );
panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
panDelta.subVectors( panEnd, panStart );
pan( panDelta.x, panDelta.y );
panStart.copy( panEnd );
scope.update();
}
function handleTouchEnd( event ) {
//console.log( 'handleTouchEnd' );
}
//
// event handlers - FSM: listen for events and reset state
//
function onMouseDown( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
if ( event.button === scope.mouseButtons.ORBIT ) {
if ( scope.enableRotate === false ) return;
handleMouseDownRotate( event );
state = STATE.ROTATE;
} else if ( event.button === scope.mouseButtons.ZOOM ) {
if ( scope.enableZoom === false ) return;
handleMouseDownDolly( event );
state = STATE.DOLLY;
} else if ( event.button === scope.mouseButtons.PAN ) {
if ( scope.enablePan === false ) return;
handleMouseDownPan( event );
state = STATE.PAN;
}
if ( state !== STATE.NONE ) {
document.addEventListener( 'mousemove', onMouseMove, false );
document.addEventListener( 'mouseup', onMouseUp, false );
document.addEventListener( 'mouseout', onMouseUp, false );
scope.dispatchEvent( startEvent );
}
}
function onMouseMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
if ( state === STATE.ROTATE ) {
if ( scope.enableRotate === false ) return;
handleMouseMoveRotate( event );
} else if ( state === STATE.DOLLY ) {
if ( scope.enableZoom === false ) return;
handleMouseMoveDolly( event );
} else if ( state === STATE.PAN ) {
if ( scope.enablePan === false ) return;
handleMouseMovePan( event );
}
}
function onMouseUp( event ) {
if ( scope.enabled === false ) return;
handleMouseUp( event );
document.removeEventListener( 'mousemove', onMouseMove, false );
document.removeEventListener( 'mouseup', onMouseUp, false );
document.removeEventListener( 'mouseout', onMouseUp, false );
scope.dispatchEvent( endEvent );
state = STATE.NONE;
}
function onMouseWheel( event ) {
if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return;
event.preventDefault();
event.stopPropagation();
handleMouseWheel( event );
scope.dispatchEvent( startEvent ); // not sure why these are here...
scope.dispatchEvent( endEvent );
}
function onKeyDown( event ) {
if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
handleKeyDown( event );
}
function onTouchStart( event ) {
if ( scope.enabled === false ) return;
switch ( event.touches.length ) {
case 1: // one-fingered touch: rotate
if ( scope.enableRotate === false ) return;
handleTouchStartRotate( event );
state = STATE.TOUCH_ROTATE;
break;
case 2: // two-fingered touch: dolly
if ( scope.enableZoom === false ) return;
handleTouchStartDolly( event );
state = STATE.TOUCH_DOLLY;
break;
case 3: // three-fingered touch: pan
if ( scope.enablePan === false ) return;
handleTouchStartPan( event );
state = STATE.TOUCH_PAN;
break;
default:
state = STATE.NONE;
}
if ( state !== STATE.NONE ) {
scope.dispatchEvent( startEvent );
}
}
function onTouchMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
switch ( event.touches.length ) {
case 1: // one-fingered touch: rotate
if ( scope.enableRotate === false ) return;
if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?...
handleTouchMoveRotate( event );
break;
case 2: // two-fingered touch: dolly
if ( scope.enableZoom === false ) return;
if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?...
handleTouchMoveDolly( event );
break;
case 3: // three-fingered touch: pan
if ( scope.enablePan === false ) return;
if ( state !== STATE.TOUCH_PAN ) return; // is this needed?...
handleTouchMovePan( event );
break;
default:
state = STATE.NONE;
}
}
function onTouchEnd( event ) {
if ( scope.enabled === false ) return;
handleTouchEnd( event );
scope.dispatchEvent( endEvent );
state = STATE.NONE;
}
function onContextMenu( event ) {
event.preventDefault();
}
//
scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
scope.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
scope.domElement.addEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
window.addEventListener( 'keydown', onKeyDown, false );
// force an update at start
this.update();
};
THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
Object.defineProperties( THREE.OrbitControls.prototype, {
center: {
get: function () {
console.warn( 'THREE.OrbitControls: .center has been renamed to .target' );
return this.target;
}
},
// backward compatibility
noZoom: {
get: function () {
console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
return ! this.enableZoom;
},
set: function ( value ) {
console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
this.enableZoom = ! value;
}
},
noRotate: {
get: function () {
console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
return ! this.enableRotate;
},
set: function ( value ) {
console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
this.enableRotate = ! value;
}
},
noPan: {
get: function () {
console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
return ! this.enablePan;
},
set: function ( value ) {
console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
this.enablePan = ! value;
}
},
noKeys: {
get: function () {
console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
return ! this.enableKeys;
},
set: function ( value ) {
console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
this.enableKeys = ! value;
}
},
staticMoving : {
get: function () {
console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
return ! this.constraint.enableDamping;
},
set: function ( value ) {
console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
this.constraint.enableDamping = ! value;
}
},
dynamicDampingFactor : {
get: function () {
console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
return this.constraint.dampingFactor;
},
set: function ( value ) {
console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
this.constraint.dampingFactor = value;
}
}
} );
</script>
When the tween is completed and the controls of camera return enabled the rotation of camera not respect the pivot center of element but follow to take as center the old element position....
Anybody can help me?
Thank's a lot!
enter code here
Related
PointerLockControls threejs "null is not an object (evaluating 'instructions.addEventListener')"
I am trying to integrate PointerLockControls into my project. I am using the example from the THREEJS examples page and I've basically copied the exact thing over and it still gives me errors in console and won't run. Im using a basic code structure and am importing three via node module and express server. this is my main html code: <!DOCTYPE html> <html lang="de"> <head> <meta charset="UTF-8"> <title>-</title> <!--<link rel="stylesheet" href="../css/main.css"> --> <style> body { margin: 0; overflow: hidden; } .blocker { position: absolute; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); } .instructions { width: 100%; height: 100%; display: -webkit-box; display: -moz-box; display: box; -webkit-box-orient: horizontal; -moz-box-orient: horizontal; box-orient: horizontal; -webkit-box-pack: center; -moz-box-pack: center; box-pack: center; -webkit-box-align: center; -moz-box-align: center; box-align: center; color: #ffffff; text-align: center; font-family: Arial, sans-serif; font-size: 14px; line-height: 24px; cursor: pointer; } </style> </head> <body> <div class="blocker"> <div class="instructions"> <span style="font-size:36px">Click to play</span> <br /><br /> Move: WASD<br/> Jump: SPACE<br/> Look: MOUSE </div> </div> <script type="module" src="client.js"> </script> </body> </html> and here's my client.js file: //---------- Imports import * as THREE from '/build/three.module.js'; //import {OrbitControls} from '/jsm/controls/OrbitControls.js'; import {PointerLockControls} from '/jsm/controls/PointerLockControls.js'; import Stats from '/jsm/libs/stats.module.js'; //---------- Setup let camera, scene, renderer, controls; const objects = []; let raycaster; let moveForward = false; let moveBackward = false; let moveLeft = false; let moveRight = false; let canJump = false; let prevTime = performance.now(); const velocity = new THREE.Vector3(); const direction = new THREE.Vector3(); const vertex = new THREE.Vector3(); const color = new THREE.Color(); init(); animate(); //---------- Controls function init() { camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000 ); camera.position.y = 10; scene = new THREE.Scene(); scene.background = new THREE.Color( 0xffffff ); scene.fog = new THREE.Fog( 0xffffff, 0, 750 ); const light = new THREE.HemisphereLight( 0xeeeeff, 0x777788, 0.75 ); light.position.set( 0.5, 1, 0.75 ); scene.add( light ); controls = new PointerLockControls( camera, document.body ); const blocker = document.getElementById( 'blocker' ); const instructions = document.getElementById( 'instructions' ); instructions.addEventListener( 'click', function () { controls.lock(); } ); controls.addEventListener( 'lock', function () { instructions.style.display = 'none'; blocker.style.display = 'none'; } ); controls.addEventListener( 'unlock', function () { blocker.style.display = 'block'; instructions.style.display = ''; } ); scene.add( controls.getObject() ); const onKeyDown = function ( 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 ) velocity.y += 350; canJump = false; break; } }; const onKeyUp = function ( 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; } }; document.addEventListener( 'keydown', onKeyDown ); document.addEventListener( 'keyup', onKeyUp ); raycaster = new THREE.Raycaster( new THREE.Vector3(), new THREE.Vector3( 0, - 1, 0 ), 0, 10 ); // floor let floorGeometry = new THREE.PlaneGeometry( 2000, 2000, 100, 100 ); floorGeometry.rotateX( - Math.PI / 2 ); // vertex displacement let position = floorGeometry.attributes.position; for ( let i = 0, l = position.count; i < l; i ++ ) { vertex.fromBufferAttribute( position, i ); vertex.x += Math.random() * 20 - 10; vertex.y += Math.random() * 2; vertex.z += Math.random() * 20 - 10; position.setXYZ( i, vertex.x, vertex.y, vertex.z ); } floorGeometry = floorGeometry.toNonIndexed(); // ensure each face has unique vertices position = floorGeometry.attributes.position; const colorsFloor = []; for ( let i = 0, l = position.count; i < l; i ++ ) { color.setHSL( Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75 ); colorsFloor.push( color.r, color.g, color.b ); } floorGeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colorsFloor, 3 ) ); const floorMaterial = new THREE.MeshBasicMaterial( { vertexColors: true } ); const floor = new THREE.Mesh( floorGeometry, floorMaterial ); scene.add( floor ); // objects const boxGeometry = new THREE.BoxGeometry( 20, 20, 20 ).toNonIndexed(); position = boxGeometry.attributes.position; const colorsBox = []; for ( let i = 0, l = position.count; i < l; i ++ ) { color.setHSL( Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75 ); colorsBox.push( color.r, color.g, color.b ); } boxGeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colorsBox, 3 ) ); for ( let i = 0; i < 500; i ++ ) { const boxMaterial = new THREE.MeshPhongMaterial( { specular: 0xffffff, flatShading: true, vertexColors: true } ); boxMaterial.color.setHSL( Math.random() * 0.2 + 0.5, 0.75, Math.random() * 0.25 + 0.75 ); const box = new THREE.Mesh( boxGeometry, boxMaterial ); box.position.x = Math.floor( Math.random() * 20 - 10 ) * 20; box.position.y = Math.floor( Math.random() * 20 ) * 20 + 10; box.position.z = Math.floor( Math.random() * 20 - 10 ) * 20; scene.add( box ); objects.push( box ); } // renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); // window.addEventListener( 'resize', onWindowResize ); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); } function animate() { requestAnimationFrame( animate ); const time = performance.now(); if ( controls.isLocked === true ) { raycaster.ray.origin.copy( controls.getObject().position ); raycaster.ray.origin.y -= 10; const intersections = raycaster.intersectObjects( objects ); const onObject = intersections.length > 0; const delta = ( time - prevTime ) / 1000; velocity.x -= velocity.x * 10.0 * delta; velocity.z -= velocity.z * 10.0 * delta; velocity.y -= 9.8 * 100.0 * delta; // 100.0 = mass direction.z = Number( moveForward ) - Number( moveBackward ); direction.x = Number( moveRight ) - Number( moveLeft ); direction.normalize(); // this ensures consistent movements in all directions if ( moveForward || moveBackward ) velocity.z -= direction.z * 400.0 * delta; if ( moveLeft || moveRight ) velocity.x -= direction.x * 400.0 * delta; if ( onObject === true ) { velocity.y = Math.max( 0, velocity.y ); canJump = true; } controls.moveRight( - velocity.x * delta ); controls.moveForward( - velocity.z * delta ); controls.getObject().position.y += ( velocity.y * delta ); // new behavior if ( controls.getObject().position.y < 10 ) { velocity.y = 0; controls.getObject().position.y = 10; canJump = true; } } prevTime = time; renderer.render( scene, camera ); } here's my app.js with a basic expression server setup const express = require('express') const app = express() const path = require('path') app.use(express.static(__dirname + '/public')) app.use('/build', express.static(path.join(__dirname, 'node_modules/three/build'))); app.use('/jsm/', express.static(path.join(__dirname, 'node_modules/three/examples/jsm'))); app.listen(3000, () => console.log('Visit http://127.0.0.1:3000') ); and here ist the console error im experiencing: [Error] TypeError: null is not an object (evaluating 'instructions.addEventListener') init (client.js:61) Modulcode (client.js:33) evaluate moduleEvaluation (anonyme Funktion) promiseReactionJob Im probably sitting on a basic mistake but still can't solve it. Ive been also battling (won the battle tho) to understand how to integrate threejs via es6 module so maybe the mistake is somewhere around there. Im happy to provide further information on my document structure if needed. Thanks a lot. David
You have this markup: <div class="instructions"> And this JS code: const instructions = document.getElementById( 'instructions' ); You can't query the DOM element in this way since it has no id attribute. You need this: <div id="instructions"> I am using the example from the THREEJS examples page and I've basically copied the exact thing over Well, the example does properly use the id attribute. https://github.com/mrdoob/three.js/blob/94f043c4e105eb73236529231388402da2b07cba/examples/misc_controls_pointerlock.html#L49
Javascript Float Becoming NaN After Use
I have the following class function Sprite( sprite, frameWidth, frameHeight ) { this.frameWidth = frameWidth; this.frameHeight = frameHeight; this.sprite = sprite; this.cycles = new Array( [ new AnimationPoint( 0, 0 ) ] ); this.currentFrame = 0; this.currentCycle = this.cycles[ 0 ]; this.scaleFactor = 1; this.pause = false; this.x = 0.0; this.y = 0.0; this.orientation = 0.0; this.transformQue = new Array(); this.QueRotation = function( radians ) { this.transformQue.push( radians ); } this.AddAnimationCycle = function( animationPoints ) { this.cycles.push( animationPoints ); } this.Pause = function() { this.pause = true; } this.UnPause = function() { this.pause = false; } this.SelectAnimationCycle = function( cycle ) { this.currentFrame = 0; this.currentCycle = this.cycles[ cycle ]; } this.Forward = function() { this.x += Math.cos( this.orientation ) * grid.UnitToPixelX( canvas ); this.y += Math.sin( this.orientation ) * grid.UnitToPixelY( canvas ); } this.Animate = function () { renderContext.save(); var rotation = this.transformQue.pop(); var x = ( ( this.frameWidth * this.scaleFactor ) / 2 ); var y = ( ( this.frameHeight * this.scaleFactor ) / 2 ); renderContext.translate( this.x + x, this.y + y ); renderContext.rotate( this.orientation += rotation ); renderContext.translate( -( this.x + x ), -( this.y + y ) ); renderContext.drawImage( this.sprite, this.currentCycle[ this.currentFrame ].x * this.frameWidth, this.currentCycle[ this.currentFrame ].y * this.frameHeight, this.frameWidth, this.frameHeight, this.x, this.y, this.frameWidth * this.scaleFactor, this.frameHeight * this.scaleFactor ); renderContext.restore(); if( this.pause == false ) { ++this.currentFrame; if( this.currentFrame >= this.currentCycle.length ) this.currentFrame = 0; } } } If I in my render function, do something like mySprite.QueRotation( .1 ) It works forever. However, if I have a function that calls QueRotation such as function MySpriteClockWise() { mySprite.QueRotation( Math.PI / 2 ); } It goes to NaN. Stranger still, this.orientation = 0.0; however if I don't touch the QueRotation function inside my render function, it goes to NaN! Even after being assigned simply to zero and having not touched it! If I do a function such as: starting = 0; function MySpriteStart() { if( starting++ < 4 ) MySpriteClockWise(); } and watch the value in the console, it calculates just fine for as many times as I want to run the function, but as soon as it is done in the render function, it becomes NaN! Its as if JS doesent think I need the value anymore, even if I make a function like: this.Forward = function() { this.x += Math.cos( this.orientation ) * grid.UnitToPixelX( canvas ); this.y += Math.sin( this.orientation ) * grid.UnitToPixelY( canvas ); } to try to coax it into not throwing the value away, it still does! This is a big problem, because I am scripting through lua the whole point of the project is for (a coding tutorial) users to be able to script to control "mySprite" (a charachter) through lua to learn! This behavior is in both firefox and chromium. Please help!
When calling mySprite.QueRotation( .1 ) the this inside QueRotation function doesn't point to mySprite object. Use arrow function instead this.QueRotation = ( radians ) => { this.transformQue.push( radians ); } Edit: It seems you do not have ES6 support. In this case use this when calling QueRotation function mySprite.QueRotation.call(mySprite, .1)
Found a bug that explained the issue, I was hasty, it seemed like something to conserve on resources too me, I am sorry.
Intersects array empty in Three JS
I want to set a different color to an object, which I selected in Three JS. I am getting the x and y position of the mouse, but the function raycaster.intersectObjects() always returns an empty array. I am loading the data here and filling the objects array: var objects = []; var mousev,raycaster, scene, renderer, controls, loader; //Load Data loader = new THREE.ObjectLoader(); loader.load("models/selectableObjects.json", function (obj) { scene.add(obj); scene.traverse(function(children){ objects.push(children); }); }, function (xhr) { console.log((xhr.loaded / xhr.total * 100) + '% loaded'); }, function (err) { console.error('An error happened'); } ); That's my mouse down Event listener: raycaster = new THREE.Raycaster(); mousev = new THREE.Vector2(); document.addEventListener('mousedown', onDocumentMouseDown, false); function onDocumentMouseDown(event){ event.preventDefault(); mousev.x = (event.clientX / renderer.domElement.width) * 2 - 1; console.log("Mouse position X: "+mousev.x); mousev.y = (event.clientY / renderer.domElement.height) * 2 + 1; console.log("Mouse position Y: "+mousev.y); raycaster.setFromCamera( mousev, camera ); var intersects = raycaster.intersectObjects( objects ); console.log("Objects: "); console.log(objects); console.log("Intersects: "); console.log(intersects); var color = (Math.random() * 0xffffff); for ( var i = 0; i < intersects.length; i++ ) { intersects[i].object.material.color.set( 0xff0000 ); } } Thank you for your help.
Optimize hundreds of dynamic lines in Three.js
I'm new to Three.js, I'm just having fun with it. I'm trying to achieve a simple dynamic background fullscreen on a page, you get the example here: function createHexagon( vertices, color ) { var geometry = new THREE.BufferGeometry(); geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); var material = new THREE.LineBasicMaterial( { color: color, opacity: Math.min((Math.random() / 5), 0.1), transparent: true } ); var hexagon = new THREE.Line( geometry, material ); return hexagon; } function initMatrix() { var color = defaultColor.getHex(); var vertices; var x = ( width / -2 ) - 90; var y = height / -2; var deltaX = 120; var deltaY = 60; var time = 5.0; while( y < height / 2 ) { while( x < width / 2 ) { vertices = new Float32Array([ 0, 30, 0, 20, 0, 0 ]); var hexagon = createHexagon( vertices, color ); scene.add( hexagon ); hexagon.position.set( x, y, 0 ); vertices = new Float32Array([ 20, 0, 0, 60, 0, 0 ]); var hexagon = createHexagon( vertices, color ); scene.add( hexagon ); hexagon.position.set( x, y, 0 ); vertices = new Float32Array([ 60, 0, 0, 80, 30, 0 ]); var hexagon = createHexagon( vertices, color ); scene.add( hexagon ); hexagon.position.set( x, y, 0 ); x += deltaX; } x = ( width / -2 ) - 90; y += deltaY; } x = ( width / -2 ) - 30; y = ( height / -2 ) - 30; deltaX = 120; deltaY = 60; while( y < height / 2 ) { while( x < width / 2 ) { vertices = new Float32Array([ 0, 30, 0, 20, 0, 0 ]); var hexagon = createHexagon( vertices, color ); scene.add( hexagon ); hexagon.position.set( x, y, 0 ); vertices = new Float32Array([ 20, 0, 0, 60, 0, 0 ]); var hexagon = createHexagon( vertices, color ); scene.add( hexagon ); hexagon.position.set( x, y, 0 ); vertices = new Float32Array([ 60, 0, 0, 80, 30, 0 ]); var hexagon = createHexagon( vertices, color ); scene.add( hexagon ); hexagon.position.set( x, y, 0 ); x += deltaX; } x = ( width / -2 ) - 30; y += deltaY; } } Those are single bufferGeometry lines (as you can see in the above functions to create the background) that randomly rotate and change opacity on mouse hover with raycaster and TweenLite. It works pretty fine. What you can notice is that CPU usage goes to almost 100%. I know that if I group lines into the same geometry it'll be better on performance, but then I'm not able to animate single lines with raycaster, especially the opacity. I searched a lot of discussions and tried so many things. The best result is this way, rendering single lines separately. Can you suggest some tips about it?
(Posted on behalf of the question author). I found a solution. I made a whole geometry and I indexed vertices and respective colors. The only problem is that I can't mange opacity this way, but it works fine and CPU is around 20%. document.addEventListener( 'DOMContentLoaded', main ); var width = screen.width; var height = screen.height; var camera, scene, renderer, raycaster, mouse; var rayColor = new THREE.Color( 0x0640C2 ); var rayColor2 = new THREE.Color( 0xCC311B ); var colors; var linesPositions, originalPositions; var linesMesh; var geometry; var intersects; var stats; function initMatrix() { var AlinesPositions = [ ]; var x = ( width / - 2 ) - 80; var y = ( height / - 2 ) - 60; var deltaX = 120; var deltaY = 60; while( y <= ( height / 2 ) ) { while( x <= ( width / 2 ) ) { AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); x += deltaX; } x = ( width / -2 ) - 80; y += deltaY; } x = ( width / - 2 ); y = ( height / - 2 ) - 60; while( y <= ( height / 2 ) ) { while( x <= ( width / 2 ) ) { AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); x += deltaX; } x = ( width / -2 ); y += deltaY; } linesPositions = new Float32Array( AlinesPositions ); var Acolors = [ ]; for( var i = 0; i < AlinesPositions.length; i++ ) { var fact = Math.random() * 20.0; var ran = Math.min( Math.max( ( Math.random() / fact ), 0.01 ), 0.5 ); Acolors[i] = ran; Acolors[i+1] = ran; Acolors[i+2] = ran; i += 2; } colors = new Float32Array( Acolors ); geometry = new THREE.BufferGeometry(); geometry.addAttribute( 'position', new THREE.BufferAttribute( linesPositions, 3 ).setDynamic( true ) ); geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).setDynamic( true ) ); var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, blending: THREE.AdditiveBlending, transparent: true } ); linesMesh = new THREE.LineSegments( geometry, material ); scene.add( linesMesh ); originalPositions = new THREE.BufferAttribute( linesPositions, 3 ); originalPositions.copy( linesMesh.geometry.attributes.position ); } function init() { scene = new THREE.Scene(); //camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 100 ); camera = new THREE.PerspectiveCamera( 120, width / height, 1, 1000 ); camera.position.set( 0, 0, 200 ); camera.lookAt( 0, 0, 0 ); // Create matrix initMatrix(); raycaster = new THREE.Raycaster(); raycaster.linePrecision = 20; mouse = new THREE.Vector2(); renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize( width, height ); document.body.appendChild( renderer.domElement ); } function main() { init(); animate(); } function animate() { requestAnimationFrame( animate ); render(); } var intersected = []; function removeX( i ) { intersected.splice( i, 1 ); } function alterate( index ) { var randColor = ( Math.random() <= 0.49 ) ? rayColor : rayColor2; // Change color var currentC = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index )); var tweenC = TweenLite.to( currentC, 1.0, { r: randColor.r, g: randColor.g, b: randColor.b, immediateRender: true, lazy: false, onUpdate: function() { linesMesh.geometry.attributes.color.setXYZ( index, currentC.r, currentC.g, currentC.b ); linesMesh.geometry.attributes.color.needsUpdate = true; }, onUpdateParams: [ index, currentC ] }); // Change coordinates var currentXY = { x: originalPositions.getX( index ), y: originalPositions.getY( index ), z: originalPositions.getZ( index ) }; var goal = [ ( currentXY.x + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.y + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.z + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ) ]; var tweenXY = TweenLite.to( currentXY, 1.0, { x: goal[ 0 ], y: goal[ 1 ], z: goal[ 2 ], immediateRender: true, lazy: false, onUpdate: function() { linesMesh.geometry.attributes.position.setXYZ( index, currentXY.x, currentXY.y, currentXY.z ); linesMesh.geometry.attributes.position.needsUpdate = true; }, onUpdateParams: [ index, currentXY ] }); } function render() { // update the picking ray with the camera and mouse position raycaster.setFromCamera( mouse, camera ); intersects = raycaster.intersectObject( linesMesh ); if( intersects.length ) { if( !intersected.includes( intersects[ 0 ].index ) ) { for( var x = 0; x < intersects.length; x++ ) { if( !intersected.includes( intersects[ x ].index ) ) { var index = intersects[ x ].index; // Save index intersected.push( index ); alterate( index ); } } var present = 0; for( var y = 0; y < intersected.length; y++ ) { for( var j = 0; j < intersects.length; j++ ) { if( intersects[ j ].index == intersected[ y ] ) { present = 1; break; } } if( !present ) { ( function( y ) { var randC = Math.min( Math.max( ( Math.random() / ( Math.random() * 20.0 ) ), 0.01 ), 0.5 ); // Current item old coordinates let index = intersected[ y ]; var indexX = originalPositions.getX( index ); var indexY = originalPositions.getY( index ); var indexZ = originalPositions.getZ( index ); removeX( y ); // Reset color var currentCreset = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index )); var tweenCreset = TweenLite.to( currentCreset, 1.5, { r: randC, g: randC, b: randC, immediateRender: true, lazy: false, onUpdate: function() { linesMesh.geometry.attributes.color.setXYZ( index, currentCreset.r, currentCreset.g, currentCreset.b ); linesMesh.geometry.attributes.color.needsUpdate = true; }, onUpdateParams: [ index, currentCreset ], onComplete: function() { } } ); // Reset coordinates var currentXYreset = { x: linesMesh.geometry.attributes.position.getX( index ), y: linesMesh.geometry.attributes.position.getY( index ), z: linesMesh.geometry.attributes.position.getZ( index ) }; var tweenXYreset = TweenLite.to( currentXYreset, 1.5, { x: indexX, y: indexY, z: indexZ, immediateRender: true, lazy: false, onUpdate: function() { linesMesh.geometry.attributes.position.setXYZ( index, currentXYreset.x, currentXYreset.y, currentXYreset.z ); linesMesh.geometry.attributes.position.needsUpdate = true; }, onUpdateParams: [ index, currentXYreset ] }); }) ( y ); } } } } renderer.render( scene, camera ); } function onMouseMove( event ) { // calculate mouse position in normalized device coordinates // (-1 to +1) for both components event.preventDefault(); mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1; mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1; var normX = event.clientX - ( window.innerWidth / 2 ); var normY = event.clientY - ( window.innerHeight / 2 ); camera.position.set( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 200 ); camera.lookAt( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 0 ); } window.addEventListener( 'click', function() { if( intersected.length ) { for( var x = 0; x < intersected.length; x++ ) { var index = intersected[ x ]; alterate( index ); } } }); window.addEventListener( 'mousemove', onMouseMove, false ); // Set new dimensions for scene when resize window window.addEventListener( 'resize', function() { var wWidth = window.innerWidth; var wHeight = window.innerHeight; /*camera.left = wWidth / -2; camera.right = wWidth / 2; camera.top = wHeight / 2; camera.bottom = wHeight / -2;*/ camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); raycaster.setFromCamera( mouse, camera ); renderer.setSize( window.innerWidth, window.innerHeight ); } ); Here is a Codepen link.
I found a solution. I made a whole geometry and I indexed vertices and respective colors. The only problem is that I can't mange opacity this way, but it works fine and CPU is around 20%. document.addEventListener( 'DOMContentLoaded', main ); var width = screen.width; var height = screen.height; var camera, scene, renderer, raycaster, mouse; var rayColor = new THREE.Color( 0x0640C2 ); var rayColor2 = new THREE.Color( 0xCC311B ); var colors; var linesPositions, originalPositions; var linesMesh; var geometry; var intersects; var stats; function initMatrix() { var AlinesPositions = [ ]; var x = ( width / - 2 ) - 80; var y = ( height / - 2 ) - 60; var deltaX = 120; var deltaY = 60; while( y <= ( height / 2 ) ) { while( x <= ( width / 2 ) ) { AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); x += deltaX; } x = ( width / -2 ) - 80; y += deltaY; } x = ( width / - 2 ); y = ( height / - 2 ) - 60; while( y <= ( height / 2 ) ) { while( x <= ( width / 2 ) ) { AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ ); x += deltaX; } x = ( width / -2 ); y += deltaY; } linesPositions = new Float32Array( AlinesPositions ); var Acolors = [ ]; for( var i = 0; i < AlinesPositions.length; i++ ) { var fact = Math.random() * 20.0; var ran = Math.min( Math.max( ( Math.random() / fact ), 0.01 ), 0.5 ); Acolors[i] = ran; Acolors[i+1] = ran; Acolors[i+2] = ran; i += 2; } colors = new Float32Array( Acolors ); geometry = new THREE.BufferGeometry(); geometry.addAttribute( 'position', new THREE.BufferAttribute( linesPositions, 3 ).setDynamic( true ) ); geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).setDynamic( true ) ); var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, blending: THREE.AdditiveBlending, transparent: true } ); linesMesh = new THREE.LineSegments( geometry, material ); scene.add( linesMesh ); originalPositions = new THREE.BufferAttribute( linesPositions, 3 ); originalPositions.copy( linesMesh.geometry.attributes.position ); } function init() { scene = new THREE.Scene(); //camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 100 ); camera = new THREE.PerspectiveCamera( 120, width / height, 1, 1000 ); camera.position.set( 0, 0, 200 ); camera.lookAt( 0, 0, 0 ); // Create matrix initMatrix(); raycaster = new THREE.Raycaster(); raycaster.linePrecision = 20; mouse = new THREE.Vector2(); renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize( width, height ); document.body.appendChild( renderer.domElement ); } function main() { init(); animate(); } function animate() { requestAnimationFrame( animate ); render(); } var intersected = []; function removeX( i ) { intersected.splice( i, 1 ); } function alterate( index ) { var randColor = ( Math.random() <= 0.49 ) ? rayColor : rayColor2; // Change color var currentC = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index )); var tweenC = TweenLite.to( currentC, 1.0, { r: randColor.r, g: randColor.g, b: randColor.b, immediateRender: true, lazy: false, onUpdate: function() { linesMesh.geometry.attributes.color.setXYZ( index, currentC.r, currentC.g, currentC.b ); linesMesh.geometry.attributes.color.needsUpdate = true; }, onUpdateParams: [ index, currentC ] }); // Change coordinates var currentXY = { x: originalPositions.getX( index ), y: originalPositions.getY( index ), z: originalPositions.getZ( index ) }; var goal = [ ( currentXY.x + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.y + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.z + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ) ]; var tweenXY = TweenLite.to( currentXY, 1.0, { x: goal[ 0 ], y: goal[ 1 ], z: goal[ 2 ], immediateRender: true, lazy: false, onUpdate: function() { linesMesh.geometry.attributes.position.setXYZ( index, currentXY.x, currentXY.y, currentXY.z ); linesMesh.geometry.attributes.position.needsUpdate = true; }, onUpdateParams: [ index, currentXY ] }); } function render() { // update the picking ray with the camera and mouse position raycaster.setFromCamera( mouse, camera ); intersects = raycaster.intersectObject( linesMesh ); if( intersects.length ) { if( !intersected.includes( intersects[ 0 ].index ) ) { for( var x = 0; x < intersects.length; x++ ) { if( !intersected.includes( intersects[ x ].index ) ) { var index = intersects[ x ].index; // Save index intersected.push( index ); alterate( index ); } } var present = 0; for( var y = 0; y < intersected.length; y++ ) { for( var j = 0; j < intersects.length; j++ ) { if( intersects[ j ].index == intersected[ y ] ) { present = 1; break; } } if( !present ) { ( function( y ) { var randC = Math.min( Math.max( ( Math.random() / ( Math.random() * 20.0 ) ), 0.01 ), 0.5 ); // Current item old coordinates let index = intersected[ y ]; var indexX = originalPositions.getX( index ); var indexY = originalPositions.getY( index ); var indexZ = originalPositions.getZ( index ); removeX( y ); // Reset color var currentCreset = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index )); var tweenCreset = TweenLite.to( currentCreset, 1.5, { r: randC, g: randC, b: randC, immediateRender: true, lazy: false, onUpdate: function() { linesMesh.geometry.attributes.color.setXYZ( index, currentCreset.r, currentCreset.g, currentCreset.b ); linesMesh.geometry.attributes.color.needsUpdate = true; }, onUpdateParams: [ index, currentCreset ], onComplete: function() { } } ); // Reset coordinates var currentXYreset = { x: linesMesh.geometry.attributes.position.getX( index ), y: linesMesh.geometry.attributes.position.getY( index ), z: linesMesh.geometry.attributes.position.getZ( index ) }; var tweenXYreset = TweenLite.to( currentXYreset, 1.5, { x: indexX, y: indexY, z: indexZ, immediateRender: true, lazy: false, onUpdate: function() { linesMesh.geometry.attributes.position.setXYZ( index, currentXYreset.x, currentXYreset.y, currentXYreset.z ); linesMesh.geometry.attributes.position.needsUpdate = true; }, onUpdateParams: [ index, currentXYreset ] }); }) ( y ); } } } } renderer.render( scene, camera ); } function onMouseMove( event ) { // calculate mouse position in normalized device coordinates // (-1 to +1) for both components event.preventDefault(); mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1; mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1; var normX = event.clientX - ( window.innerWidth / 2 ); var normY = event.clientY - ( window.innerHeight / 2 ); camera.position.set( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 200 ); camera.lookAt( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 0 ); } window.addEventListener( 'click', function() { if( intersected.length ) { for( var x = 0; x < intersected.length; x++ ) { var index = intersected[ x ]; alterate( index ); } } }); window.addEventListener( 'mousemove', onMouseMove, false ); // Set new dimensions for scene when resize window window.addEventListener( 'resize', function() { var wWidth = window.innerWidth; var wHeight = window.innerHeight; /*camera.left = wWidth / -2; camera.right = wWidth / 2; camera.top = wHeight / 2; camera.bottom = wHeight / -2;*/ camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); raycaster.setFromCamera( mouse, camera ); renderer.setSize( window.innerWidth, window.innerHeight ); } ); Here is a Codepen link: Example
move the camera to face the selected object 3js
so this is the first time i try to learn how to create web with 3 js and tween js. i created multiple object when user click 1 of object it will display its content. but when i try to move the camera to face the object (zooming and focus on the selected the object), the camera just jump to the top of canvas and scan all of the canvas. (function() { var DZOOM, OX, OY, aspectt, camera, height, light, loader, render, renderer, scene, view, width, raycaster, intersects, INTERSECTED; var objects = [], uuids = [], names = []; var container = document.getElementById( 'main-content' ); var windowHalfX = container.offsetWidth / 2; var windowHalfY = container.offsetHeight / 2; width = container.offsetWidth; height = container.offsetHeight; var raycaster = new THREE.Raycaster(); var lookAtVector = new THREE.Vector3(0,0,0); var mouse = new THREE.Vector2(), INTERSECTED; document.body.appendChild( container ); aspectt = width / height; scene = new THREE.Scene(); scene.background = new THREE.Color( '#fff' ); camera = new THREE.PerspectiveCamera(85, window.innerWidth / window.innerHeight, 1, 1000); renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); window.addEventListener( 'resize', onWindowResize, false ); function onWindowResize() { windowHalfX = container.offsetWidth / 2; windowHalfY = container.offsetHeight / 2; camera.aspect = container.offsetWidth / container.offsetHeight; camera.updateProjectionMatrix(); renderer.setSize( container.offsetWidth, container.offsetHeight ); } renderer.setSize(width, height); container.appendChild( renderer.domElement ); light = new THREE.DirectionalLight(0xffffff, 1); light.position.set(15, -5, 15); scene.add(light); OX = -35; OY = -5; if (!deviceIsMobile()) { camera.position.set(OX + 60, OY - 5, 35); } else{ camera.position.set(OX + 30, OY - 10, 20); } camera.up = new THREE.Vector3(0, 0, 1); camera.lookAt(new THREE.Vector3(OX, OY, 0)); container.addEventListener( 'mousedown', onDocumentMouseDown, false ); container.addEventListener( 'touchstart', onDocumentTouchStart, false ); container.addEventListener( 'touchmove', onDocumentTouchMove, false ); function displayCaptionOnClick( event ) { event.preventDefault(); mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1; mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1; raycaster.setFromCamera( mouse, camera ); intersects = raycaster.intersectObjects( objects[0].children, true ); if ( intersects.length > 0 ) { for( var i = 0; i < intersects.length; i++ ) { displayCaption(intersects[0].object.uuid); } } } function redOnHover( event ) { event.preventDefault(); mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1; mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1; raycaster.setFromCamera( mouse, camera ); intersects = raycaster.intersectObjects( objects[0].children, true ); if ( intersects.length > 0 ) { if ( INTERSECTED != intersects[ 0 ].object ) { if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex ); INTERSECTED = intersects[ 0 ].object; INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex(); INTERSECTED.material.emissive.setHex( 0xff0000 ); } } else { if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex ); INTERSECTED = null; } } function toObj(obj) { console.log(obj); var normalMatrix = new THREE.Matrix3().getNormalMatrix( obj.matrixWorld ); var worldNormal = new THREE.Vector3(0,0,1).applyMatrix3( normalMatrix ).normalize(); console.log(worldNormal); var camPosition = new THREE.Vector3().copy(obj.position).add(worldNormal.multiplyScalar(100)); var rotateTween = new TWEEN.Tween(lookAtVector) .to({ x: obj.position.x, y: obj.position.y, z: obj.position.z }, 4000) .easing(TWEEN.Easing.Quadratic.InOut) .onUpdate(function(){ camera.lookAt(lookAtVector); }) .onComplete(function(){ lookAtVector.copy(obj.position); }) .start(); var goTween = new TWEEN.Tween(camera.position) .to(camPosition, 4000) .easing(TWEEN.Easing.Quadratic.InOut) .start(); } function displayCaption( uuid ) { var curObj = uuids.indexOf(uuid); console.log(curObj); if(curObj<=7){ $('.single-page').hide(); var crnt= $('#'+curObj); crnt.show(); $('#sidebar').animate({right:0}); redOnHover( event ); } } var targetRotation = 0; var targetRotationOnMouseDown = 0; var mouseX = 0; var mouseXOnMouseDown = 0; function animate() { requestAnimationFrame(animate); render(); } render = function() { TWEEN.update(); camera.updateMatrixWorld(); return renderer.render(scene, camera); }; function onDocumentMouseDown( event ) { event.preventDefault(); container.addEventListener( 'mousemove', onDocumentMouseMove, false ); container.addEventListener( 'mouseup', onDocumentMouseUp, false ); container.addEventListener( 'mouseout', onDocumentMouseOut, false ); mouseXOnMouseDown = event.clientX - windowHalfX; targetRotationOnMouseDown = targetRotation; checkClick(event); displayCaptionOnClick( event ); toObj(INTERSECTED); } var myObj; loader = new THREE.ObjectLoader(); loader.load("js/try.dae.json",function ( obj ) { if (!deviceIsMobile()) { obj.scale.x = obj.scale.y = obj.scale.z = 2.3; } else{ obj.scale.x = obj.scale.y = obj.scale.z = 0.8; } // FIXED obj.position.x = -15; obj.position.y = -5; obj.position.z = 24; obj.rotation.y = 0.15; obj.rotation.z = 0.6; myObj = obj; scene.add( obj ); objects.push(obj); console.log(objects); var exclude = ["Cylinder_007", "Cube_005", "Cube_004", "Cylinder_006", "Cylinder_005", "Cylinder_004", "Cylinder_003", "Cylinder_002", "Plane_004", "Plane_003", "Plane_002", "Cube_003", "Cube_002", "Cube", "Cube_001", "Cylinder_001", "Plane_001", "Cylinder", "Torus_001", "Text"]; for( var i = 0; i < objects[0].children.length; i++ ) { if ( exclude.indexOf(objects[0].children[i].name) < 0 ) { uuids.push(objects[0].children[i].children[0].uuid); } } for( var i = 0; i < objects[0].children.length; i++ ) { uuids.push(objects[0].children[i].children[0].uuid); } return render(); }); animate(); }).call(this);