Orbit Controls is not a constructor - three.js

I followed a tutorial of 2018 in order to add OrbitControls in an existing working project.
These are the lines of code i added:
<script type="text/javascript" src="../libs/OrbitControls.js"></script>
var controls; // AS A GLOBAL VARIABLE
//following three lines are inside the init()
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.minDistance = 1;
controls.maxDistance = 1000;
//at the animate() function
controls.update();
However, nothing is rendered at the browser. When i take a look at the console, i see that:
Uncaught SyntaxError: Unexpected token '<' at OrbitControls.js:6
test.html:130 Uncaught TypeError: THREE.OrbitControls is not a constructor
at init (test.html:130)
I found another question with a similar problem but it did not help me.
The only scripts i include are three.js, OrbitControls.js, dat.gui.min.js.
FULL CODE:
<!DOCTYPE html>
<html>
<head>
<title>Example 01.02 - First Scene</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/OrbitControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>
<style>
body {
/* set margin to 0 and overflow to hidden, to go fullscreen */
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<!-- Div which will hold the Output -->
<div id="WebGL-output">
</div>
<!-- Javascript code that runs our Three.js examples -->
<script type="text/javascript">
window.onload = init();
animate(); //calling function that does all the rendering
//GLOBAL VARS
var scene, camera, renderer;
var cube;
var raycaster, mouse;
var INTERSECTED;
//global flag
var isClicked = false;
//for the camera
var controls;
//creating and rendering the GUI
params = {
yAxis: "0.00001"
}
var gui = new dat.GUI();
gui.add(params, "yAxis").onFinishChange(val => {
cube.scale.y = parseFloat(val);
});
//we make sure to make it hidden
let vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
// once everything is loaded, we run our Three.js stuff.
function init() {
// create a scene, that will hold all our elements such as objects, cameras and lights.
scene = new THREE.Scene();
//SET CAMERA
camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000)
camera.position.z = 5;
// create a render and set the size
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setClearColor("#e5e5e5"); //background color
renderer.setSize(window.innerWidth,window.innerHeight); //size of renderer
//bind rendered to the dom element
document.getElementById("WebGL-output").appendChild(renderer.domElement);
//RAYCASTER
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2(1,1);
// create a cube
var cubeGeometry = new THREE.BoxGeometry(20, 20, 20);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xffff00 }); //0xF7F7F7 = gray
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.scale.y = 0.00001;
cube.userData.originalColor = 0xffff00;
// position the cube
cube.position.x = 0;
cube.position.y = 3;
cube.position.z = 0;
/*
//USEFUL METHODS
cube.rotation.x +=0.5
cube.scale.x +=0.5
*/
// add the cube to the scene
scene.add(cube);
/* RENDERING A PLANE
var geometry = new THREE.PlaneGeometry( 20, 20);
var material = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} );
var plane = new THREE.Mesh( geometry, material );
plane.rotation.set(80,0,0);
scene.add( plane );
//plane.position.x = 2;
*/
//ADDING LIGHTS
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
// position and point the camera to the center of the scene
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
//camera
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.minDistance = 1;
controls.maxDistance = 1000;
// when the mouse moves, call the given function
document.addEventListener('mousemove', onDocumentMouseMove, false);
//when the mouse is clicked, call the given function
document.addEventListener('click', onDocumentMouseClick, false);
}
function onDocumentMouseMove(event)
{
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
event.preventDefault();
// update the mouse variable
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
//TRY THIS
// intersects = raycaster.intersectObject(cube); // to get the cube only
//if the mouse hovers over the cube mesh, change its color to red
//when mouse leaves the mesh, change it back to its original color
//ONLY MAKE THESE MODIFICATION IF THE MESH IS NOT CLICKED
//BECAUSE IF IT IS CLICKED, YOU HAVE TO PAINT THE MESH ACCORDING TO THE onDocumentMouseClick()
if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === false)
{
cube.material.color.set( 0xff0000 );
}
else if (isClicked === false)
{
cube.material.color.set( cube.userData.originalColor );
}
}
function onDocumentMouseClick(event) //if we detect a click event
{
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
event.preventDefault();
// update the mouse variable
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
//if mouse is on top of the mesh when the click occurs, change color of mesh and render GUI
if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === false)
{
isClicked = true;
cube.material.color.set( 0xF7F7F7 );
/*
var params = {
textField: "Enter value:"
}
var item = gui.add(params, "textField").onFinishChange(function (value) {
//Do something with the new value
//console.log(value);
cube.scale.y +=value;
});
*/
//when its clicked, change the visibily of the GUI
vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
}
//if mouse is on top of the mesh when the click occurs, but it already marked as 'clicked', now mark it as 'unclicked'
else if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === true)
{
isClicked = false;
cube.material.color.set( cube.userData.originalColor );
//when its clicked, change the visibily of the GUI
vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
// gui.__proto__.constructor.toggleHide()
//dat.GUI.toggleHide();
//gui.toggleHide()
}
}
function render()
{
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
renderer.render(scene, camera); //render the scene
}
function animate()
{
requestAnimationFrame( animate ); //pauses when user switches tab
controls.update();
render();
}
</script>
</body>
</html>

Did you try removing the "type = text/javascript" when you import OrbitControls.js ?

Related

I need to zoom on an object upon hover

I want something like this:
https://drive.google.com/file/d/1v_4M59Ny93QwT52unfM9N_tfzo3xRAq1/view?usp=share_link
This is my code
<!DOCTYPE html>
<html>
<head>
<title>Three.js JPG Background with Custom Position</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r121/three.min.js"></script>
<script src="./js/tween.js/dist/tween.umd.js"></script>
<!-- Css -->
<style>
body {
overflow: hidden;
}
canvas {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
}
</style>
</head>
<body>
<script>
// Initialize Three.js scene
var scene = new THREE.Scene();
var camera = new THREE.OrthographicCamera(window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 0.1,);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Load the Main BG texture
var mainBg = new THREE.TextureLoader().load("http://127.0.0.1:5500/img/main-bg.jpg");
// Create a full-screen plane to display the texture
var geometry1 = new THREE.PlaneGeometry(window.innerWidth, window.innerHeight);
var material1 = new THREE.MeshBasicMaterial({ map: mainBg });
var plane1 = new THREE.Mesh(geometry1, material1);
plane1.position.set(0, 0, 0)
scene.add(plane1);
// Create a full-screen plane to display the texture
var frame1 = new THREE.TextureLoader().load("http://127.0.0.1:5500/img/frame1.png");
var geometry2 = new THREE.PlaneGeometry(90, 110);
var material2 = new THREE.MeshBasicMaterial({ map: frame1 });
var plane2 = new THREE.Mesh(geometry2, material2);
plane2.position.set(-190, 130, .1);
scene.add(plane2);
// Check if mouse is over the child asset
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var zoomed = false;
// Mouse move function
function onMouseMove(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([plane2]);
if (intersects.length > 0 && !zoomed) {
zoomed = true;
camera.position.set(plane2.position.x, plane2.position.y, plane2.position.z);
camera.lookAt(plane2.position.x, plane2.position.y, plane2.position.z);
} else if (intersects.length === 0 && zoomed) {
zoomed = false;
camera.position.set(0, 0, 5);
camera.lookAt(new THREE.Vector3(0, 0, 0));
}
}
// Mouse move function calling on moving mouse
document.addEventListener('mousemove', onMouseMove, false);
window.addEventListener('mousedown', function () {
gsap.to(camera.position, {
z: 15,
duration: 15
});
})
// Render loop
function render() {
camera.position.z = 5;
requestAnimationFrame(render);
renderer.render(scene, camera);
}
render();
</script>
</body>
</html>
assets:
frame1
and
main-bg
i made frame 1 on the center of the screen by hovering it but cannot make camera to zoom in on it. I tried tween but I think there is something wrong in the basics of my code
Sorry i am new to this and this is my first threejs project
You can apply a mousemove event on the canvas and use event.clientX or event.clientY to read live mouse pointer values. These values can then be used within the the listener to update values of camera.position.z to change zoom.

I cannot type in dat.gui in JSFiddle

I have written code that works perfectly when i run it locally on my system.
This is the link:
https://jsfiddle.net/rand0mus3r/L3j7kz5a/
When you click the mesh, a dat.gui instance appears. However, when i use backspace or try to input something in the textbox, it doesn't work.
It works fine in my system though.
This is the code:
<!DOCTYPE html>
<html>
<head>
<title>Example 01.02 - First Scene</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.101.1/examples/js/controls/OrbitControls.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<!-- Div which will hold the Output -->
<div id="WebGL-output">
</div>
</body>
</html>
window.onload = init();
animate(); //calling function that does all the rendering
//GLOBAL VARS
var scene, camera, renderer;
var cube;
var raycaster, mouse;
var INTERSECTED;
//global flag
var isClicked = false;
//for the camera
var controls;
//creating and rendering the GUI
params = {
yAxis: "0.00001"
}
var gui = new dat.GUI();
gui.add(params, "yAxis").onFinishChange(val => {
cube.scale.y = parseFloat(val);
});
//we make sure to make it hidden
let vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
// once everything is loaded, we run our Three.js stuff.
function init() {
// create a scene, that will hold all our elements such as objects, cameras and lights.
scene = new THREE.Scene();
//SET CAMERA
camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000)
camera.position.z = 5;
// create a render and set the size
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setClearColor("#e5e5e5"); //background color
renderer.setSize(window.innerWidth,window.innerHeight); //size of renderer
//bind rendered to the dom element
document.getElementById("WebGL-output").appendChild(renderer.domElement);
//RAYCASTER
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2(1,1);
// create a cube
var cubeGeometry = new THREE.BoxGeometry(20, 20, 20);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xffff00 }); //0xF7F7F7 = gray
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.scale.y = 0.00001;
cube.userData.originalColor = 0xffff00;
// position the cube
cube.position.x = 0;
cube.position.y = 3;
cube.position.z = 0;
/*
//USEFUL METHODS
cube.rotation.x +=0.5
cube.scale.x +=0.5
*/
// add the cube to the scene
scene.add(cube);
/* RENDERING A PLANE
var geometry = new THREE.PlaneGeometry( 20, 20);
var material = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} );
var plane = new THREE.Mesh( geometry, material );
plane.rotation.set(80,0,0);
scene.add( plane );
//plane.position.x = 2;
*/
//ADDING LIGHTS
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
// position and point the camera to the center of the scene
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
//camera
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.minDistance = 1;
controls.maxDistance = 1000;
// when the mouse moves, call the given function
document.addEventListener('mousemove', onDocumentMouseMove, false);
//when the mouse is clicked, call the given function
document.addEventListener('click', onDocumentMouseClick, false);
}
function onDocumentMouseMove(event)
{
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
event.preventDefault();
// update the mouse variable
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
//TRY THIS
// intersects = raycaster.intersectObject(cube); // to get the cube only
//if the mouse hovers over the cube mesh, change its color to red
//when mouse leaves the mesh, change it back to its original color
//ONLY MAKE THESE MODIFICATION IF THE MESH IS NOT CLICKED
//BECAUSE IF IT IS CLICKED, YOU HAVE TO PAINT THE MESH ACCORDING TO THE onDocumentMouseClick()
if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === false)
{
cube.material.color.set( 0xF7F7F7 );
}
else if (isClicked === false)
{
cube.material.color.set( cube.userData.originalColor );
}
}
// 0xff0000 red
//0xF7F7F7 = gray
function onDocumentMouseClick(event) //if we detect a click event
{
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
event.preventDefault();
// update the mouse variable
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
//if mouse is on top of the mesh when the click occurs, change color of mesh and render GUI
if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === false)
{
isClicked = true;
cube.material.color.set( 0xff0000);
/*
var params = {
textField: "Enter value:"
}
var item = gui.add(params, "textField").onFinishChange(function (value) {
//Do something with the new value
//console.log(value);
cube.scale.y +=value;
});
*/
//when its clicked, change the visibily of the GUI
vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
}
//if mouse is on top of the mesh when the click occurs, but it already marked as 'clicked', now mark it as 'unclicked'
else if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === true)
{
isClicked = false;
cube.material.color.set( cube.userData.originalColor );
//when its clicked, change the visibily of the GUI
vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
// gui.__proto__.constructor.toggleHide()
//dat.GUI.toggleHide();
//gui.toggleHide()
}
}
function render()
{
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
renderer.render(scene, camera); //render the scene
}
function animate()
{
requestAnimationFrame( animate ); //pauses when user switches tab
controls.update();
render();
}
This problem is related to a bug in OrbitControls which was fixed with r110. Upgrading OrbitControls to r110 solves the issue.
window.onload = init();
animate(); //calling function that does all the rendering
//GLOBAL VARS
var scene, camera, renderer;
var cube;
var raycaster, mouse;
var INTERSECTED;
//global flag
var isClicked = false;
//for the camera
var controls;
//creating and rendering the GUI
params = {
yAxis: 0.00001
}
var gui = new dat.GUI();
gui.add(params, "yAxis").onFinishChange(val => {
cube.scale.y = val;
});
//we make sure to make it hidden
let vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
// once everything is loaded, we run our Three.js stuff.
function init() {
// create a scene, that will hold all our elements such as objects, cameras and lights.
scene = new THREE.Scene();
//SET CAMERA
camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000)
camera.position.z = 5;
// create a render and set the size
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setClearColor("#e5e5e5"); //background color
renderer.setSize(window.innerWidth,window.innerHeight); //size of renderer
//bind rendered to the dom element
document.getElementById("WebGL-output").appendChild(renderer.domElement);
//RAYCASTER
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2(1,1);
// create a cube
var cubeGeometry = new THREE.BoxGeometry(20, 20, 20);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xffff00 }); //0xF7F7F7 = gray
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.scale.y = 0.00001;
cube.userData.originalColor = 0xffff00;
// position the cube
cube.position.x = 0;
cube.position.y = 3;
cube.position.z = 0;
/*
//USEFUL METHODS
cube.rotation.x +=0.5
cube.scale.x +=0.5
*/
// add the cube to the scene
scene.add(cube);
/* RENDERING A PLANE
var geometry = new THREE.PlaneGeometry( 20, 20);
var material = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} );
var plane = new THREE.Mesh( geometry, material );
plane.rotation.set(80,0,0);
scene.add( plane );
//plane.position.x = 2;
*/
//ADDING LIGHTS
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
// position and point the camera to the center of the scene
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
//camera
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.minDistance = 1;
controls.maxDistance = 1000;
// when the mouse moves, call the given function
document.addEventListener('mousemove', onDocumentMouseMove, false);
//when the mouse is clicked, call the given function
document.addEventListener('click', onDocumentMouseClick, false);
}
function onDocumentMouseMove(event)
{
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
event.preventDefault();
// update the mouse variable
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
//TRY THIS
// intersects = raycaster.intersectObject(cube); // to get the cube only
//if the mouse hovers over the cube mesh, change its color to red
//when mouse leaves the mesh, change it back to its original color
//ONLY MAKE THESE MODIFICATION IF THE MESH IS NOT CLICKED
//BECAUSE IF IT IS CLICKED, YOU HAVE TO PAINT THE MESH ACCORDING TO THE onDocumentMouseClick()
if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === false)
{
cube.material.color.set( 0xF7F7F7 );
}
else if (isClicked === false)
{
cube.material.color.set( cube.userData.originalColor );
}
}
// 0xff0000 red
//0xF7F7F7 = gray
function onDocumentMouseClick(event) //if we detect a click event
{
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
event.preventDefault();
// update the mouse variable
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
//if mouse is on top of the mesh when the click occurs, change color of mesh and render GUI
if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === false)
{
isClicked = true;
cube.material.color.set( 0xff0000);
/*
var params = {
textField: "Enter value:"
}
var item = gui.add(params, "textField").onFinishChange(function (value) {
//Do something with the new value
//console.log(value);
cube.scale.y +=value;
});
*/
//when its clicked, change the visibily of the GUI
vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
}
//if mouse is on top of the mesh when the click occurs, but it already marked as 'clicked', now mark it as 'unclicked'
else if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === true)
{
isClicked = false;
cube.material.color.set( cube.userData.originalColor );
//when its clicked, change the visibily of the GUI
vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
// gui.__proto__.constructor.toggleHide()
//dat.GUI.toggleHide();
//gui.toggleHide()
}
}
function render()
{
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
renderer.render(scene, camera); //render the scene
}
function animate()
{
requestAnimationFrame( animate ); //pauses when user switches tab
controls.update();
render();
}
body {
margin: 0;
}
canvas {
display: block;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.110/build/three.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.110/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>
<div id="WebGL-output">
</div>
BTW: Please always ensure to use three.js files from the same release. In your demo, three.js is from r110 whereas OrbitControls is from 101.1. Such configurations are not supported and can produce undefined behavior.

Basic Three.js template scene, can´t animate

I'm really new in Three.js and javascript in general.
My question, I'm been trying to create some king of basic frankenstein template ( mainly based on Lee Stemkoski's examples) to use Three.js but as right know i can't make the cube spin infinitely, I have been watching tutorials and other examples but i can't make it work, any ideas why or how to solve it?
And
Any suggestions on how to improve this template scene?
Thanks in advance
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js Template</title>
<meta charset=utf-8>
<link rel="stylesheet" type="text/css" href="css/styles.css">
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,300,700' rel='stylesheet' type='text/css'>
<script src="js/three.js"></script>
<script src="js/Detector.js"></script>
<script src="js/Stats.js"></script>
<script src="js/OrbitControls.js"></script>
<script src="js/OBJLoader.js"></script>
<script src="js/MTLLoader.js"></script>
<script src="js/DDSLoader.js"></script>
<script src="js/THREEx.KeyboardState.js"></script>
<script src="js/THREEx.FullScreen.js"></script>
<script src="js/THREEx.WindowResize.js"></script>
</head>
<body>
<div id="info">
three.js Template Scene<br />
from Base scene
</div>
<div id="threeJSScene"></div>
<script>
// MAIN //
// standard global variables
var container, scene, camera, renderer, controls, stats, animate;
var keyboard = new THREEx.KeyboardState();
var clock = new THREE.Clock();
// initialization
init();
// animation loop / game loop
animate();
// FUNCTIONS //
function init()
{
// SCENE //
scene = new THREE.Scene();
//Add fog to the scene
// scene.fog = new THREE.FogExp2( 0xcccccc, 0.001 );
// CAMERA //
// set the view size in pixels (custom or according to window size)
// var SCREEN_WIDTH = 400, SCREEN_HEIGHT = 300;
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight;
// camera attributes
var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
// set up camera
camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
// add the camera to the scene
scene.add(camera);
// the camera defaults to position (0,0,0)
// so pull it back (z = 400) and up (y = 100) and set the angle towards the scene origin
camera.position.set(0,150,400);
camera.lookAt(scene.position);
// RENDERER //
// create and start the renderer; choose antialias setting.
if ( Detector.webgl )
renderer = new THREE.WebGLRenderer( {alpha:true, antialias:true} );
else
renderer = new THREE.CanvasRenderer();
// Configure renderer size
renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
//Change BG Color
//renderer.setClearColor( 0xAA20AA );
//Configure pixel aspect ratio
renderer.setPixelRatio( window.devicePixelRatio );
//Enable shadows
renderer.shadowMapEnabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// Modify gamma
// renderer.gammaInput = true;
// renderer.gammaOutput = true;
//Attach div element to variable to contain the renderer
container = document.getElementById( 'threeJSScene' );
// alternatively: to create the div at runtime, use:
// container = document.createElement( 'div' );
// document.body.appendChild( container );
// attach renderer to the *container* div
container.appendChild( renderer.domElement );
// EVENTS //
// automatically resize renderer
THREEx.WindowResize(renderer, camera);
// toggle full-screen on given key press
THREEx.FullScreen.bindKey({ charCode : 'm'.charCodeAt(0) });
// CONTROLS //
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // remove when using animation loop
// enable animation loop when using damping or autorotation
//controls.enableDamping = true;
//controls.dampingFactor = 0.25;
controls.enableZoom = true;
//controls.update(); ----------> // required if controls.enableDamping = true, or if controls.autoRotate = true
// STATS //
// displays current and past frames per second attained by scene
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
// LIGHT //
// Add ambient light to Scene - Color(Blue) - Intensity
var Ambientlight = new THREE.AmbientLight (0x506699, 1);
scene.add(Ambientlight);
// Add light to Scene - Color(Red) - Intensity - Distance - decay
var light1 = new THREE.PointLight (0xff0000, 2, 400, 2);
light1.position.set(-60,150,-30);
light1.castShadow = true;
light1.shadowCameraVisible = true;
light1.shadow.mapSize.width = 1024 * 2;
light1.shadow.mapSize.height = 1024 * 2;
light1.shadowDarkness = 0.95;
light1.shadow.camera.near = 20;
light1.shadow.camera.far = 10000;
scene.add(light1);
// spotlight #1 -- yellow, dark shadow
var spotlight = new THREE.SpotLight(0xffff00);
spotlight.position.set(-60,150,-30);
spotlight.shadowCameraVisible = true;
spotlight.shadowDarkness = 0.95;
spotlight.intensity = 2;
// must enable shadow casting ability for the light
spotlight.castShadow = true;
scene.add(spotlight);
// GEOMETRY //
// Create a Cube Mesh //
var geometry = new THREE.BoxGeometry( 50, 50, 50 );
// Create a basic material
var material = new THREE.MeshStandardMaterial( {
color: "#ffffff",
side: THREE.DoubleSide,
//transparent: true,
//opacity: 0.5,
//wireframe: true,
//wireframeLinewidth: 5,
map: new THREE.TextureLoader().load('img/pattern.jpg'),
normalMap: new THREE.TextureLoader().load('img/pattern_NRM.png')
});
//Join the two attribute ( Geometry and material )
var mesh = new THREE.Mesh( geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.position.set(0, 50, 0); // Chance object position
//Add geometry to the scene
scene.add (mesh);
// Create a TorusKnot //
var TorusknotGeometry = new THREE.TorusKnotGeometry( 15, 5, 60, 25 );
var Torusknot = new THREE.Mesh( TorusknotGeometry, material); // We are using the same material created for the cube
Torusknot.castShadow = true;
Torusknot.receiveShadow = true;
Torusknot.position.set (0,100,0);
scene.add (Torusknot);
// Create a cube for the ground //
var groundGeometry = new THREE.BoxGeometry(200,200,10);
var ground = new THREE.Mesh( groundGeometry, material);
ground.castShadow = true;
ground.receiveShadow = true;
ground.position.set (0,0,0);
ground.rotation.x = 1.57;
scene.add (ground);
// Load in the mesh and add it to the scene.
var loader = new THREE.JSONLoader();
loader.load( "models/treehouse_logo.js", function(log){
var materiallogo = new THREE.MeshLambertMaterial({color: 0x55B663});
logo = new THREE.Mesh(log, materiallogo);
logo.scale.set (50,50,50);
logo.position.y = -1;
logo.castShadow = true;
logo.receiveShadow = true;
scene.add(logo);
});
// FLOOR //
// note: 4x4 checkboard pattern scaled so that each square is 25 by 25 pixels.
var floorTexture = new THREE.ImageUtils.loadTexture( 'img/checkerboard.jpg' );
floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.repeat.set( 10, 10 );
// DoubleSide: render texture on both sides of mesh
var floorMaterial = new THREE.MeshBasicMaterial( { map: floorTexture, side: THREE.DoubleSide } );
var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 1, 1);
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.castShadow = true;
floor.receiveShadow = true;
floor.position.y = -0.5;
floor.rotation.x = Math.PI / 2;
scene.add(floor);
// create a set of coordinate axes to help orient user
// specify length in pixels in each direction
var axes = new THREE.AxisHelper(100);
scene.add( axes );
// SKY //
// recommend either a skybox or fog effect (can't use both at the same time)
// without one of these, the scene's background color is determined by webpage background
// make sure the camera's "far" value is large enough so that it will render the skyBox!
var skyBoxGeometry = new THREE.CubeGeometry( 10000, 10000, 10000 );
// BackSide: render faces from inside of the cube, instead of from outside (default).
var skyBoxMaterial = new THREE.MeshBasicMaterial( { color: 0x9999ff, side: THREE.BackSide } );
var skyBox = new THREE.Mesh( skyBoxGeometry, skyBoxMaterial );
// scene.add(skyBox);
}
function update()
{
controls.update();
stats.update();
}
//Animate function
function animate()
{
requestAnimationFrame( animate );
render();
update();
}
// Render the scene - Always goes at the end
function render()
{
renderer.render( scene, camera );
}
</script>
</body>
</html>
To rotate your cube you'll need to add some value to the cube's rotation every frame. The reason this didn't work when you did it before is that the cube is defined in your init function and the render function doesn't have a reference to it.
So your fix requires two things:
Define your cube in a scope that both methods can "see"
Add some value to the rotation of your cube every frame
Inside of the init function you're defining your cube as mesh, so rename this to cube and remove var:
//Join the two attribute ( Geometry and material )
//var mesh = new THREE.Mesh( geometry, material); // Old
cube = new THREE.Mesh( geometry, material); // new
Removing var causes cube to become a global variable defined on the dom window rather than the init function. So your render function can "see" cube. So now all you have to do is rotate it!
function render()
{
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render( scene, camera );
}
I hope that helps!
and if you'd like to learn more about scope give this link a good read, it's helped me quite a bit.
https://toddmotto.com/everything-you-wanted-to-know-about-javascript-scope/
I ctrl + f'd the code, and you don't reference the rotation of the cube once. That would explain why it's not rotating. You can't will it to rotate, you need to actually write something that will change the values of the elements in your scene.
In order to continuously change the rotation of something, you need to reference or increment its rotation in the part of the code that loops.

Object moving in different direction after rotating the plane, three js(v73)

I am have having issue in moving the object, when using mouse. When i move the object initially it works fine, but after rotating the scene about the y-axis, the object starts moving in opposite direction of the mouse. I have created a jsfiddle for that. Here is the code:
//define global variables here
var container, renderer;
var camera, scene, projector,mouseVector,controls;
var mouseX, mouseY, draggable;
var pen,c_mesh,interactiveObj = [];
var cube_selected=false;
var wallWidth=1200;
var wallHeight=400;
var raycaster = new THREE.Raycaster(); // create once
var mouse = new THREE.Vector2(); // create once
init();
animate();
function init(){
container=document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 10, 10000);
//camera.position.set(0,-wallWidth/2+10,wallWidth);
camera.position.set(0,-wallHeight/2+10,wallWidth);
// camera.lookAt(new THREE.Vector3(0,-wallWidth/2 +10,10));
//camera.lookAt(new THREE.Vector3(10,10,10));
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight(0x666666);
scene.add(ambient);
//walls
walls = new THREE.Object3D();
var groundMat = new THREE.MeshPhongMaterial({color:0x808080});
var groundGeo_2 = new THREE.PlaneGeometry(wallWidth, wallWidth); //for roof and floor
var ground = new THREE.Mesh(groundGeo_2, groundMat);
ground.overdraw = true;
ground.position.set(0, -wallHeight, 0);
ground.rotation.x = -Math.PI/2;
walls.add(ground);
var cube_geometry = new THREE.CubeGeometry(500,300,100);
var c_material = new THREE.MeshNormalMaterial();
c_mesh = new THREE.Mesh(cube_geometry, c_material);
c_mesh.overdraw = true;
c_mesh.name = "first_cube";
c_mesh.position.set(0, -wallHeight+100/2-1 ,0); //c_mesh.add(camera);
c_mesh.rotation.x = Math.PI * 0.1;
interactiveObj.push(c_mesh);
walls.add(c_mesh);
scene.add(walls);
//projector = new THREE.Projector();
mouseVector = new THREE.Vector3();
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
container.appendChild( renderer.domElement );
//IE, Chrome, Safari, Opera
document.addEventListener('mousewheel',onDocumentMouseWheel, false);
//Firefox
document.addEventListener('DOMMouseScroll', onDocumentMouseWheel, false);
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('mouseup', onDocumentMouseUp, false);
window.addEventListener( 'resize', onWindowResize, false );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
renderer.render( scene, camera );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseDown(event){
draggable = true;
event.preventDefault();
mouse.x = ( event.clientX / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.height ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( interactiveObj, true );
console.log(intersects);
if ( intersects.length > 0 ) {
if(intersects[0].object == c_mesh) {
renderer.domElement.style.cursor = 'pointer';
console.log("cube selected "); cube_selected=true;
}else{
cube_selected=false;
}
draggable = false;
}
}
function onDocumentMouseUp(event){
draggable = false;
cube_selected=false;
renderer.domElement.style.cursor = 'auto';
}
function onDocumentMouseMove( event ) {
if(draggable){
if(mouseX != 0 && mouseY != 0){
deltaX = event.clientX - mouseX;
deltaY = event.clientY - mouseY;
walls.rotation.y += deltaX * 0.01;
walls.rotation.x += deltaY * 0.01;
}
}else if (cube_selected==true){
if(mouseX != 0 && mouseY != 0){
deltaX = event.clientX - mouseX;
deltaY = event.clientY - mouseY;
c_mesh.position.x += deltaX*1.5 ;
c_mesh.position.y -= deltaY*1.5 ;
}
}
mouseX = event.clientX;
mouseY = event.clientY;
render();
}
function onDocumentMouseWheel( event ) {
mouseDelta = (-event.wheelDeltaY|| event.detail);
camera.position.z += mouseDelta * 1 ;
// console.log("camera position : "+ camera.position.z);
render();
}
https://jsfiddle.net/pn1pL4jb/5/
What i have done:
Added object in plane, rather then to scene because I want the same rotation of plane and object in the same direction.
The problem was that when you rotated the walls/ground with the mouse drag, the cube's rotation was not updated to match. So when you went to drag the cube after a world rotation, the cube is still operating in its old coordinate system. Also, be careful how you add children to parents. The cube should be a child of the scene (or world coordinate system) if you intend to move it all around. If it is a child of the floor or walls, things can get wacky really fast. :)
I updated your JSFiddle as well as cleaned up the code a little bit:
https://jsfiddle.net/aucyekux/
The magic is happening on line 126 with the copying of the wall's rotation into the cube's rotation so it matches:
c_mesh.rotation.copy(walls.rotation);
I won't assume how much 3D math you know, but I'll just say that copying the ground's rotation essentially creates a new frame of reference or local coordinate system for the cube so that when you drag it to the right, and the code says x = x + ... , its new coordinate system has its x direction pointing in the same direction as the ground/walls' new x direction (after the rotation you performed on them).
Hope this makes sense - don't worry if you don't quite get how it all works. I am a veteran 3D game programmer hobbyist for 20 years and I still sometimes forget how this all works and I have struggled with it plenty. I also refresh my understanding by browsing scratchapixel (the 3D math / coordinate system primer section) every now and then. It keeps all this 3D math stuff fresh. Good luck to you! :)
The following is a 2nd answer to this problem. The OP has added orbitControls.js to his app and now all the objects are more manageable. Here is the whole app's .html file, updated to reflect the changes. Now, you can click/drag to rotate the whole room. And then you can click/drag on the chair or the sofa and drag them around the room. Their Y positions are clamped to the ground (0.0) so that they do not incorrectly penetrate the floor.
The sliding of objects along the floor is achieved through raycasting against just the ground (when the mouse has selected them and dragging them). The raycast returns the intersection point in world space, so I just copy that into the draggable objects' position. Then a post-clamp of their Y positions prevent them from falling through the floor.
Even better, is the fact that we don't need localToWorld and worldToLocal matrix 'hacks' anymore because everything is relative to the Scene, which is how it is in real life. :) To prove this, I added an example update the chair's rotation around its own Y axis, so we have a spinning chair that is able to rotate around its own local center of gravity, while also being able to move its position relative to the Scene (world).
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head lang="en">
<meta charset="UTF-8">
<title>Room</title>
</head>
<body>
<script src="http://alexan0308.github.io/threejs/build/three.min.js"></script>
<script src="http://alexan0308.github.io/threejs/examples/js/loaders/OBJLoader.js"></script>
<script src="http://alexan0308.github.io/threejs/examples/js/loaders/MTLLoader.js"></script>
<script src="http://alexan0308.github.io/threejs/examples/js/loaders/OBJMTLLoader.js"></script>
<script src="http://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div id="workspace"></div>
<script>
//define global variables here
var container, renderer;
var camera, scene, projector, mouseVector, controls;
var mouseX, mouseY, draggable;
var pen, c_mesh, interactiveObj = [], rotateObj = [], groundRaycastObj = [];
var wallWidth = 1200;
var wallHeight = 400;
var chair_model, sofa_model;
var chair_selected = false;
var sofa_selected = false;
var raycaster;
var mouse = new THREE.Vector2(), INTERSECTED;
var radius = 100, theta = 0;
var oldIntersectPoint = new THREE.Vector3();
var newIntersectPoint = new THREE.Vector3();
var intersectOffset = new THREE.Vector3();
var chair_rotate = false;
var walls;
var mesh_box;
var wallright, wallleft, wallback, wallfront, ceiling, ground;
var strDownloadMime = "image/octet-stream";
init();
animate();
function init() {
container = document.getElementById('workspace'); //document.createElement('div');
document.body.appendChild(container);
//camera
//camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 10, 10000);
// camera.position.set(0, -wallHeight / 2 + 10, wallWidth);
// camera.lookAt(new THREE.Vector3(10, 10, 10));
//renderer
renderer = new THREE.WebGLRenderer({preserveDrawingBuffer: true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x889988);
renderer.shadowMapEnabled = true;
container.appendChild(renderer.domElement);
scene = new THREE.Scene();
raycaster = new THREE.Raycaster();
var ambient = new THREE.AmbientLight(0xffffff);
scene.add(ambient);
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z= wallWidth;
camera.position.y= wallWidth/2;
controls = new THREE.OrbitControls( camera, renderer.domElement );
//controls.addEventListener( 'change', render ); // add this only if there is no animation loop (requestAnimationFrame)
controls.enableDamping = true;
controls.dampingFactor = 0.25;
//controls.enableZoom = false;
//walls
walls = new THREE.Object3D();
var groundGeo_2 = new THREE.PlaneGeometry(wallWidth, wallWidth); //for roof and floor
var groundGeo = new THREE.PlaneGeometry(wallWidth, wallHeight);
var wallTextureRight = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/rainbow.jpg')
});
wallTextureRight.map.needsUpdate = true;
var wallTextureLeft = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/rainbow.jpg')
});
var wallTextureFront = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/wall4.jpg')
});
var wallTextureBack = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/wall3.png')
});
var floorTexture = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/floor.jpg')
});
floorTexture.map.needsUpdate = true;
var ceilTexture = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('textures/walls/wall4.jpg')
});
ground = new THREE.Mesh(groundGeo_2, floorTexture);
ground.overdraw = true;
ground.position.set(0, 0, 0);
ground.rotation.x = -Math.PI / 2;
walls.add(ground);
console.log(ground);
wallleft = new THREE.Mesh(groundGeo, wallTextureLeft);
wallleft.overdraw = true;
wallleft.position.set(-wallWidth / 2, wallHeight / 2, 0);
wallleft.rotation.y = Math.PI / 2;
walls.add(wallleft);
wallright = new THREE.Mesh(groundGeo, wallTextureRight);
wallright.overdraw = true;
wallright.position.set(wallWidth / 2, wallHeight / 2, 0);
wallright.rotation.y = -Math.PI / 2;
walls.add(wallright);
wallback = new THREE.Mesh(groundGeo, wallTextureBack);
wallback.overdraw = true;
wallback.position.set(0, wallHeight / 2, -wallWidth / 2);
walls.add(wallback);
wallfront = new THREE.Mesh(groundGeo, wallTextureFront);
wallfront.overdraw = true;
wallfront.position.set(0, wallHeight / 2, wallWidth / 2);
wallfront.rotation.y = -Math.PI;
walls.add(wallfront);
ceiling = new THREE.Mesh(groundGeo_2, ceilTexture);
ceiling.position.set(0, wallHeight, 0);
ceiling.rotation.x = Math.PI / 2;
walls.add(ceiling);
scene.add(walls);
groundRaycastObj.push(walls);
//load bed texture
var bed_texture = new THREE.ImageUtils.loadTexture("textures/cb-rochelle-gray_baked.png");
var bedMaterial = new THREE.MeshBasicMaterial({
map: bed_texture,
side: THREE.DoubleSide
});
//load bed
var loader = new THREE.JSONLoader();
loader.load('js/sofa.js', function (geometry) {
sofa_model = new THREE.Mesh(geometry, bedMaterial);
for (var i = 0; i < sofa_model.children.length; i++) {
sofa_model.children[i].material = material;
sofa_model.children[i].userDataParent = sofa_model;
sofa_model.children[i].name = 'sofa_model';
}
sofa_model.position.set(200,0, -200);
sofa_model.rotation.set(0, 0, 0);
sofa_model.scale.set(3, 3, 3);
sofa_model.name = 'sofa_model';
interactiveObj.push(sofa_model);
scene.add(sofa_model);
});
//load chair texture
var chair_texture = new THREE.ImageUtils.loadTexture("textures/chair.png");
var chairMaterial = new THREE.MeshBasicMaterial({
map: chair_texture,
side: THREE.DoubleSide
});
//load chair
var loader = new THREE.JSONLoader();
loader.load('js/chair_model.js', function (geometry) {
chair_model = new THREE.Mesh(geometry, chairMaterial);
for (var i = 0; i < chair_model.children.length; i++) {
chair_model.children[i].material = material;
chair_model.children[i].userDataParent = sofa_model;
chair_model.children[i].name = 'chair_model';
}
chair_model.position.set(-200,0, -200);
chair_model.rotation.set(0, 0, 0);
chair_model.scale.set(3, 3, 3);
chair_model.name = 'chair_model';
interactiveObj.push(chair_model);
scene.add(chair_model);
});
//IE, Chrome, Safari, Opera
document.addEventListener('mousewheel', onDocumentMouseWheel, false);
//Firefox
document.addEventListener('DOMMouseScroll', onDocumentMouseWheel, false);
document.addEventListener('mousemove', onDocumentMouseMove, false);
document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('mouseup', onDocumentMouseUp, false);
window.addEventListener('resize', onWindowResize, false);
}
function animate() {
requestAnimationFrame(animate);
chair_model.rotation.y += 0.02;
controls.update();
// Render the frame
//Don't render twice, it will slow down your animation!
//render();
renderer.render(scene, camera);
}
function render() {
renderer.render(scene, camera);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
//controls.handleResize();
}
function onDocumentMouseDown(event) {
draggable = true;
event.preventDefault();
var testIntersects;
testIntersects = raycaster.intersectObjects(groundRaycastObj, true);
if (testIntersects.length > 0)
oldIntersectPoint.copy(testIntersects[0].point);
// find intersections
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(interactiveObj, true);
if (intersects.length > 0) {
controls.enabled=false;
if (intersects[0].object.name == 'chair_model') {
container.style.cursor = 'pointer';
chair_selected = true;
} else if (intersects[0].object.name == 'sofa_model') {
container.style.cursor = 'pointer';
sofa_selected = true;
}
else {
chair_selected = false;
sofa_selected = false;
}
draggable = false;
}
}
function onDocumentMouseUp(event) {
draggable = false;
chair_selected = false;
sofa_selected = false;
chair_rotate = false;
container.style.cursor = 'auto';
controls.enabled=true;
oldIntersectPoint.set(0,0,0);
newIntersectPoint.set(0,0,0);
intersectOffset.set(0,0,0);
}
function onDocumentMouseMove(event) {
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = -( event.clientY / window.innerHeight ) * 2 + 1;
var deltaX = event.clientX - mouseX;
var deltaY = event.clientY - mouseY;
var testIntersects;
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(interactiveObj, true);
if (intersects.length > 0) {
container.style.cursor = 'pointer';
//addRotationLine(intersects[0].object);
} else {
container.style.cursor = 'auto';
}
if (draggable) {
} else if (chair_selected == true) {
testIntersects = raycaster.intersectObjects(groundRaycastObj, true);
if (testIntersects.length > 0) {
newIntersectPoint.copy(testIntersects[0].point);
intersectOffset.copy(newIntersectPoint);
intersectOffset.sub(oldIntersectPoint);
//uncomment below if you want more precision mouse movements of objects
//intersectOffset.multiplyScalar(0.1);
oldIntersectPoint.copy(newIntersectPoint);
chair_model.position.add(intersectOffset);
}
// clamp chair position to the ground
chair_model.position.y = 0;
} else if (chair_rotate == true) {
rotate_object(chair_model, event);
}
else if (sofa_selected == true) {
testIntersects = raycaster.intersectObjects(groundRaycastObj, true);
if (testIntersects.length > 0) {
newIntersectPoint.copy(testIntersects[0].point);
intersectOffset.copy(newIntersectPoint);
intersectOffset.sub(oldIntersectPoint);
//uncomment below if you want more precision mouse movements of objects
//intersectOffset.multiplyScalar(0.1);
oldIntersectPoint.copy(newIntersectPoint);
sofa_model.position.add(intersectOffset);
}
// clamp sofa position to the ground
sofa_model.position.y = 0;
}
mouseX = event.clientX;
mouseY = event.clientY;
//render(); // no need to render
}
function onDocumentMouseWheel(event) {
// This is automatically handled for you by orbitControls.js,
// but you can't disable zoom on the controls - so don't type controls.enableZoom = false;
//mouseDelta = (-event.wheelDeltaY || event.detail);
//camera.position.z += mouseDelta * 1;
//render(); // no need to render
}
function addRotationLine(objModel) {
var material = new THREE.LineBasicMaterial({
color: 0x0000ff,
linewidth: 6
});
var geometry = new THREE.Geometry();
geometry.vertices.push(
new THREE.Vector3(-10, 500, 0),
new THREE.Vector3(1000, 500, 0)
);
var line = new THREE.Line(geometry, material);
objModel.add(line);
}
function rotate_object(object, event) {
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = -( event.clientY / window.innerHeight ) * 2 + 1;
var deltaX = event.clientX - mouseX;
var deltaY = event.clientY - mouseY;
object.rotation.y += deltaX * 0.02;
object.rotation.y += deltaY * 0.01;
}
</script>
</body>
</html>
Change line 143 of the fiddle to:
c_mesh.position.x += deltaX*1.5 * Math.cos( walls.rotation.y);
When you rotate the scene, the plane the mouse moves on doesn't rotate with it. So you have to act like it does by scaling by the cosine of the rotation.

Three.js mousehover on a 3D element

My problem is the following : The mousehover actions works on the cubes even if the mouse is not directly over the cubes, it works if it is on an Y axis over or under the cubes, out of the scene. Can someone explain me why and how to fix it?
<!DOCTYPE html>
<html lang="fr" xml:lang="fr" xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" type="text/javascript"></script>
<script src="lib/Three.js" type="text/javascript"></script>
<script src="lib/Detector.js"></script>
<script src="lib/stats.min.js" type="text/javascript"></script>
<script src="lib/THREEx/THREEx.WindowResize.js"></script>
<script src="lib/THREEx/THREEx.FUllScreen.js"></script>
<script src="lib/TrackballControls.js"></script>
<style type="text/css">
body
{
color: #ffffff;
text-align:center;
background-color: gray;
margin: 0px;
}
#center
{
color: #fff;
position: absolute;
top: 50px; width: 100%;
padding: 5px;
z-index:100;
}
#center h1
{
font-size:60px;
}
#container
{
position:absolute;
bottom:0;
}
</style>
</head>
<body>
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
<div id="container"></div>
</body>
<script>
// MAIN
console.log("Main.js");
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
// global variables
var container, scene, camera, renderer, stats, controls;
// custom variables
var t = THREE;
var cube;
// to keep track of the mouse position
var projector, INTERSECTED, mouse = { x: 0, y: 0 },
// an array to store our particles in
particles = [];
init();
animate();
function init()
{
// scene
scene = new THREE.Scene();
// camera
var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
scene.add(camera);
camera.position.set(0,0,800);
camera.lookAt(scene.position);
// renderer
renderer = new THREE.CanvasRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
// container
container = document.getElementById('container');
container.appendChild( renderer.domElement );
// stats
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
// events
THREEx.WindowResize(renderer, camera);
THREEx.FullScreen.bindKey({ charCode : 'm'.charCodeAt(0) });
// initialize object to perform world/screen calculations
projector = new THREE.Projector();
// CUSTOM
// Cubes
x = window.innerWidth / 5;
y = window.innerHeight / 10;
console.log(window.innerWidth);
console.log(window.innerHeight);
var geometry = new t.CubeGeometry(125,125,125);
var material = new t.MeshBasicMaterial({color:0xCCCCCC});
cube = new t.Mesh(geometry, material);
cube.name = "cube";
scene.add(cube);
cube.position.set(-x,-y,0);
x = window.innerWidth;
y = window.innerHeight / 10;
var geometry2 = new t.CubeGeometry(125,125,125);
var material2 = new t.MeshBasicMaterial({color:0xCCCCCC});
cube2 = new t.Mesh(geometry2, material2);
scene.add(cube2);
cube2.name = "cube2";
cube2.position.set(0,-y,0);
x = window.innerWidth / 5;
y = window.innerHeight / 10;
var geometry3 = new t.CubeGeometry(125,125,125);
var material3 = new t.MeshBasicMaterial({color:0xCCCCCC});
cube3 = new t.Mesh(geometry3, material3);
cube3.name = "cube3";
scene.add(cube3);
cube3.position.set(x,-y,0);
// particles
makeParticles();
// Mouse events
document.addEventListener( 'mousemove', onMouseMove, false );
document.addEventListener( 'mousedown', onMouseDown, false );
}
// called when the mouse moves
function onMouseMove( event )
{
// store the mouseX and mouseY position
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function onMouseDown( event )
{
event.preventDefault();
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, camera );
var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() );
var intersects = ray.intersectObjects( scene.children );
if ( intersects.length > 0 )
{
console.log(INTERSECTED.name);
if(INTERSECTED.name == "cube")
{
page("real");
}
}
}
function animate()
{
cube.rotation.y +=0.005;
cube.rotation.x -=0.005;
cube2.rotation.y +=0.005;
cube2.rotation.x -=0.005;
cube3.rotation.y +=0.005;
cube3.rotation.x -=0.005;
//textMesh.rotation.y +=0.005;
requestAnimationFrame( animate );
render();
update();
}
// creates a random field of Particle objects
function makeParticles()
{
var particle, material;
// we're gonna move from z position -1000 (far away)
// to 1000 (where the camera is) and add a random particle at every pos.
for ( var zpos= -1000; zpos < 1000; zpos+=20 )
{
// we make a particle material and pass through the
// colour and custom particle render function we defined.
material = new THREE.ParticleCanvasMaterial( { program: particleRender } );
// make the particle
particle = new THREE.Particle(material);
// give it a random x and y position between -500 and 500
particle.position.x = Math.random() * 1000 - 500;
particle.position.y = Math.random() * 1000 - 500;
// set its z position
particle.position.z = zpos;
// scale it up a bit
particle.scale.x = particle.scale.y = 10;
// add it to the scene
scene.add( particle );
// and to the array of particles.
particles.push(particle);
}
}
// there isn't a built in circle particle renderer
// so we have to define our own.
function particleRender( context )
{
// we get passed a reference to the canvas context
context.beginPath();
// and we just have to draw our shape at 0,0 - in this
// case an arc from 0 to 2Pi radians or 360º - a full circle!
context.arc( 0, 0, 0.2, 2, Math.PI * 4, true );
context.fillStyle = "white";
context.fill();
};
// moves all the particles dependent on mouse position
function updateParticles()
{
// iterate through every particle
for(var i=0; i<particles.length; i++)
{
particle = particles[i];
// and move it forward dependent on the mouseY position.
particle.position.z += 250 * 0.02;
// if the particle is too close move it to the back
if(particle.position.z>1000) particle.position.z-=2000;
}
}
function render()
{
renderer.render( scene, camera );
}
function update()
{
updateParticles();
stats.update();
// find intersections
// create a Ray with origin at the mouse position
// and direction into the scene (camera direction)
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, camera );
var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() );
// create an array containing all objects in the scene with which the ray intersects
var intersects = ray.intersectObjects( scene.children );
// INTERSECTED = the object in the scene currently closest to the camera
// and intersected by the Ray projected from the mouse position
// if there is one (or more) intersections
if ( intersects.length > 0 )
{
// if the closest object intersected is not the currently stored intersection object
if ( intersects[ 0 ].object != INTERSECTED)
{
// restore previous intersection object (if it exists) to its original scale
if ( INTERSECTED )
{
INTERSECTED.scale.x = INTERSECTED.currentscale.x;
INTERSECTED.scale.y = INTERSECTED.currentscale.y;
INTERSECTED.scale.z = INTERSECTED.currentscale.z;
}
// store reference to closest object as current intersection object
INTERSECTED = intersects[ 0 ].object;
// store scale of closest object (for later restoration)
scalex = INTERSECTED.scale.x;
scaley = INTERSECTED.scale.y;
scalez = INTERSECTED.scale.z;
INTERSECTED.currentscale = { x : scalex , y : scaley, z : scalez };
// set a new scale for closest object
INTERSECTED.scale.x = INTERSECTED.scale.y = INTERSECTED.scale.z = 1.5;
}
}
else // there are no intersections
{
// restore previous intersection object (if it exists) to its original scale
if ( INTERSECTED )
{
INTERSECTED.scale.x = INTERSECTED.currentscale.x;
INTERSECTED.scale.y = INTERSECTED.currentscale.y;
INTERSECTED.scale.z = INTERSECTED.currentscale.z;
}
// remove previous intersection object reference
// by setting current intersection object to "nothing"
INTERSECTED = null;
}
}
// Pour charger une page dynamiquement
function page(page){
$("body").animate({opacity:0},1000, function(){
$("body").empty();
$("body").load(page +'.html');
$("body").animate({opacity:1},1000, function(){
});
});
}
</script>
</html>
Is there an offset between the window top left corner and the canvas top left corner?
If so, you have to calculate the offset (top and left) and subtract the values from the event.client values.
Try this :
// now get the space between top left browser window corner
var absoluteOffsetLeft = 0;
var absoluteOffsetTop = 0;
var obj = <your HTML object containing the canvas>;
// taken from http://www.quirksmode.org/js/findpos.html
if (obj.offsetParent) {
do {
absoluteOffsetLeft += obj.offsetLeft;
absoluteOffsetTop += obj.offsetTop;
} while (obj = obj.offsetParent);
} else {
console.log("Method offsetParent not supported");
}
// YOUR mouse move event handler - also take the innerWidth and Height from the canvas
function onMouseMove( event ) {
// store the mouseX and mouseY position
mouse.x = ( (event.clientX - absoluteOffsetLeft) / canvas.innerWidth ) * 2 - 1;
mouse.y = - ( (event.clientY - absoluteOffsetTop) / canvas.innerHeight ) * 2 + 1;
}

Resources