Skip to content

Commit

Permalink
EnvironmentControls: Add support for orthographic camera (#518)
Browse files Browse the repository at this point in the history
* Add support for orthographic controls

* move variables

* Bump controls

* Move options

* updates

* Shift the camera on zoom

* Update

* Add zoom speed

* Update rotation speed
  • Loading branch information
gkjohnson authored Mar 29, 2024
1 parent 8474e89 commit bf6cc4a
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 42 deletions.
49 changes: 42 additions & 7 deletions example/fadingTiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import {
AmbientLight,
WebGLRenderer,
PerspectiveCamera,
OrthographicCamera,
Group,
} from 'three';
import { FadeTilesRenderer, } from './src/FadeTilesRenderer.js';
import { EnvironmentControls } from '../src/index.js';
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';

let camera, controls, scene, renderer;
let camera, ortho, controls, scene, renderer;
let groundTiles, skyTiles, tilesParent;

const params = {
Expand All @@ -22,6 +23,7 @@ const params = {
fadeDuration: 0.5,
renderScale: 1,
fadingGroundTiles: '0 tiles',
camera: 'perspective',

};

Expand All @@ -41,6 +43,8 @@ function init() {
document.body.appendChild( renderer.domElement );
renderer.domElement.tabIndex = 1;

ortho = new OrthographicCamera( - 1, 1, 1, - 1, 0, 4000 );

camera = new PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.25, 4000 );
camera.position.set( 20, 10, 20 );

Expand All @@ -67,6 +71,27 @@ function init() {
window.addEventListener( 'resize', onWindowResize, false );

const gui = new GUI();
gui.add( params, 'camera', [ 'perspective', 'orthographic' ] ).onChange( v => {

if ( v === 'perspective' ) {

camera.position.copy( ortho.position );
camera.rotation.copy( ortho.rotation );
controls.setCamera( camera );
groundTiles.deleteCamera( ortho );
skyTiles.deleteCamera( ortho );

} else {

ortho.position.copy( camera.position );
ortho.rotation.copy( camera.rotation );
controls.setCamera( ortho );
groundTiles.deleteCamera( camera );
skyTiles.deleteCamera( camera );

}

} );
gui.add( params, 'useFade' );
gui.add( params, 'fadeRootTiles' );
gui.add( params, 'errorTarget', 0, 1000 );
Expand Down Expand Up @@ -107,6 +132,14 @@ function reinstantiateTiles() {

function onWindowResize() {

const aspect = window.innerWidth / window.innerHeight;

ortho.bottom = - 40;
ortho.top = 40;
ortho.left = - 40 * aspect;
ortho.right = 40 * aspect;
ortho.updateProjectionMatrix();

camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
Expand All @@ -117,24 +150,26 @@ function render() {

requestAnimationFrame( render );

const activeCamera = params.camera === 'perspective' ? camera : ortho;

controls.update();
camera.updateMatrixWorld();
activeCamera.updateMatrixWorld();

groundTiles.errorTarget = params.errorTarget;
groundTiles.fadeRootTiles = params.fadeRootTiles;
groundTiles.setCamera( camera );
groundTiles.setResolutionFromRenderer( camera, renderer );
groundTiles.setCamera( activeCamera );
groundTiles.setResolutionFromRenderer( activeCamera, renderer );
groundTiles.update();

skyTiles.fadeRootTiles = params.fadeRootTiles;
skyTiles.setCamera( camera );
skyTiles.setResolutionFromRenderer( camera, renderer );
skyTiles.setCamera( activeCamera );
skyTiles.setResolutionFromRenderer( activeCamera, renderer );
skyTiles.update();

groundTiles.fadeDuration = params.useFade ? params.fadeDuration * 1000 : 0;
skyTiles.fadeDuration = params.useFade ? params.fadeDuration * 1000 : 0;

renderer.render( scene, camera );
renderer.render( scene, activeCamera );

params.fadingGroundTiles = groundTiles.fadingTiles + ' tiles';

Expand Down
104 changes: 69 additions & 35 deletions src/three/controls/EnvironmentControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const _rotationAxis = new Vector3();
const _quaternion = new Quaternion();
const _plane = new Plane();
const _localUp = new Vector3();
const _mouseBefore = new Vector3();
const _mouseAfter = new Vector3();

const _pointer = new Vector2();
const _prevPointer = new Vector2();
Expand Down Expand Up @@ -73,11 +75,15 @@ export class EnvironmentControls extends EventDispatcher {
this.state = NONE;
this.pinchState = NONE;
this.cameraRadius = 5;
this.rotationSpeed = 5;
this.rotationSpeed = 1;
this.minAltitude = 0;
this.maxAltitude = 0.45 * Math.PI;
this.minZoomDistance = 10;
this.maxZoomDistance = Infinity;
this.minDistance = 10;
this.maxDistance = Infinity;
this.minZoom = 0;
this.maxZoom = Infinity;
this.zoomSpeed = 1;

this.reorientOnDrag = true;
this.reorientOnZoom = false;
this.adjustHeight = true;
Expand Down Expand Up @@ -611,11 +617,14 @@ export class EnvironmentControls extends EventDispatcher {
zoomPoint,
zoomDirection,
camera,
minZoomDistance,
maxZoomDistance,
minDistance,
maxDistance,
raycaster,
pointerTracker,
domElement,
minZoom,
maxZoom,
zoomSpeed,
} = this;

let scale = this.zoomDelta;
Expand All @@ -628,49 +637,74 @@ export class EnvironmentControls extends EventDispatcher {

}

// initialize the zoom direction
mouseToCoords( _pointer.x, _pointer.y, domElement, _pointer );
raycaster.setFromCamera( _pointer, camera );
zoomDirection.copy( raycaster.ray.direction ).normalize();
this.zoomDirectionSet = true;
if ( camera.isOrthographicCamera ) {

// track the zoom direction we're going to use
const finalZoomDirection = _vec.copy( zoomDirection );
// get the mouse position before zoom
mouseToCoords( _pointer.x, _pointer.y, domElement, _mouseBefore );
_mouseBefore.unproject( camera );

// always update the zoom target point in case the tiles are changing
if ( this._updateZoomPoint() ) {
// zoom the camera
const normalizedDelta = Math.pow( 0.95, Math.abs( scale * 0.05 ) );
const scaleFactor = scale > 0 ? 1 / Math.abs( normalizedDelta ) : normalizedDelta;

const dist = zoomPoint.distanceTo( camera.position );
camera.zoom = Math.max( minZoom, Math.min( maxZoom, camera.zoom * scaleFactor * zoomSpeed ) );
camera.updateProjectionMatrix();

// scale the distance based on how far there is to move
if ( scale < 0 ) {
// get the mouse position after zoom
mouseToCoords( _pointer.x, _pointer.y, domElement, _mouseAfter );
_mouseAfter.unproject( camera );

const remainingDistance = Math.min( 0, dist - maxZoomDistance );
scale = scale * dist * 0.01;
scale = Math.max( scale, remainingDistance );
// shift the camera on the near plane so the mouse is in the same spot
camera.position.sub( _mouseAfter ).add( _mouseBefore );
camera.updateMatrixWorld();

} else {
} else {

const remainingDistance = Math.max( 0, dist - minZoomDistance );
scale = scale * ( dist - minZoomDistance ) * 0.01;
scale = Math.min( scale, remainingDistance );
// initialize the zoom direction
mouseToCoords( _pointer.x, _pointer.y, domElement, _pointer );
raycaster.setFromCamera( _pointer, camera );
zoomDirection.copy( raycaster.ray.direction ).normalize();
this.zoomDirectionSet = true;

}
// track the zoom direction we're going to use
const finalZoomDirection = _vec.copy( zoomDirection );

camera.position.addScaledVector( zoomDirection, scale );
camera.updateMatrixWorld();
// always update the zoom target point in case the tiles are changing
if ( this._updateZoomPoint() ) {

} else {
const dist = zoomPoint.distanceTo( camera.position );

// if we're zooming into nothing then use the distance from the ground to scale movement
const hit = this._getPointBelowCamera();
if ( hit ) {
// scale the distance based on how far there is to move
if ( scale < 0 ) {

const dist = hit.distance;
finalZoomDirection.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );
camera.position.addScaledVector( finalZoomDirection, scale * dist * 0.01 );
const remainingDistance = Math.min( 0, dist - maxDistance );
scale = scale * dist * zoomSpeed * 0.0025;
scale = Math.max( scale, remainingDistance );

} else {

const remainingDistance = Math.max( 0, dist - minDistance );
scale = scale * ( dist - minDistance ) * zoomSpeed * 0.0025;
scale = Math.min( scale, remainingDistance );

}

camera.position.addScaledVector( zoomDirection, scale );
camera.updateMatrixWorld();

} else {

// if we're zooming into nothing then use the distance from the ground to scale movement
const hit = this._getPointBelowCamera();
if ( hit ) {

const dist = hit.distance;
finalZoomDirection.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );
camera.position.addScaledVector( finalZoomDirection, scale * dist * 0.01 );
camera.updateMatrixWorld();

}

}

}
Expand Down Expand Up @@ -813,7 +847,7 @@ export class EnvironmentControls extends EventDispatcher {
// get the rotation motion and scale the rotation based on pixel ratio for consistency
pointerTracker.getCenterPoint( _pointer );
pointerTracker.getPreviousCenterPoint( _prevPointer );
_deltaPointer.subVectors( _pointer, _prevPointer ).multiplyScalar( 0.01 / devicePixelRatio );
_deltaPointer.subVectors( _pointer, _prevPointer ).multiplyScalar( 0.02 / devicePixelRatio );

const azimuth = - _deltaPointer.x * rotationSpeed;
let altitude = _deltaPointer.y * rotationSpeed;
Expand Down
6 changes: 6 additions & 0 deletions src/three/controls/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,10 @@ export function mouseToCoords( clientX, clientY, element, target ) {
target.x = ( ( clientX - element.offsetLeft ) / element.clientWidth ) * 2 - 1;
target.y = - ( ( clientY - element.offsetTop ) / element.clientHeight ) * 2 + 1;

if ( target.isVector3 ) {

target.z = 0;

}

}

0 comments on commit bf6cc4a

Please sign in to comment.