THREE.js: OrbitControls pan and zoom issue - three.js

I tried adding the latest OrbitControls.js to my scene and orbit seams to work ok. However, when I zoom using the middle mouse button or scroll wheel the axis seams to be off and it no longer rotates correctly. Pan (or strafe) does not seem to work correctly in my scene either.
In the example, http://threejs.org/examples/#misc_controls_orbit right mouse button moves the camera parallel to the scene and in my scene it just orbits the same as left mouse button. You can see how mine is misbehaving http://www.xrez.com/tufa_test/.
<!DOCTYPE html>
<html lang="en">
<head>
<title>obj tester</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:#fff;
padding:0;
margin:0;
overflow:hidden;
font-family:georgia;
text-align:center;
}
</style>
</head>
<body>
<script src="cam.js"></script>
<script src="three.min.js"></script>
<script src="OrbitControls.js"></script>
<script>
var SCREEN_WIDTH = window.innerWidth;
var SCREEN_HEIGHT = window.innerHeight;
var container;
var camera, scene, controls, renderer;
var canvasRenderer, webglRenderer;
var mesh, zmesh, geometry;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var meshes = [];
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 100000);
camera.position.x = 400;
camera.position.y = 200;
camera.position.z = 400;
controls = new THREE.OrbitControls( camera );
controls.addEventListener( 'change', render );
scene = new THREE.Scene();
// LIGHTS
var ambient = new THREE.AmbientLight(0xFFFFFF);
scene.add(ambient);
// var directionalLight = new THREE.DirectionalLight(0x000000);
//directionalLight.position.set(0, 70, 100).normalize();
//scene.add(directionalLight);
// RENDERER
webglRenderer = new THREE.WebGLRenderer();
webglRenderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
webglRenderer.domElement.style.position = "relative";
container.appendChild(webglRenderer.domElement);
var loader = new THREE.JSONLoader(),
callbackKey = function(geometry) {createScene(geometry, 0, 0, 0, 15, "twe.jpg")};
loader.load("tufaWallEarly02_v3.js", callbackKey);
window.addEventListener('resize', onWindowResize, false);
}
function createScene(geometry, x, y, z, scale, tmap) {
zmesh = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({map: THREE.ImageUtils.loadTexture(tmap)}));
zmesh.position.set(x, y, z);
zmesh.scale.set(scale, scale, scale);
meshes.push(zmesh);
scene.add(zmesh);
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
webglRenderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
for(var i = 0; i < meshes.length; i++){
meshes[i].rotation.y += .001;
}
requestAnimationFrame(animate);
controls.update();
render();
}
function render() {
camera.lookAt(scene.position);
webglRenderer.render(scene, camera);
}
</script>
</body>

remove the camera.lookAt(scene.position); line

In OrbitControls.js line 219 find onMouseDown
and comment this lines
/*if ( event.button === 2 )
state = STATE.PAN;*/

Related

Change the color of 3D model using colorPicker three.js

I am trying to change the color of 3D object where intersected using color picker.I am trying with dat.gui.I want to change the color of 3d part where it gets clicked and change the selected from the colorPicker.I tried out some possible ways but it doesn't work out.Please,refer to the code I tried out. Help me out with some solution and draw my attention to where I am getting wrong. Thanks.
<!DOCTYPE html>
<html lang="en">
<head>
<title>color</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: #000;
color: #fff;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<script src="three.js"></script>
<script src="OrbitControls.js"></script>
<script src="Detector.js"></script>
<script src="stats.min.js"></script>
<script src="loaders/MTLLoader.js"></script>
<script src="loaders/OBJLoader.js"></script>
<script type='text/javascript' src='DAT.GUI.min.js'></script>
<script>
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
var container, stats;
var camera, controls, scene, renderer,effectController;
var raycaster;
var objects = [];
var selectedObject,selectedPos;
var rotation;
var pos,quat;
var INTERSECTED;
var guiColor;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 15;
controls = new THREE.OrbitControls( camera );
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x555000 );
scene.add( camera );
// light
var dirLight = new THREE.DirectionalLight( 0xffffff );
dirLight.position.set( 200, 200, 1000 ).normalize();
camera.add( dirLight );
camera.add( dirLight.target );
var mtlLoader = new THREE.MTLLoader(); mtlLoader.setBaseUrl('assets/'); mtlLoader.setPath('assets/'); mtlLoader.load('anno.mtl', function (materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.setPath('assets/');
objLoader.load('anno.obj', function (object) {
scene.add( object );
objects.push( object );
});
});
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
/* Controls */
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = false;
raycaster = new THREE.Raycaster();
gui = new dat.GUI();
parameters =
{
color: "#ff0000",
};
gui.add( parameters, 'reset' ).name("Reset");
guiColor = gui.addColor( parameters, 'color' ).name('Color');
container = document.createElement( 'div' );
document.body.appendChild( container );
container.appendChild( renderer.domElement );
stats = new Stats();
container.appendChild( stats.dom );
window.addEventListener( 'resize', onWindowResize, false );
renderer.domElement.addEventListener("click", onclick, false);
}
var mouse = new THREE.Vector2();
function onclick(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(objects, true);
if (intersects.length > 0) {
INTERSECTED = intersects[0].object;
if ( INTERSECTED && INTERSECTED.material.emissive != null ){
guiColor.onChange(function(){
INTERSECTED.material.emissive.setHex(parameters.color)
});
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
stats.update();
}
</script>
</body>
</html>
I've create a little live demo with your code and a basic working solution. I'd like to highlight three important changes:
You can use the onChange() event handler in order to know when a certain dat.gui property has changed. The demo uses this feature to update the color of a selected object.
I have refactored your raycasting logic into something more simple. I've seen you've copied some code from the official three.js examples but the new code should be sufficient for your case. Besides, it's also better to update Material.color instead of Material.emissive.
If you set OrbitControls.enableDamping to true, you have to update the controls in your animation loop.
https://jsfiddle.net/btuzd23o/2/
three.js R103

ThreeJS full scene minimal rotation

I want to know on how does this website (http://www.cryptarismission.com/) do it's scene face to different directions when you move your mouse over.
On their home page, it seems that when you move your mouse, the scene follows smoothly in a very small amount of axis rotation.
What is the idea behind this thing using Three.js? I know that you can rotate object, scene or the camera but I am not sure what to rotate. I'm thinking of rotating the whole scene container but I don't know if that's good.
You can either change camera position and call lookAt in every loop depending on mouse position or change whole scene rotation. Here is how first approach works.
<!DOCTYPE html>
<html lang="en">
<head>
<title>ThreeJS full scene minimal rotation</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<style>
body {
margin: 0px;
padding: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<canvas></canvas>
</body>
<script>
var camera, scene, renderer, stats, windowHalfX = window.innerWidth / 2,
windowHalfY = window.innerHeight / 2,
mouseX = 0,
mouseY = 0;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.set(30, 30, 30)
scene = new THREE.Scene();
scene.add(new THREE.Mesh(
new THREE.BoxGeometry(5, 5, 5),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
));
renderer = new THREE.WebGLRenderer({ antialias: true, canvas: document.querySelector('canvas') });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
window.addEventListener('mousemove', onDocumentMouseMove, false);
}
function onDocumentMouseMove(event) {
mouseX = (event.clientX - windowHalfX) / 10;
mouseY = (event.clientY - windowHalfY) / 10;
}
function animate() {
requestAnimationFrame(animate);
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);
}
</script>
</html>

Annotation using three.js

I have two cubes of .obj file.The obj file is render on the browser. I want to do is as the user click on any of the cube or anywhere on the cube one prompt box display to add annotation to that place on the cube.How to do this? I am new to three.js. Anyone can help me out?
Here is my code with.obj file
<!DOCTYPE html>
<html>
<head>
<title>Mouse Picking</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="three.js"></script>
<script src="Detector.js"></script>
<script src="OrbitControls.js"></script>
<script src="OBJLoader.js"></script>
<script src="MTLLoader.js"></script>
<script src="DragControls.js"></script>
<style>
body {
overflow: hidden;
margin: 0;
padding: 0;
background: hsl(0, 0%, 10%);
}
</style>
</head>
<body>
<script>
if (!Detector.webgl) {
Detector.addGetWebGLMessage();
}
var container;
var camera, controls, scene, renderer;
var lighting, ambient, keyLight, fillLight, backLight;
var BlueCube, RedCube;
var objects = [];
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
/* Camera */
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.x = 5;
camera.position.y = 3;
camera.position.z = 7;
/* Scene */
scene = new THREE.Scene();
lighting = true;
ambient = new THREE.AmbientLight(0xffffff, 2.5);
scene.add(ambient);
keyLight = new THREE.DirectionalLight(new THREE.Color('hsl(30, 100%, 75%)'), 1.0);
keyLight.position.set(-100, 0, 100);
fillLight = new THREE.DirectionalLight(new THREE.Color('hsl(240, 100%, 75%)'), 0.75);
fillLight.position.set(100, 0, 100);
backLight = new THREE.DirectionalLight(0xffffff, 1.0);
backLight.position.set(100, 0, -100).normalize();
/* Model */
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setBaseUrl('assets/');
mtlLoader.setPath('assets/');
mtlLoader.load('mouse_picking.mtl', function (materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.setPath('assets/');
objLoader.load('mouse_picking.obj', function (object) {
scene.add( object );
objects.push( object );
});
});
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2();
/* Renderer */
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(new THREE.Color("hsl(0, 0%, 10%)"));
container.appendChild(renderer.domElement);
/* Controls */
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = false;
/* Events */
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
//selected();
requestAnimationFrame(animate);
controls.update();
render();
}
function render() {
renderer.render(scene, camera);
}
</script>
</body>
</html>
I recommend that you subdivide the geometry of the cube mesh, and then use raycasting to determine the part of the scene (specifically, the triangle in the mesh) that was clicked. Then you could add a sprite object, which is a plane that always faces the camera, to display some text.
For more information, check out the collection of examples at http://stemkoski.github.io/Three.js/index.html - they are a little outdated, but the examples "Mouse Click" and "Sprite Text Labels" might help you to get started.

threejs : error when deleting a Geometry or BufferGeometry in combination with a Sprite/Ortho camera

I load some objects using the ctm binary loader in Threejs r69. This returns Mesh objects using a BufferGeometry internally.
I need to remove from the scene then delete one of these Meshes, including their material/texture/geometry. According to examples and google, I should use:
scene.remove(m_mesh);
m_mesh.geometry.dispose();
m_mesh.geometry = null;
m_mesh.material.dispose();
m_mesh.material = null;
m_mesh = null;
This removes the object from the scene, but the screen goes black for a second, and I've got a GL error :
Error: WebGL: drawElements: no VBO bound to enabled vertex attrib index 2!
Looks like the above sequence (ran in my render() operation, just before drawing the scene) did not clean everything, or at least I still have references to non existing VBOs somewhere.
I've spent quite some time debugging the problem and came to the conclusion that this happens only when using an orthographic camera with a Sprite and a Perspective camera, in 2 differents scenes.
Basically, I draw a flat background using a Sprite and a dedicated scene, then my 3D scene with Meshes. If I delete a mesh from the 3D scene, then the drawing of the flat background fails.
I can't figure out why. Looks like there's a side effect of deleting a Mesh on Sprites, even if attached to different scenes.
If I comment the background drawing, then the deletion of my mesh works perfectly.
I insert below a reproduction of the problem using the standard threejs distribution. Wait about 5 seconds and you should see some GL errors on the jaavscript console.
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - geometries</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: #000;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<script src="build/three.min.js"></script>
<script src="js/Detector.js"></script>
<script src="js/libs/stats.min.js"></script>
<script>
if (!Detector.webgl) Detector.addGetWebGLMessage();
var container, stats;
var camera, scene, renderer;
frame_count = 0;
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.y = 400;
// I need a second camera for my 2D sprite (used as a background)
// must use another texture so that it's not destroyed when removing the first object
cameraOrtho = new THREE.OrthographicCamera(window.innerWidth / -2,
window.innerWidth / 2,
window.innerHeight / 2,
window.innerHeight / -2,
1, 10);
cameraOrtho.position.z = 10;
cameraOrtho.position.y = 400;
sceneBackground = new THREE.Scene();
var map1 = THREE.ImageUtils.loadTexture('textures/disturb.jpg');
var material1 = new THREE.SpriteMaterial({
map: map1
});
var spriteBackground = new THREE.Sprite(material1);
spriteBackground.scale.set(window.innerWidth, window.innerHeight, 1);
spriteBackground.position.set(window.innerWidth / 2,
window.innerHeight / 2);
sceneBackground.add(spriteBackground);
scene = new THREE.Scene();
var light;
my_object = null;
scene.add(new THREE.AmbientLight(0x404040));
light = new THREE.DirectionalLight(0xffffff);
light.position.set(0, 1, 0);
scene.add(light);
var map = THREE.ImageUtils.loadTexture('textures/UV_Grid_Sm.jpg');
map.wrapS = map.wrapT = THREE.RepeatWrapping;
map.anisotropy = 16;
var material = new THREE.MeshLambertMaterial({
map: map,
side: THREE.DoubleSide
});
// one object is enough to demonstrate
// can't reproduce the problem with a standard SphereGeometry
// try to convert it to a BufferGeometry
var sphereGeometry = new THREE.SphereGeometry(75, 20, 10);
var bufferGeometry = new THREE.BufferGeometry().fromGeometry(sphereGeometry);
my_object = new THREE.Mesh(bufferGeometry, material);
my_object.position.set(-400, 0, 200);
scene.add(my_object);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.autoClear = false;
renderer.autoClearDepth = false;
container.appendChild(renderer.domElement);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild(stats.domElement);
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
//
function animate() {
requestAnimationFrame(animate);
render();
stats.update();
}
function render() {
frame_count++;
var timer = Date.now() * 0.0001;
camera.position.x = Math.cos(timer) * 800;
camera.position.z = Math.sin(timer) * 800;
camera.lookAt(scene.position);
//after a few frames I want to destroy completely the object
//means remove from the scene, remove texture, material, geometry
//note that here it's a Geometry, not a BufferGeometry
//may be different
if (frame_count > 60 * 5) {
if (my_object != null) {
console.log("destroy object buffer");
scene.remove(my_object);
my_object.material.map.dispose();
my_object.material.dispose();
my_object.geometry.dispose();
my_object = null;
}
}
for (var i = 0, l = scene.children.length; i < l; i++) {
var object = scene.children[i];
object.rotation.x = timer * 5;
object.rotation.y = timer * 2.5;
}
renderer.render(sceneBackground, cameraOrtho);
renderer.clearDepth();
renderer.render(scene, camera);
}
</script>
</body>
</html>
Any hints on how to fix this issue?
Thank you,
Pascal

How to add another image in a scene in Three.js

I have this code and I cannot seem to be able to add any other image/texture to it. i tried this simple code right after scene = new Three.Scene();
var sphere1 = createMesh(new THREE.CubeGeometry(15, 15, 15), "plaster.jpg");
sphere1.rotation.y = -0.5;
sphere1.position.x = 12;
scene.add(sphere1);
into this code of my project
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js canvas - geometry - earth</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link href="styles/styles.css" rel="stylesheet" type="text/css"></link>
<style>
body {
background:#3b404a;
margin:0; padding:0;
}
</style>
</head>
<body>
<div class="wrapperBG">
<!-- top menu -->
<div class="menu">
</div> <!-- end .menu -->
<!-- end top menu -->
<div id="container"></div>
<div id="info">three.js - earth demo</div>
<script src="../build/three.min.js"></script>
<script src="js/libs/stats.min.js"></script>
<script>
var container, stats;
var camera, scene, renderer;
var group;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function init() {
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 700;
camera.position.y = 170;
scene = new THREE.Scene();
group = new THREE.Object3D();
scene.add( group );
// earth
var image = 'WorldMap.svg';
var loader = new THREE.TextureLoader();
loader.load( image, function ( texture ) {
//var geometry = new THREE.SphereGeometry( 180, 60, 60 );
var geometry = new THREE.PlaneGeometry(1024, 560);
var material = new THREE.MeshBasicMaterial( { map: texture, overdraw: 0.5 } );
var mesh = new THREE.Mesh( geometry, material );
group.add( mesh );
} );
renderer = new THREE.WebGLRenderer({alpha : true});
//renderer.setClearColor( 0xffa200, 0.5 );
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 );
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX );
mouseY = ( event.clientY - windowHalfY );
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
//camera.position.x += ( mouseX - camera.position.x ) * 0.05;
//camera.position.y += ( - mouseY - camera.position.y ) * 0.05;
/* ============= FLAT MAP INTERCATIVITY COORDINATES ============ */
camera.position.x += ( mouseX - (camera.position.x*50) ) * 0.007;
camera.position.y += ( - mouseY - (camera.position.y *10) ) * 0.007;
/*================================================================ */
camera.lookAt( scene.position );
//group.rotation.y -= 0.01;
renderer.render( scene, camera );
}
</script>
<!-- ====== EDITING THE SVG MAP =====
<object data="WorldMap.svg" type="image/svg+xml" id="map"></object>
<script>
// refers to entire map loaded as object
//note: svg document must have same path as the accessor
var world = document.getElementById("map");
//load asynchronous svg
world.addEventListener("load",function(){
var region = world.contentDocument; //get the inner DOM of WorldMap.svg
var mapRegion = region.getElementById("DZ"); //get Algeria
mapRegion.setAttribute('fill', '#000000');
},false);
</script>
================================ -->
</div> <!-- end .wrapperBG -->
</body>
</html>
I have the world map loaded but now i cannot add anything else to the scene. how can i do that?
Look at your code and your project code.
var sphere1 = createMesh(new THREE.CubeGeometry(15, 15, 15), "plaster.jpg");
This is not going to work as plaster-jpg is a texture, not a material.
Create a material first like you did in your project with:
var material = new THREE.MeshBasicMaterial( { map: texture, overdraw: 0.5 } );
where texture is your plaster.jpg which is loaded using the image loader, too.
This should do the trick. Just stick to your projects initial code! :)

Resources