I am using this demo:
https://threejs.org/examples/?q=video#webgl_video_panorama_equirectangular
with the following code:
let camera, scene, renderer;
let isUserInteracting = false,
lon = 0, lat = 0,
phi = 0, theta = 0,
onPointerDownPointerX = 0,
onPointerDownPointerY = 0,
onPointerDownLon = 0,
onPointerDownLat = 0;
const distance = 50;
init();
animate();
function init() {
const container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1100 );
scene = new THREE.Scene();
const geometry = new THREE.SphereGeometry( 500, 60, 40 );
// invert the geometry on the x-axis so that all of the faces point inward
geometry.scale( - 1, 1, 1 );
const video = document.getElementById( 'video' );
video.play();
const texture = new THREE.VideoTexture( video );
const material = new THREE.MeshBasicMaterial( { map: texture } );
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'pointerdown', onPointerDown );
document.addEventListener( 'pointermove', onPointerMove );
document.addEventListener( 'pointerup', onPointerUp );
//
window.addEventListener( 'resize', onWindowResize );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onPointerDown( event ) {
isUserInteracting = true;
onPointerDownPointerX = event.clientX;
onPointerDownPointerY = event.clientY;
onPointerDownLon = lon;
onPointerDownLat = lat;
}
function onPointerMove( event ) {
if ( isUserInteracting === true ) {
lon = ( onPointerDownPointerX - event.clientX ) * 0.1 + onPointerDownLon;
lat = ( onPointerDownPointerY - event.clientY ) * 0.1 + onPointerDownLat;
}
}
function onPointerUp() {
isUserInteracting = false;
}
function animate() {
requestAnimationFrame( animate );
update();
}
function update() {
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.MathUtils.degToRad( 90 - lat );
theta = THREE.MathUtils.degToRad( lon );
camera.position.x = distance * Math.sin( phi ) * Math.cos( theta );
camera.position.y = distance * Math.cos( phi );
camera.position.z = distance * Math.sin( phi ) * Math.sin( theta );
camera.lookAt( 0, 0, 0 );
renderer.render( scene, camera );
}
Is there an api for the THREE.PerspectiveCamera so you can manually call zoom in / out, move left / right inside the scene ..etc?
Is this possible?
There is indeed an API for the Three.PerspectiveCamera, including a method to call to zoom in/out the camera.
https://threejs.org/docs/#api/en/cameras/PerspectiveCamera.zoom
If you wish to move the camera around the scene, you can update the position of the camera through updating its position attribute. This can be done by either updating each of its three coordinates individually, or using the 'set' method to pass in a Vector3 for its position. Below is the documentation to Object3D, which camera inherits from.
https://threejs.org/docs/#api/en/core/Object3D.position
Be wary though that script is already updating its position during its 'update' function, so you will need to possibly change those lines directly, depending on what you want to do.
Hopefully this helps!
Related
So I've been trying to make a spherical 360 panorama using three.js which implements clickable objects, which at the moment I would like to make hyperlinks.I've read many of the previous examples of raycasting and such, but have had no luck in getting the object to actually redirect me to the site. If someone could tell me where I'm going wrong in the code I'd greatly appreciate it.
I have a feeling the orbiting/panning function under "onDocumentMouseDown" is interfering with the raycasting? I'm still new to this and figuring it out.
<div id="container"></div>
<script src="three.min.js"></script>
<script>
var container, stats;
var camera, controls, scene, renderer;
var objects = [], plane;
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2(),
offset = new THREE.Vector3();
var fov = 70,
texture_placeholder,
isUserInteracting = false,
onMouseDownMouseX = 0, onMouseDownMouseY = 0,
lon = 0, onMouseDownLon = 0,
lat = 0, onMouseDownLat = 0,
phi = 0, theta = 0;
init();
animate();
function init() {
var container, mesh1, sphere1, cube1;
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 1, 1100 );
camera.target = new THREE.Vector3( 0, 0, 0 );
scene = new THREE.Scene();
mesh1 = new THREE.Mesh( new THREE.SphereGeometry( 500, 60, 40 ), new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'spherical_map_small.jpg' ), transparent: true} ) );
mesh1.scale.x = -1;
scene.add( mesh1 );
meshMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff});
var sphere1 = new THREE.Mesh( new THREE.SphereGeometry( 2.5,20,20 ), meshMaterial );
sphere1.position.set( 50, 10, 0 );
scene.add( sphere1 );
sphere1.userData = { URL:"http://www.google.com"};
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener( 'mouseup', onDocumentMouseUp, false );
document.addEventListener( 'mousewheel', onDocumentMouseWheel, false );
document.addEventListener( 'DOMMouseScroll', onDocumentMouseWheel, false);
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseDown( event ) {
event.preventDefault();
isUserInteracting = true;
onPointerDownPointerX = event.clientX;
onPointerDownPointerY = event.clientY;
onPointerDownLon = lon;
onPointerDownLat = lat;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( sphere1 );
if ( intersects.length > 0 ) {
controls.enabled = true;
SELECTED = intersects[ 0 ].sphere1;
var intersects = raycaster.intersectObject( sphere1 );
if ( intersects.length > 0 ) {
window.open(intersects[0].object.userData.URL);
}
}
}
function onDocumentMouseMove( event ) {
if ( isUserInteracting ) {
lon = ( onPointerDownPointerX - event.clientX ) * 0.1 + onPointerDownLon;
lat = ( event.clientY - onPointerDownPointerY ) * 0.1 + onPointerDownLat;
}
}
function onDocumentMouseUp( event ) {
isUserInteracting = false;
}
function onDocumentMouseWheel( event ) {
isUserInteracting = false;
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.Math.degToRad( 90 - lat );
theta = THREE.Math.degToRad( lon );
camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );
camera.target.y = 500 * Math.cos( phi );
camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta );
camera.lookAt( camera.target );
renderer.render( scene, camera );
}
</script>
Looking at your code I notice that you create var mouse = new THREE.Vector2(), then you don't set its values at any point in the supplied code. Then in onDocumentMouseDown() you cast a ray into the scene with undefined mouse coordinates raycaster.setFromCamera( mouse, camera ); As var mouse has not been set it is very likely that that is the reason you are not getting the navigation to start.
What you need to do is get the normalised screen coordiantes of the mouse and pass that into the raycaster.setFromCamera I can not quite remenber if the screen is one or two units but something like
mouse.x = (event.clientX / window.innerWidth); // normalise the mouse screen pos
mouse.y = (event.clientY / window.innerHeight); // same
mouse.x *= 2; // 0 is the center. -1 is left and 1 is right
mouse.y -= 1; // center
mouse.y *= -2; // Think this is upside down If it does not work try positive 2
mouse.y += 1; // center
if it does not work try switching mouse.y the otherway around;
mouse.y *= 2; // remove the -2 and put in 2
mouse.y -= 1; // remove the += and put in -=
What I find very handy when messing about in 3D is to have a spare debug object in the scene. Something simple like a box or sphere. Use it to show a point on the raycaster's ray.
Something like
// creat a box
var box... blah blah box creation code/
boxP = new vector3(); // position of debug object
// position it halfway in the raycasters range
boxP.x = camera.x + rayCaster.ray.x* ((rayCaster.near+rayCaster.far)/2);
boxP.y = camera.y + rayCaster.ray.y* ((rayCaster.near+rayCaster.far)/2);
boxP.z = camera.z + rayCaster.ray.z* ((rayCaster.near+rayCaster.far)/2);
box.position.set(boxP.x,boxP.y,boxP.z);
Now with luck you should see where you clicks are going.
Also, I am not sure but if you are looking at the sphere from the inside you may have to set the material to doubleSided (I can't see it in your code) as the raycaster ignores faces with normals pointing away. Or try reversing the direction of each polygon.
That's about all I can suggest at the moment. Hope you find the problem.
I'm currently using PointCloud to generate a particle system but within that I would like one single sprite that floats in the position of my indication. When I tried using this three.js example: http://threejs.org/examples/#webgl_sprites I found that the Orthographic Camera limited my ability to still zoom about.
var container, stats;
var camera, scene, renderer, particles, geometry, materials =[], i, h, color, sprite, size;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 2, 2000 );
camera.position.z = 1000;
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2( 0x000000, 0.001 );
geometry = new THREE.Geometry();
sprite = THREE.ImageUtils.loadTexture( "disc.png" );
for ( i = 0; i < 5000; i ++ ) {
var vertex = new THREE.Vector3();
vertex.x = 2000 * Math.random() - 1000;
vertex.y = 2000 * Math.random() - 1000;
vertex.z = 2000 * Math.random() - 1000;
geometry.vertices.push( vertex );
}
// size = Math.random() * 10;
material = new THREE.PointCloudMaterial( { size: 5, sizeAttenuation: false, map: sprite, alphaTest: 0.5, transparent: true } );
particles = new THREE.PointCloud( geometry, material );
scene.add( particles );
var map2 = THREE.ImageUtils.loadTexture( "astronaut.png" );
var material2 = new THREE.SpriteMaterial( { map: map2, color: 0xffffff, fog: true } );
var sprite2 = new THREE.Sprite( material2 );
sprite2.position.x = 0;
sprite2.position.y = 0;
sprite2.position.z = 498;
scene.add( sprite2 );
//
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
//
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener( 'touchstart', onDocumentTouchStart, false );
document.addEventListener( 'touchmove', onDocumentTouchMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
mouseX = event.clientX - windowHalfX;
mouseY = event.clientY - windowHalfY;
}
function onDocumentTouchStart( event ) {
if ( event.touches.length == 1 ) {
event.preventDefault();
mouseX = event.touches[ 0 ].pageX - windowHalfX;
mouseY = event.touches[ 0 ].pageY - windowHalfY;
}
}
function onDocumentTouchMove( event ) {
if ( event.touches.length == 1 ) {
event.preventDefault();
mouseX = event.touches[ 0 ].pageX - windowHalfX;
mouseY = event.touches[ 0 ].pageY - windowHalfY;
}
}
//
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
var time = Date.now() * 0.00005;
camera.position.x += ( mouseX - camera.position.x ) * 0.05;
camera.position.y += ( - mouseY - camera.position.y ) * 0.05;
camera.lookAt( scene.position );
h = ( 360 * ( 1.0 + time ) % 360 ) / 360;
renderer.render( scene, camera );
}
My attempt at solving it was:
var map2 = THREE.ImageUtils.loadTexture( "astronaut.png" );
var material2 = new THREE.SpriteMaterial( { map: map2, color: 0xffffff, fog: true } );
var sprite2 = new THREE.Sprite( material2 );
sprite2.position.x = 0;
sprite2.position.y = 0;
sprite2.position.z = 498;
scene.add( sprite2 );
Right now the sprite is in the center of the screen when I first load but instantly disappears when I begin to move the camera. Ideally, I would like the astronaut.png sprite to move with the other particles but if this is difficult, having him always fixed to the center of the screen would work fine as well.
Resolved this on my own. I created a second THREE.Geometry and THREE.Vector3 an positioned it with vertices.
geometry2 = new THREE.Geometry();
var vertex2 = new THREE.Vector3(0, 0, -50);
geometry2.vertices.push( vertex2 );
var material2 = new THREE.PointCloudMaterial( { size: 100, sizeAttenuation: false, map: map2, alphaTest: 0.5, transparent: true } );
particles2 = new THREE.PointCloud( geometry2, material2 );
scene.add( particles2 );
It seems to me that your values for mouse position would be way too high for camera positions. OpenGL works on the (-1,1) (1,1) (1,-1) (-1,-1) bounding rectangle as a unit. Pixels for your cursor position are in screen pixels like 350,720 etc.
When you increment by the half distance, your numbers are still too large. So here you have to divide by your width/height:
camera.position.x += ( mouseX / window.innerWidth- camera.position.x ) * 0.05;
camera.position.y += ( - mouseY /window.innerHeight- camera.position.y ) * 0.05;
assuming your GL portal is the same size as the window.
I'm starting learning three.js. I studied the Getting started section on the website and had a look to some examples, but I can't figure out many things.
What I'd like to do is to create a cube (a box) and put the camera in the middle of it, so that I would see the inner side of each face. Is this actually possible?
Looking at the documentation, various examples and on stackoverflow, right now I'm at this point:
var camera, scene, renderer;
var isUserInteracting = false,
onMouseDownMouseX = 0, onMouseDownMouseY = 0,
lon = 0, onMouseDownLon = 0,
lat = 0, onMouseDownLat = 0,
phi = 0, theta = 0;
init();
animate();
function init() {
var container, mesh;
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1100 );
camera.target = new THREE.Vector3(1, 1, 1);
scene = new THREE.Scene();
var geometry = new THREE.BoxGeometry(7, 7, 7);
geometry.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
var material = [
new THREE.MeshBasicMaterial( { color: 0x8bf2a7 } ),
new THREE.MeshBasicMaterial( { color: 0x1ad9a5 } ),
new THREE.MeshBasicMaterial( { color: 0xdadcad } ),
new THREE.MeshBasicMaterial( { color: 0xb1b1b1 } ),
new THREE.MeshBasicMaterial( { color: 0x3a3a3a } ), // front
new THREE.MeshBasicMaterial( { color: 0xf5f5f5 } )
];
mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial(material) );
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener( 'mouseup', onDocumentMouseUp, false );
document.addEventListener( 'dragover', function ( event ) {
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
}, false );
document.addEventListener( 'dragenter', function ( event ) {
document.body.style.opacity = 0.5;
}, false );
document.addEventListener( 'dragleave', function ( event ) {
document.body.style.opacity = 1;
}, false );
document.addEventListener( 'drop', function ( event ) {
event.preventDefault();
var reader = new FileReader();
reader.addEventListener( 'load', function ( event ) {
material.map.image.src = event.target.result;
material.map.needsUpdate = true;
}, false );
reader.readAsDataURL( event.dataTransfer.files[ 0 ] );
document.body.style.opacity = 1;
}, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseDown( event ) {
event.preventDefault();
isUserInteracting = true;
onPointerDownPointerX = event.clientX;
onPointerDownPointerY = event.clientY;
onPointerDownLon = lon;
onPointerDownLat = lat;
}
function onDocumentMouseMove( event ) {
if ( isUserInteracting === true ) {
lon = ( onPointerDownPointerX - event.clientX ) * 0.1 + onPointerDownLon;
lat = ( event.clientY - onPointerDownPointerY ) * 0.1 + onPointerDownLat;
}
}
function onDocumentMouseUp( event ) {
isUserInteracting = false;
}
function onDocumentMouseWheel( event ) {
// WebKit
if ( event.wheelDeltaY ) {
camera.fov -= event.wheelDeltaY * 0.05;
// Opera / Explorer 9
} else if ( event.wheelDelta ) {
camera.fov -= event.wheelDelta * 0.05;
// Firefox
} else if ( event.detail ) {
camera.fov += event.detail * 1.0;
}
camera.updateProjectionMatrix();
}
function animate() {
requestAnimationFrame( animate );
update();
}
function update() {
if ( isUserInteracting === false ) {
// lon += 0.1;
}
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.Math.degToRad( 90 - lat );
theta = THREE.Math.degToRad( lon );
camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );
camera.target.y = 500 * Math.cos( phi );
camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta );
camera.lookAt( camera.target );
camera.position.z = 3;
/*
// distortion
camera.position.copy( camera.target ).negate();
*/
renderer.render( scene, camera );
}
I can actually see the cube from the inside, but it's not exactly what I want. I'm trying to put the camera in the exact center of the room and I would it to rotate on itself, but I don't exactly how to precisely move it.
If you look at this fiddle the only difference is the material.
Comment and uncomment the following line to see the effect.
THREE.ImageUtils.crossOrigin = '';
Also you can read more at this issue
This may be a really stupid question, but I am new to three.js and while I've gotten the obj file to load on the web and be controllable via mouse, I'm not quite sure how to handle the next step.
What I'd really like to do is overlay a clickable .jpg or .png file over a section of the existing mesh linking out to some web pages I already have completed. How would I go about doing this? I'd appreciate it if someone could point me to an example or let me know if it's not doable so I can look for possible alternatives.
The code I currently have is below -- it's mostly cobbled together from online example so forgive me if it seems redundant or inelegant.
var container, stats;
var camera, scene, renderer;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 10;
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( render );
// scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0xFFFFFF );
scene.add( ambient );
/*var directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.set( 1, 1, 0 ).normalize();
scene.add( directionalLight );*/
var hemisphereLight = new THREE.HemisphereLight( 0xffffff, 0xffffff, .70);
scene.add( hemisphereLight );
// model
var onProgress = function ( xhr ) {
if ( xhr.lengthComputable ) {
var percentComplete = xhr.loaded / xhr.total * 100;
console.log( Math.round(percentComplete, 2) + '% downloaded' );
}
};
var onError = function ( xhr ) {
};
THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
var loader = new THREE.OBJMTLLoader();
loader.load( 'obj/test/test_model.obj', 'obj/test/test_model.mtl', function ( object ) {
object.scale = new THREE.Vector3( 25, 25, 25 );
//object.position.y = - 80;
scene.add( object );
}, onProgress, onError );
//
renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
controls.handleResize();
}
function onDocumentMouseMove( event ) {
// mouseX = ( event.clientX - windowHalfX ) / 2;
// mouseY = ( event.clientY - windowHalfY ) / 2;
}
//
function animate() {
requestAnimationFrame( animate );
controls.update();
render();
}
function render() {
//camera.position.x += ( mouseX - camera.position.x ) * .05;
//camera.position.y += ( - mouseY - camera.position.y ) * .05;
//camera.lookAt( scene.position );
renderer.render( scene, camera );
}
In your onDocumentMouseMove - or in a click event - you must compute a ray-picking intersection with http://threejs.org/docs/#Reference/Core/Raycaster and handle the reaction from there.
There are multiples examples of this, in the Three.js examples (like http://threejs.org/examples/#webgl_geometry_terrain_raycast) and on StackOverflow.
A very naive solution could be:
// note that you may have to use other properties if the renderer is not fullscreen"
// like here http://stackoverflow.com/questions/13542175/three-js-ray-intersect-fails-by-adding-div
var mouseX = ( event.clientX / window.innerWidth ) * 2 - 1;
var mouseY = -( event.clientY / window.innerHeight ) * 2 + 1;
var vector = new THREE.Vector3( mouseX, mouseY, camera.near );
// Convert the [-1, 1] screen coordinate into a world coordinate on the near plane
vector.unproject( camera );
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
// See if the ray from the camera into the world hits one of our meshes
var intersects = raycaster.intersectObjects( scene, true ); // true for recursive
// Toggle rotation bool for meshes that we clicked
if ( intersects.length > 0 ) {
var clickedObject = intersects[ 0 ].object;
// here, handle the clickedObject properties to react accordingly
// show an overlay, jump to a page, etc..
}
The code I'm using to control my camera works by moving a vector(camera.target) and then setting the camera to look at that vector. In this case camera.lookAt( camera.target );. The onDocumentMouseMove function calculates a longitude and latitude and this helps to set the camera.target x, y, and z values. Below is an example of how things work. Pressing any key will cause the camera to rotate to an object but if you do that the next time you go to move the camera by dragging the mouse the camera will automatically jump to the last position before the keystroke. This is because in order for the mouse to move the camera it has to keep track of the lat and lon. So what I'm having issues with is calculating the new lat lon position after the keystroke. I was thinking if the target x,y,z values can be calculated from the lon and lat than it would stand to reason that the lat and lon could be reverse calculated from the x,y,z, values. Unfortunately the math is beyond my means. I have added a bounty to this issue. Any help would be very much appreciated. http://codepen.io/anon/pen/GgRXJz
var spriteImg, material, geometry;
var camera, scene, renderer;
var keyboard = new THREEx.KeyboardState();
var fov = 70,
texture_placeholder,
isUserInteracting = false,
onMouseDownMouseX = 0, onMouseDownMouseY = 0,
lon = 0, onMouseDownLon = 0,
lat = 0, onMouseDownLat = 0,
phi = 0, theta = 0;
init();
animate();
function init() {
var container;
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 1, 1100 );
camera.target = new THREE.Vector3( 0, 0, 0 );
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xffffff, 1);
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener( 'mouseup', onDocumentMouseUp, false );
window.addEventListener( 'resize', onWindowResize, false );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '0px';
stats.domElement.style.left = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
material = new THREE.MeshBasicMaterial( { color: 0x0125fd} );
geometry = new THREE.PlaneGeometry(50, 50, 3, 3);
blue1 = new THREE.Mesh(geometry, material);
blue1.position.set(200,100,200);
scene.add(blue1);
blue1.lookAt( camera.position );
blue2 = new THREE.Mesh(geometry, material);
blue2.position.set(-200,-100,-200);
blue2.lookAt( camera.position );
scene.add(blue2);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseDown( event ) {
isUserInteracting = true;
onPointerDownPointerX = event.clientX;
onPointerDownPointerY = event.clientY;
onPointerDownLon = lon;
onPointerDownLat = lat;
}
function onDocumentMouseMove( event ) {
if ( isUserInteracting ) {
lon = ( event.clientX - onPointerDownPointerX ) * 0.3 + onPointerDownLon;
lat = ( onPointerDownPointerY - event.clientY ) * 0.3 + onPointerDownLat;
}
}
function onDocumentMouseUp( event ) {
isUserInteracting = false;
}
function animate() {
requestAnimationFrame( animate );
render();
update();
}
function update()
{
stats.update();
}
function render() {
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.Math.degToRad( 90 - lat );
theta = THREE.Math.degToRad( lon );
camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );
camera.target.y = 500 * Math.cos( phi );
camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta );
camera.lookAt( camera.target );
renderer.render( scene, camera );
}
To calculate that latitude and longitude from the x,y,z coordinates you inverse your methods. Please see updated code-pen for demo.
Original:
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.Math.degToRad( 90 - lat );
theta = THREE.Math.degToRad( lon );
camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );
camera.target.y = 500 * Math.cos( phi );
camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta );
Inverse:
Snippet taken from the original code-pen, found on the document.body.onkeydown action event listener within the update() method -- modified this method in the demo with the following code
//Since you are looking at a given shape on keypress, decided to inverse from that
//shape's position.
camera.lookAt( blue1.position );
//Added the following four lines of code
phi = Math.acos((blue1.position.y)/500);
theta = Math.acos((blue1.position.x)/(500 * Math.sin(phi)))
lon = THREE.Math.radToDeg(theta);
lat = 90-THREE.Math.radToDeg(phi);
Finer details in algebra
Seeing as how I always got docked points in school for only showing solutions, here is how I got to the inverse/solving for latitude and longitude from x, y, and z
Let x, y, and z represent your given viewpoint (camera.target.x or <object>.position.x, etc).
Let phi = Φ, theta = Θ
Solving for Θ
x = 500 * sin (Φ) * cos(Θ)
x/(500 * sin(Φ)) = 500 * sin (Φ) * cos(Θ) / (500 * sin(Φ))
x/(500 * sin(Φ)) = cos(Θ)
arccos(x/(500 * sin(Φ))) = arccos(cos(Θ))
arccos(x/(500 * sin(Φ))) = Θ
Solving for Φ
y = 500 * cos(Φ)
y/500 = 500 * cos(Φ) / 500
y/500 = cos(Φ)
arccos(y/500) = arccos(cos(Φ))
arccos(y/500) = Φ
Solving for lon
Θ = degToRad(lon)
radToDeg(Θ) = degToRad(degToRad(lon))
radToDeg(Θ) = lon
solving for lat
Φ = degToRad( 90 - lat );
radToDeg(Φ) = radToDeg(degToRad( 90 - lat ))
radToDeg(Φ) = 90 - lat
radToDeg(Φ) - 90 = 90 - lat - 90
radToDeg(Φ) - 90 = -lat
-1*(radToDeg(Φ) - 90) = -1*(-lat)
-radToDeg(Φ + 90 = lat
90 - radToDeg(Φ) = lat
90 - radToDeg(Φ) = lat
camera.lookAt(yourObject.position);//?
Supposing i got you right and the question is this part: "a way to rotate the camera to look at an object"
BTW I think it's confusing in your code how you both modify camera.target and then also call camera.lookAt. I think when you call lookAt you don't need to modify the target, and vice versa.