Last active
November 29, 2021 02:44
-
-
Save mrflix/8351020 to your computer and use it in GitHub Desktop.
Three.OrbitControls for multiple instances on a page.Usage: controls = new THREE.OrbitControls(camera, document, renderer.domElement); Based on: http://threejs.org/examples/js/controls/OrbitControls.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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 | |
* @author mrflix / http://felixniklas.de | |
* | |
* released under MIT License (MIT) | |
*/ | |
/*global THREE, console */ | |
// This set of controls performs orbiting, dollying (zooming), and panning. It maintains | |
// the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is | |
// supported. | |
// | |
// 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 | |
// | |
// This is a drop-in replacement for (most) TrackballControls used in examples. | |
// That is, include this js file and wherever you see: | |
// controls = new THREE.TrackballControls( camera ); | |
// controls.target.z = 150; | |
// Simple substitute "OrbitControls" and the control should work as-is. | |
THREE.OrbitControls = function ( object, domElement, localElement ) { | |
this.object = object; | |
this.domElement = ( domElement !== undefined ) ? domElement : document; | |
this.localElement = ( localElement !== undefined ) ? localElement : document; | |
// API | |
// Set to false to disable this control | |
this.enabled = true; | |
// "target" sets the location of focus, where the control orbits around | |
// and where it pans with respect to. | |
this.target = new THREE.Vector3(); | |
// center is old, deprecated; use "target" instead | |
this.center = this.target; | |
// This option actually enables dollying in and out; left as "zoom" for | |
// backwards compatibility | |
this.noZoom = false; | |
this.zoomSpeed = 1.0; | |
// Limits to how far you can dolly in and out | |
this.minDistance = 0; | |
this.maxDistance = Infinity; | |
// Set to true to disable this control | |
this.noRotate = false; | |
this.rotateSpeed = 1.0; | |
// Set to true to disable this control | |
this.noPan = false; | |
this.keyPanSpeed = 7.0; // pixels moved per arrow key push | |
// Set to true to automatically rotate around the target | |
this.autoRotate = false; | |
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 | |
// 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 | |
// Set to true to disable use of the keys | |
this.noKeys = false; | |
// The four arrow keys | |
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; | |
//////////// | |
// internals | |
var scope = this; | |
var EPS = 0.000001; | |
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(); | |
var phiDelta = 0; | |
var thetaDelta = 0; | |
var scale = 1; | |
var pan = new THREE.Vector3(); | |
var lastPosition = new THREE.Vector3(); | |
var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; | |
var state = STATE.NONE; | |
// events | |
var changeEvent = { type: 'change' }; | |
this.rotateLeft = function ( angle ) { | |
if ( angle === undefined ) { | |
angle = getAutoRotationAngle(); | |
} | |
thetaDelta -= angle; | |
}; | |
this.rotateUp = function ( angle ) { | |
if ( angle === undefined ) { | |
angle = getAutoRotationAngle(); | |
} | |
phiDelta -= angle; | |
}; | |
// pass in distance in world space to move left | |
this.panLeft = function ( distance ) { | |
var panOffset = new THREE.Vector3(); | |
var te = this.object.matrix.elements; | |
// get X column of matrix | |
panOffset.set( te[0], te[1], te[2] ); | |
panOffset.multiplyScalar(-distance); | |
pan.add( panOffset ); | |
}; | |
// pass in distance in world space to move up | |
this.panUp = function ( distance ) { | |
var panOffset = new THREE.Vector3(); | |
var te = this.object.matrix.elements; | |
// get Y column of matrix | |
panOffset.set( te[4], te[5], te[6] ); | |
panOffset.multiplyScalar(distance); | |
pan.add( panOffset ); | |
}; | |
// main entry point; pass in Vector2 of change desired in pixel space, | |
// right and down are positive | |
this.pan = function ( delta ) { | |
var element = scope.domElement === document ? scope.domElement.body : scope.domElement; | |
if ( scope.object.fov !== undefined ) { | |
// perspective | |
var position = scope.object.position; | |
var offset = position.clone().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 | |
scope.panLeft( 2 * delta.x * targetDistance / element.clientHeight ); | |
scope.panUp( 2 * delta.y * targetDistance / element.clientHeight ); | |
} else if ( scope.object.top !== undefined ) { | |
// orthographic | |
scope.panLeft( delta.x * (scope.object.right - scope.object.left) / element.clientWidth ); | |
scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / element.clientHeight ); | |
} else { | |
// camera neither orthographic or perspective - warn user | |
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); | |
} | |
}; | |
this.dollyIn = function ( dollyScale ) { | |
if ( dollyScale === undefined ) { | |
dollyScale = getZoomScale(); | |
} | |
scale /= dollyScale; | |
}; | |
this.dollyOut = function ( dollyScale ) { | |
if ( dollyScale === undefined ) { | |
dollyScale = getZoomScale(); | |
} | |
scale *= dollyScale; | |
}; | |
this.update = function () { | |
var position = this.object.position; | |
var offset = position.clone().sub( this.target ); | |
// 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 ) ); | |
// move target to panned location | |
this.target.add( pan ); | |
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.target ).add( offset ); | |
this.object.lookAt( this.target ); | |
thetaDelta = 0; | |
phiDelta = 0; | |
scale = 1; | |
pan.set(0,0,0); | |
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.zoomSpeed ); | |
} | |
function onMouseDown( event ) { | |
if ( scope.enabled === false ) { return; } | |
event.preventDefault(); | |
if ( event.button === 0 ) { | |
if ( scope.noRotate === true ) { return; } | |
state = STATE.ROTATE; | |
rotateStart.set( event.clientX, event.clientY ); | |
} else if ( event.button === 1 ) { | |
if ( scope.noZoom === true ) { return; } | |
state = STATE.DOLLY; | |
dollyStart.set( event.clientX, event.clientY ); | |
} else if ( event.button === 2 ) { | |
if ( scope.noPan === true ) { return; } | |
state = STATE.PAN; | |
panStart.set( event.clientX, event.clientY ); | |
} | |
// Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be | |
scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); | |
scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); | |
} | |
function onMouseMove( event ) { | |
if ( scope.enabled === false ) return; | |
event.preventDefault(); | |
var element = scope.domElement === document ? scope.domElement.body : scope.domElement; | |
if ( state === STATE.ROTATE ) { | |
if ( scope.noRotate === true ) return; | |
rotateEnd.set( event.clientX, event.clientY ); | |
rotateDelta.subVectors( rotateEnd, rotateStart ); | |
// rotating across whole screen goes 360 degrees around | |
scope.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 | |
scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); | |
rotateStart.copy( rotateEnd ); | |
} else if ( state === STATE.DOLLY ) { | |
if ( scope.noZoom === true ) return; | |
dollyEnd.set( event.clientX, event.clientY ); | |
dollyDelta.subVectors( dollyEnd, dollyStart ); | |
if ( dollyDelta.y > 0 ) { | |
scope.dollyIn(); | |
} else { | |
scope.dollyOut(); | |
} | |
dollyStart.copy( dollyEnd ); | |
} else if ( state === STATE.PAN ) { | |
if ( scope.noPan === true ) return; | |
panEnd.set( event.clientX, event.clientY ); | |
panDelta.subVectors( panEnd, panStart ); | |
scope.pan( panDelta ); | |
panStart.copy( panEnd ); | |
} | |
// Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be | |
scope.update(); | |
} | |
function onMouseUp( /* event */ ) { | |
if ( scope.enabled === false ) return; | |
// Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be | |
scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); | |
scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); | |
state = STATE.NONE; | |
} | |
function onMouseWheel( event ) { | |
if ( scope.enabled === false || scope.noZoom === true ) 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.dollyOut(); | |
} else { | |
scope.dollyIn(); | |
} | |
} | |
function onKeyDown( event ) { | |
if ( scope.enabled === false ) { return; } | |
if ( scope.noKeys === true ) { return; } | |
if ( scope.noPan === true ) { return; } | |
// pan a pixel - I guess for precise positioning? | |
// Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be | |
var needUpdate = false; | |
switch ( event.keyCode ) { | |
case scope.keys.UP: | |
scope.pan( new THREE.Vector2( 0, scope.keyPanSpeed ) ); | |
needUpdate = true; | |
break; | |
case scope.keys.BOTTOM: | |
scope.pan( new THREE.Vector2( 0, -scope.keyPanSpeed ) ); | |
needUpdate = true; | |
break; | |
case scope.keys.LEFT: | |
scope.pan( new THREE.Vector2( scope.keyPanSpeed, 0 ) ); | |
needUpdate = true; | |
break; | |
case scope.keys.RIGHT: | |
scope.pan( new THREE.Vector2( -scope.keyPanSpeed, 0 ) ); | |
needUpdate = true; | |
break; | |
} | |
// Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be | |
if ( needUpdate ) { | |
scope.update(); | |
} | |
} | |
function touchstart( event ) { | |
if ( scope.enabled === false ) { return; } | |
switch ( event.touches.length ) { | |
case 1: // one-fingered touch: rotate | |
if ( scope.noRotate === true ) { return; } | |
state = STATE.TOUCH_ROTATE; | |
rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); | |
break; | |
case 2: // two-fingered touch: dolly | |
if ( scope.noZoom === true ) { return; } | |
state = STATE.TOUCH_DOLLY; | |
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 ); | |
break; | |
case 3: // three-fingered touch: pan | |
if ( scope.noPan === true ) { return; } | |
state = STATE.TOUCH_PAN; | |
panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); | |
break; | |
default: | |
state = STATE.NONE; | |
} | |
} | |
function touchmove( event ) { | |
if ( scope.enabled === false ) { return; } | |
event.preventDefault(); | |
event.stopPropagation(); | |
var element = scope.domElement === document ? scope.domElement.body : scope.domElement; | |
switch ( event.touches.length ) { | |
case 1: // one-fingered touch: rotate | |
if ( scope.noRotate === true ) { return; } | |
if ( state !== STATE.TOUCH_ROTATE ) { return; } | |
rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); | |
rotateDelta.subVectors( rotateEnd, rotateStart ); | |
// rotating across whole screen goes 360 degrees around | |
scope.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 | |
scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); | |
rotateStart.copy( rotateEnd ); | |
break; | |
case 2: // two-fingered touch: dolly | |
if ( scope.noZoom === true ) { return; } | |
if ( state !== STATE.TOUCH_DOLLY ) { return; } | |
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 ) { | |
scope.dollyOut(); | |
} else { | |
scope.dollyIn(); | |
} | |
dollyStart.copy( dollyEnd ); | |
break; | |
case 3: // three-fingered touch: pan | |
if ( scope.noPan === true ) { return; } | |
if ( state !== STATE.TOUCH_PAN ) { return; } | |
panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); | |
panDelta.subVectors( panEnd, panStart ); | |
scope.pan( panDelta ); | |
panStart.copy( panEnd ); | |
break; | |
default: | |
state = STATE.NONE; | |
} | |
} | |
function touchend( /* event */ ) { | |
if ( scope.enabled === false ) { return; } | |
state = STATE.NONE; | |
} | |
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); | |
this.localElement.addEventListener( 'mousedown', onMouseDown, false ); | |
this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); | |
this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox | |
this.domElement.addEventListener( 'keydown', onKeyDown, false ); | |
this.localElement.addEventListener( 'touchstart', touchstart, false ); | |
this.domElement.addEventListener( 'touchend', touchend, false ); | |
this.domElement.addEventListener( 'touchmove', touchmove, false ); | |
}; | |
THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); |
Hi Felix. Is this file under an MIT license? If so, would you mind adding that to the heading?
Hello there,
Where is the mistake in my code?
It does not rotates.
Thanks,
<script src='js/three.min.js' </script><script src="http://threejs.org/build/three.min.js"></script>
<script src="js/OrbitControls.js"></script>
<script src="http://threejs.org/examples/js/loaders/STLLoader.js"></script>
<script>
$(document).ready(function(){
var container, camera, scene, renderer;
var controls;
init();
animate();
function init() {
container = document.createElement( 'div' );
container.style.cssText = "position:absolute;z-index:1;top:10px;";
$("#object").append( container );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth/3, window.innerHeight/2 );
container.appendChild( renderer.domElement );
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 1, 15 );
camera.position.set( 3, 0.50, 15 );
scene.add( camera ); // required, because we are adding a light as a child of the camera
// lights
scene.add( new THREE.AmbientLight( 0x777777 ) );
var light = new THREE.PointLight( 0xffffff, 0.8 );
camera.add( light );
// object
var loader = new THREE.STLLoader();
loader.load( 'output.stl', function ( geometry ) {
var material = new THREE.MeshPhongMaterial( { color: 0xff5533 } );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.set( 0, - 0.25, 0.6 );
mesh.rotation.set( 0, - Math.PI / 2, 0 );
mesh.scale.set( 0.05, 0.05, 0.05 );
scene.add( mesh );
} );
window.addEventListener( 'resize', onWindowResize, false );
controls = new THREE.OrbitControls(camera, renderer.domElement);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
controls.update();
}
function render() {
var timer = Date.now() * 0.0005;
camera.position.x = -5;
camera.position.z = 5;
camera.position.y = 5;
camera.lookAt( scene.position );
renderer.render(scene, camera);
}
});
</script>
Any chance we can make this commonJS compatible?
Good evening. I'm trying to make a code with OrbitControls.js, when people browses I want to avoid firing the "touchend" event. I'm doing it like in a desktop, checking "touchmove" after "touchstart" and setting a flag to avoid it, however, it does not work, because on line 503, stopPropagation() is called.
What I want to know is why is it called?
Great code!And hope a new version with the Event Listener of the Gyroscope,then we can use it to develop VR-app.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi,
This is nice.. You should however consider making it a fully fledged GitHub project and not just a Gist (or have you already done that?)