Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: OrbitControls: added support for zoom to cursor #17145

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 88 additions & 4 deletions examples/jsm/controls/OrbitControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ var OrbitControls = function ( object, domElement ) {
this.enableZoom = true;
this.zoomSpeed = 1.0;

// Set to true to zoom to cursor // panning may have to be enabled... // does it make sense for orbit controls?
this.zoomToCursor = false;

// Set to false to disable rotating
this.enableRotate = true;
this.rotateSpeed = 1.0;
Expand Down Expand Up @@ -185,7 +188,7 @@ var OrbitControls = function ( object, domElement ) {

spherical.makeSafe();


var prevRadius = spherical.radius;
spherical.radius *= scale;

// restrict radius to be between desired limits
Expand All @@ -203,6 +206,22 @@ var OrbitControls = function ( object, domElement ) {

}

// suport zoomToCursor (mouse only)

if ( scope.zoomToCursor ) {

if ( scope.object.isPerspectiveCamera ) {

scope.target.lerp( mouse3D, 1 - spherical.radius / prevRadius );
WestLangley marked this conversation as resolved.
Show resolved Hide resolved

} else if ( scope.object.isOrthographicCamera ) {

scope.target.lerp( mouse3D, 1 - zoomFactor );

}

}

offset.setFromSpherical( spherical );

// rotate offset back to "camera-up-vector-is-up" space
Expand Down Expand Up @@ -242,6 +261,7 @@ var OrbitControls = function ( object, domElement ) {
lastPosition.copy( scope.object.position );
lastQuaternion.copy( scope.object.quaternion );
zoomChanged = false;
zoomFactor = 1;

return true;

Expand Down Expand Up @@ -304,6 +324,7 @@ var OrbitControls = function ( object, domElement ) {
var scale = 1;
var panOffset = new Vector3();
var zoomChanged = false;
var zoomFactor = 1;

var rotateStart = new Vector2();
var rotateEnd = new Vector2();
Expand All @@ -317,6 +338,8 @@ var OrbitControls = function ( object, domElement ) {
var dollyEnd = new Vector2();
var dollyDelta = new Vector2();

var mouse3D = new Vector3();

function getAutoRotationAngle() {

return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
Expand Down Expand Up @@ -430,7 +453,9 @@ var OrbitControls = function ( object, domElement ) {

} else if ( scope.object.isOrthographicCamera ) {

zoomFactor = scope.object.zoom;
scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
zoomFactor /= scope.object.zoom
WestLangley marked this conversation as resolved.
Show resolved Hide resolved
scope.object.updateProjectionMatrix();
zoomChanged = true;

Expand All @@ -451,7 +476,9 @@ var OrbitControls = function ( object, domElement ) {

} else if ( scope.object.isOrthographicCamera ) {

zoomFactor = scope.object.zoom;
scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
zoomFactor /= scope.object.zoom
WestLangley marked this conversation as resolved.
Show resolved Hide resolved
scope.object.updateProjectionMatrix();
zoomChanged = true;

Expand Down Expand Up @@ -522,7 +549,7 @@ var OrbitControls = function ( object, domElement ) {

if ( dollyDelta.y > 0 ) {

dollyIn( getZoomScale() );
dollyIn( getZoomScale() ); // fix - set mouse3D - dragging with middle mouse button

} else if ( dollyDelta.y < 0 ) {

Expand Down Expand Up @@ -558,10 +585,65 @@ var OrbitControls = function ( object, domElement ) {

}

var updateMouse3D = function () {

var v = new Vector3();
var dir = new Vector3();

return function updateMouse3D( event ) {

var element = scope.domElement === document ? scope.domElement.body : scope.domElement;

if ( scope.object.isPerspectiveCamera ) {

v.set(
( event.clientX / element.clientWidth ) * 2 - 1,
- ( event.clientY / element.clientHeight ) * 2 + 1,
0.5 );
WestLangley marked this conversation as resolved.
Show resolved Hide resolved

v.unproject( scope.object );

v.sub( scope.object.position ).normalize();

var distance = ( scope.target.y - scope.object.position.y ) / v.y; // y-up case only - fix; .y can be zero

mouse3D.copy( scope.object.position ).add( v.multiplyScalar( distance ) );

} else if ( scope.object.isOrthographicCamera ) {

// ref: https://stackoverflow.com/questions/38902223/three-js-orthographic-camera-find-x-and-z-where-y-0/38904730#38904730
v.set(
( event.clientX / element.clientWidth ) * 2 - 1,
- ( event.clientY / element.clientHeight ) * 2 + 1,
( scope.object.near + scope.object.far ) / ( scope.object.near - scope.object.far ) );

v.unproject( scope.object ); // set v in plane of camera

dir.set( 0, 0, - 1 ).transformDirection( scope.object.matrixWorld ); // could use quaternion...

var distance = - v.y / dir.y; // y-up case only - fix; .y can be zero

mouse3D.copy( v ).add( dir.multiplyScalar( distance ) );

} else {

// camera neither orthographic nor perspective
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type.' );

}

//console.log( mouse3D );

};

}();

function handleMouseWheel( event ) {

// console.log( 'handleMouseWheel' );

updateMouse3D( event );

if ( event.deltaY < 0 ) {

dollyOut( getZoomScale() );
Expand Down Expand Up @@ -756,7 +838,7 @@ var OrbitControls = function ( object, domElement ) {

dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );

dollyIn( dollyDelta.y );
dollyIn( dollyDelta.y ); // fix - set mouse3D for zoom to cursor

dollyStart.copy( dollyEnd );

Expand Down Expand Up @@ -952,7 +1034,7 @@ var OrbitControls = function ( object, domElement ) {

if ( scope.enableZoom === false ) return;

handleMouseMoveDolly( event );
handleMouseMoveDolly( event ); // fix zoom to cursor - dragging with middle mouse button - could be from alt key???

break;

Expand Down Expand Up @@ -1334,6 +1416,8 @@ var MapControls = function ( object, domElement ) {
this.touches.ONE = TOUCH.PAN;
this.touches.TWO = TOUCH.DOLLY_ROTATE;

this.zoomToCursor = true;

};

MapControls.prototype = Object.create( EventDispatcher.prototype );
Expand Down
86 changes: 47 additions & 39 deletions examples/misc_controls_map.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@

import * as THREE from '../build/three.module.js';

import { GUI } from './jsm/libs/dat.gui.module.js';

import { MapControls } from './jsm/controls/OrbitControls.js';

var camera, controls, scene, renderer;
Expand All @@ -40,14 +38,19 @@

scene = new THREE.Scene();
scene.background = new THREE.Color( 0xcccccc );
scene.fog = new THREE.FogExp2( 0xcccccc, 0.002 );
scene.fog = new THREE.FogExp2( 0xcccccc, 0.0012 );

renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
renderer.gammaOutput = true;

//var h = 100, aspect = window.innerWidth / window.innerHeight;
//camera = new THREE.OrthographicCamera( - h * aspect, h * aspect, h, - h, 10, 10000 );

camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 10, 10000 );
camera.position.set( 400, 200, 0 );

// controls
Expand All @@ -59,55 +62,34 @@
controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
controls.dampingFactor = 0.05;

controls.screenSpacePanning = false;

controls.minDistance = 100;
controls.maxDistance = 500;

controls.maxPolarAngle = Math.PI / 2;
controls.maxDistance = 700;

// world
controls.maxPolarAngle = Math.PI / 3;

var geometry = new THREE.BoxBufferGeometry( 1, 1, 1 );
geometry.translate( 0, 0.5, 0 );
var material = new THREE.MeshPhongMaterial( { color: 0xffffff, flatShading: true } );
//scene.add( new THREE.AxesHelper( 500 ) );

for ( var i = 0; i < 500; i ++ ) {
new THREE.TextureLoader().load( 'textures/terrain/agrarian_economy.jpg', function ( texture ) {

var mesh = new THREE.Mesh( geometry, material );
mesh.position.x = Math.random() * 1600 - 800;
mesh.position.y = 0;
mesh.position.z = Math.random() * 1600 - 800;
mesh.scale.x = 20;
mesh.scale.y = Math.random() * 80 + 10;
mesh.scale.z = 20;
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
scene.add( mesh );
texture.encoding = THREE.sRGBEncoding;

}
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 6, 6 );

// lights
var geometry = new THREE.PlaneBufferGeometry( 6000, 6000 );
geometry.rotateX( - Math.PI / 2 );

var light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
var material = new THREE.MeshBasicMaterial( { map: texture } );

var light = new THREE.DirectionalLight( 0x002288 );
light.position.set( - 1, - 1, - 1 );
scene.add( light );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh )

var light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
//render(); // if there is no animation loop

//
} );

window.addEventListener( 'resize', onWindowResize, false );


var gui = new GUI();
gui.add( controls, 'screenSpacePanning' );

}

function onWindowResize() {
Expand All @@ -125,6 +107,32 @@

controls.update(); // only required if controls.enableDamping = true, or if controls.autoRotate = true

// poor man's infinity map

if ( camera.position.x > 1000 ) {

camera.position.x -= 1000;
controls.target.x -= 1000;

} else if ( camera.position.x < - 1000 ) {

camera.position.x += 1000;
controls.target.x += 1000;

}

if ( camera.position.z > 1000 ) {

camera.position.z -= 1000;
controls.target.z -= 1000;

} else if ( camera.position.z < - 1000 ) {

camera.position.z += 1000;
controls.target.z += 1000;

}

render();

}
Expand Down
Binary file added examples/textures/terrain/agrarian_economy.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.