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
Related
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!
Quick run down - I'm trying to build a gallery of 360 images, when a gallery image is clicked, modal opens with 360 viewer inside, but I want the images to change dynamically based on the gallery image that has been clicked.
THREE.WebGLRenderer: image is not power of two (1920x960). Resized to 2048x1024
blob:http://nolan.bfdevserver.com/7ca91892-2e3b-4fff-b672-ec250ef13498 new image blob
blob:http://nolan.bfdevserver.com/7788013c-9611-497a-bc12-66a06bb2244f old image blob
THREE.WebGLRenderer: image is not power of two (0x0). Resized to 0x0
THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.
THREE.Texture
WebGL: INVALID_VALUE: texImage2D: no canvas
This is the error I'm recieving in the console. As you can see in my code below:
material.minFilter = THREE.LinearFilter;
So I'm not sure why this is happening.
<div id="container" data="<?php $ThreeSixtyImage = the_field('image'); ?>"></div>
<div id="gallery-modal" class="modal">
</div>
<div id="gallery" class="gallery">
<div class="gallery-item">
<img src="https://www.bluefire360.com/wp-content/uploads/V4260450-min.jpg" />
</div>
<div class="gallery-item">
<img src="http://nolan.bfdevserver.com/wp-content/uploads/pano_2048.jpg" />
</div>
<div class="gallery-item">
<img src="http://nolan.bfdevserver.com/wp-content/uploads/pano_2048.jpg" />
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
var camera, scene, renderer;
var Container = document.getElementById('container');
var imageFile = Container.getAttribute('data');
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( 'gallery-modal' );
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1100 );
camera.target = new THREE.Vector3( 0, 0, 0 );
scene = new THREE.Scene();
var geometry = new THREE.SphereGeometry( 500, 60, 40 );
geometry.scale( - 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( {
map: new THREE.TextureLoader().load( imageFile )
} );
material.minFilter = THREE.LinearFilter;
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
//renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setSize( window.innerWidth, 500);
container.appendChild( renderer.domElement );
// Controls to move the image
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener( 'mouseup', onDocumentMouseUp, false );
document.addEventListener( 'wheel', onDocumentMouseWheel, 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 );
/// ADDITIONAL CODE STARTS HERE
var galleryDOM = document.getElementById('gallery');
var modal = document.getElementById('gallery-modal');
galleryDOM.addEventListener('click', function(e) {
if(e.target.localName == 'img') {
// Show Modal
modal.style.display = "block";
// Create blob from image url
var blob = new Blob([e.target.src], {type: 'url'});
var newestImage = window.URL.createObjectURL(blob);
console.log(newestImage, "new image blob")
console.log(material.map.image.src, "old image blob")
// Apply newestImage to material object in THREEjs
if(newestImage) {
material.map.image.src = newestImage;
material.map.needsUpdate = true;
}
}
});
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = 'none';
}
}
window.addEventListener( 'resize', onWindowResize, false );
}
function dataURItoBlob(dataURI) {
var mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
var binary = atob(dataURI.replace(/^data:image\/(png|jpeg|jpg);base64,/, ''));
var array = [];
for (var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {type: mime});
}
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 ) {
camera.fov += event.deltaY * 0.05;
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 );
/*
// distortion
camera.position.copy( camera.target ).negate();
*/
renderer.render( scene, camera );
}
I've been starring at this for hours so any help or even just a course direction would be GREATLY appreciated. Thanks in advance!
To fix the power of two error, this line needs to be changed:
material.minFilter = THREE.LinearFilter;
to
material.map.minFilter = THREE.LinearFilter;
And to get the texture to update, went back to a standard image url versus a blob url and used:
if(newestImage) {
material.map = new THREE.TextureLoader().load(newestImage);
}
Three.js is new to me, so I am a bit stuck on this.
I read the answer to this question: Using multiuple textures on a sphere [Three.js]
The answer suggests the user to use a cube to load the textures on, instead of sphere.That being done, the cube can be expanded to a sphere.
var geometry = new THREE.BoxGeometry( 1, 1, 1, 8, 8, 8 );
for ( var i in geometry.vertices ) {
var vertex = geometry.vertices[ i ];
vertex.normalize().multiplyScalar(radius);
}
var materialArray = [];
var faceMaterial = new THREE.MeshLambertMaterial({
color: sphereColor,
transparent: true,
opacity: 0.4
});
for (var i = 0; i < 6; i++) {
materialArray.push(faceMaterial);
}
var material = new THREE.MeshFaceMaterial(materialArray);
var sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );
Will the same thing work for videos? If I wish to play six videos on a sphere, should I load the 'video' texture onto the six faces of the cube and then expand it to a sphere.
EDIT:
I have the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - equirectangular video panorama</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
background-color: #000000;
margin: 0px;
overflow: hidden;
}
#info {
position: absolute;
top: 0px; width: 100%;
color: #ffffff;
padding: 5px;
font-family:Monospace;
font-size:13px;
font-weight: bold;
text-align:center;
}
a {
color: #ffffff;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="three.js"></script>
<script>
var camera, scene, renderer;
var texture_placeholder,
isUserInteracting = false,
onMouseDownMouseX = 0, onMouseDownMouseY = 0,
lon = 0, onMouseDownLon = 0,
lat = 0, onMouseDownLat = 0,
phi = 0, theta = 0,
distance = 500;
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( 0, 0, 0 );
scene = new THREE.Scene();
//var geometry = new THREE.SphereBufferGeometry( 500, 60, 40 );
//geometry.scale( - 1, 1, 1 );
var geometry = new THREE.BoxBufferGeometry( 1,1,1,32,32,32 );
geometry.scale( - 1, 1, 1 );
for ( var i in geometry.vertices ) {
var vertex = geometry.vertices[ i ];
vertex.normalize().multiplyScalar(distance);
}
//var video = document.createElement( 'video' );
//video.width = 640;
//video.height = 360;
//video.autoplay = true;
//video.loop = true;
//video.src = "pano.webm";
var path = "vids/";
var format = '.mp4';
var urls = [
path + 'Row1Col1' + format, path + 'Row1Col2' + format,
path + 'Row1Col3' + format, path + 'Row2Col1' + format,
path + 'Row2Col2' + format, path + 'Row2Col3' + format
];
var reflectionCube = new THREE.CubeTextureLoader().load( urls );
reflectionCube.minFilter = THREE.LinearFilter;
reflectionCube.format = THREE.RGBFormat;
//var texture = new THREE.VideoTexture( video );
//texture.minFilter = THREE.LinearFilter;
//texture.format = THREE.RGBFormat;
//var material = new THREE.MeshBasicMaterial( { map : texture } );
//mesh = new THREE.Mesh( geometry, material );
//scene.add( mesh );
var materialArray = [];
var faceMaterial = new THREE.MeshLambertMaterial({
color: 0xffff00,
transparent: true,
opacity: 0.4
});
for (var i = 0; i < 6; i++) {
materialArray.push(faceMaterial);
}
var material = new THREE.MeshFaceMaterial(materialArray);
var sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );
//End of what I have added
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( 'mousewheel', onDocumentMouseWheel, false );
document.addEventListener( 'MozMousePixelScroll', 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;
}
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 ) {
distance -= event.wheelDeltaY * 0.05;
// Opera / Explorer 9
} else if ( event.wheelDelta ) {
distance -= event.wheelDelta * 0.05;
// Firefox
} else if ( event.detail ) {
distance += event.detail * 1.0;
}
}
function animate() {
requestAnimationFrame( animate );
update();
}
function update() {
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.Math.degToRad( 90 - lat );
theta = THREE.Math.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( camera.target );
/*
// distortion
camera.position.copy( camera.target ).negate();
*/
renderer.render( scene, camera );
}
</script>
</body>
However, it does not work. After I host it on the web there's just a black screen. Can you tell me where am I going wrong?
Based on some of my recent findings working with 360 videos in threejs (I am also new to threejs), you could be seeing the black screen due to the overall resolution being too high. If each video is at 4k resolution, then you are trying to display a 24k res video. I start encountering issues where I get black screen after 5.7k, even on higher end equipment and 100+ mbps network speeds.
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've created a tube geometry with data of 200 points loaded from external javascript file in JSON format. Please find the below code.
<!DOCTYPE html>
<html lang="en">
<head>
<title>3d Model using HTML5 and three.js</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<input type="button" value="plot" onClick="return plotPath();" />
<script src="three.min.js" type="text/javascript"></script>
<script src="Curve.js" type="text/javascript"></script>
<script src="TubeGeometry.js" type="text/javascript"></script>
<script src="Stats.js" type="text/javascript"></script>
<script src="Detector.js" type="text/javascript"></script>
<script src="path.js" type="text/javascript"></script>
<script>
// variables
var container, stats;
var camera, scene, renderer, controls, stats;
var text, plane, tube, tubeMesh, parent;
var targetRotation = 0;
var targetRotationOnMouseDown = 0;
var mouseX = 0;
var mouseXOnMouseDown = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var radius = 600;
var theta = 0;
var PI2 = Math.PI * 2;
function plotPath()
{
var obj = getPath();
var segments = 50;
var closed = false;
var debug = true;
var radiusSegments = 12;
var tube;
var points = [];
var x=0,y=0,z=0;
for(var i=0; i<obj.path.length; i++)
{
console.log(obj.path[i].point);
points.push(obj.path[i].point);
extrudePath = new THREE.SplineCurve3(points);
extrudePath.dynamic = true;
tube = new THREE.TubeGeometry(extrudePath, segments, 2, radiusSegments, closed, debug);
tube.dynamic = true;
tubeMesh = new THREE.Mesh(tube ,new THREE.MeshBasicMaterial({
color: 0x000000, side: THREE.DoubleSide,
opacity: 0.5, transparent: true, wireframe: true}));
tubeMesh.__dirtyVertices = true;
tubeMesh.dynamic = true;
parent = new THREE.Object3D();
parent.position.y = 100;
if ( tube.debug ) tubeMesh.add( tube.debug );
parent.add( tubeMesh );
}
scene.add( tubeMesh );
scene.add(parent);
animate();
}
init();
function init(){
// container
container = document.createElement( 'div' );
document.body.appendChild( container );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setClearColorHex(0xEEEEEE, 1.0);
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.clear();
container.appendChild( renderer.domElement );
// camera
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set(-100,75,75);
// scene
scene = new THREE.Scene();
camera.lookAt(scene.position);
// light
scene.add( new THREE.AmbientLight( 0x404040 ) );
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 0, 1, 0 );
scene.add( light );
// CONTROLS
controls = new THREE.TrackballControls( camera );
// Grid
geometry = new THREE.Geometry();
geometry.vertices.push( new THREE.Vector3( - 500, 0, 0 ) );
geometry.vertices.push( new THREE.Vector3( 500, 0, 0 ) );
for ( var i = 0; i <= 20; i ++ ) {
line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2 } ) );
line.position.z = ( i * 50 ) - 500;
scene.add( line );
line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2 } ) );
line.position.x = ( i * 50 ) - 500;
line.rotation.y = 90 * Math.PI / 180;
scene.add( line );
}
// projector
projector = new THREE.Projector();
plotPath();
// stats
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
document.addEventListener( 'mouseover', onDocumentMouseOver, false );
document.addEventListener( 'touchstart', onDocumentTouchStart, false );
document.addEventListener( 'touchmove', onDocumentTouchMove, false );
window.addEventListener('DOMMouseScroll', mousewheel, false);
window.addEventListener('mousewheel', mousewheel, false);
window.addEventListener( 'resize', onWindowResize, false );
}
function mousewheel(event) {
var fovMAX = 160;
var fovMIN = 1;
camera.fov -= event.wheelDeltaY * 0.05;
camera.fov = Math.max( Math.min( camera.fov, fovMAX ), fovMIN );
camera.projectionMatrix = new THREE.Matrix4().makePerspective(camera.fov, window.innerWidth / window.innerHeight, camera.near, camera.far);
}
function onWindowResize() {
camera.left = window.innerWidth / - 2;
camera.right = window.innerWidth / 2;
camera.top = window.innerHeight / 2;
camera.bottom = window.innerHeight / - 2;
camera.aspect = window.innerWidth / window.innerHeight;
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseDown( event ) {
event.preventDefault();
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener( 'mouseup', onDocumentMouseUp, false );
document.addEventListener( 'mouseout', onDocumentMouseOut, false );
mouseXOnMouseDown = event.clientX - windowHalfX;
targetRotationOnMouseDown = targetRotation;
}
function onDocumentMouseMove( event ) {
mouseX = event.clientX - windowHalfX;
targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.02;
}
function onDocumentMouseUp( event ) {
document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
document.removeEventListener( 'mouseout', onDocumentMouseOut, false );
}
function onDocumentMouseOut( event ) {
document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
document.removeEventListener( 'mouseout', onDocumentMouseOut, false );
}
function onDocumentMouseOver( event ) {
document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
document.removeEventListener( 'mouseout', onDocumentMouseOut, false );
}
function onDocumentTouchStart( event ) {
if ( event.touches.length === 1 ) {
event.preventDefault();
mouseXOnMouseDown = event.touches[ 0 ].pageX - windowHalfX;
targetRotationOnMouseDown = targetRotation;
}
}
function onDocumentTouchMove( event ) {
if ( event.touches.length === 1 ) {
event.preventDefault();
mouseX = event.touches[ 0 ].pageX - windowHalfX;
targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.05;
}
}
function animate() {
requestAnimationFrame( animate, renderer.domElement );
render();
update();
}
function update(){
controls.update();
}
function toCameraCoords(position) {
return camera.matrixWorldInverse.multiplyVector3(position.clone());
}
function render() {
tubeMesh.rotation.y += ( targetRotation - tubeMesh.rotation.y ) * 0.15;
camera.lookAt(scene.position);
camera.updateMatrixWorld();
renderer.render( scene, camera );
}
</script>
</body>
</html>
When I use mouse wheel to zoom in, the camera will zoom in at starting point of the tube. How do I make the tube geometry can be zoomed completely or in other words any part of the tube can be zoomed ?
As with any conventional real life camera, you can change the focal length (zoom) of THREE.PerspectiveCamera by calling setLens method. I find this way very intuitive and much simplier than moving the camera to zoom in/out.
Here is the method docs in three.js:
/**
* Uses Focal Length (in mm) to estimate and set FOV
* 35mm (fullframe) camera is used if frame size is not specified;
* Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html
*/
THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight )
...
And here is an example usage:
var focalLength = 25.734; // equivalent to FOV=50
$('#scene').mousewheel(function (event, delta, deltaX, deltaY) {
event.preventDefault();
focalLength += deltaY;
camera.setLens(focalLength);
});
The method jQuery.fn.mousewheel is provided by Mousewheel Plugin.
document.body.addEventListener( 'mousewheel', mousewheel, false );
document.body.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox
function mousewheel( e ) {
var d = ((typeof e.wheelDelta != "undefined")?(-e.wheelDelta):e.detail);
d = 100 * ((d>0)?1:-1);
var cPos = camera.position;
if (isNaN(cPos.x) || isNaN(cPos.y) || isNaN(cPos.y))
return;
var r = cPos.x*cPos.x + cPos.y*cPos.y;
var sqr = Math.sqrt(r);
var sqrZ = Math.sqrt(cPos.z*cPos.z + r);
var nx = cPos.x + ((r==0)?0:(d * cPos.x/sqr));
var ny = cPos.y + ((r==0)?0:(d * cPos.y/sqr));
var nz = cPos.z + ((sqrZ==0)?0:(d * cPos.z/sqrZ));
if (isNaN(nx) || isNaN(ny) || isNaN(nz))
return;
cPos.x = nx;
cPos.y = ny;
cPos.z = nz;
}
or even simpler ajusting only the camera :
var mousewheel = function ( e ) {
var d = ((typeof e.wheelDelta != "undefined")?(-e.wheelDelta):e.detail);
d = 100 * ((d>0)?1:-1);
var cPos = camera.position;
if (isNaN(cPos.x) || isNaN(cPos.y) || isNaN(cPos.y)) return;
// Your zomm limitation
// For X axe you can add anothers limits for Y / Z axes
if (cPos.x > _YOUR_ZOOM_MIN_X_ || cPos.x < _YOUR_ZOOM_MAX_X_ ){
return ;
}
mb = d>0 ? 1.1 : 0.9;
cPos.x = cPos.x * mb;
cPos.y = cPos.y * mb;
cPos.z = cPos.z * mb;
}