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;
}
Related
I am using Threejs for container cargo loading interactive program. Since my cargo loading plan is layer by layer for one container. So I want to divide the browser into multiple scenes. Each scene represents one layer. Every layer has its own arrangement of cargo boxes and operator could make some adjustment. And all cargo boxes are cubic type of shape.
My codes are based on example, webgl_interactive_cubes_ortho.html (r82), and I does not so many cubes in the example so I modify the code example a little bit.
The example codes works but only has one scene with Viewport. Since I want to divide browser vertically, so I begin to divide the height into 2 scenes. Then even I just devided Viewport into 2, see Render method in the script, Then I could not catch object selected in mouse down event.
What I believe is because of mouse click and 3D scene transform calculation. Anyway, in my case, I just want to select the object and drag and drop. Anyone could help me to make the object selected and can make drag and drop?
/1/
//var height = renderer.domElement.height/2; //only want to divide into two layers
/2/
var container, stats;
var camera, scene, scene2, raycaster, renderer;
var cameras =[];
var mouse = new THREE.Vector2(), INTERSECTED,SELECTED;
var radius = 500, theta = 0;
var offset = new THREE.Vector3();
var frustumSize = 1000;
var cubesL0=[], cubesL1=[];
var plane = new THREE.Plane();
var intersection = new THREE.Vector3();
var scenes =[];
var aspect = window.innerWidth / window.innerHeight;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
var info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = 'three.js webgl - interactive cubes';
container.appendChild( info );
camera = new THREE.OrthographicCamera( frustumSize * aspect / - 2, frustumSize * aspect / 2, frustumSize /2 , frustumSize / -2, 1, 1000 );
scene = new THREE.Scene();
scene2 = new THREE.Scene();
var geometry = new THREE.BoxBufferGeometry( 150, 150, 150 );
for ( var i = 0; i < 2; i ++ ) {
var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: 0x000fff } ) );
if(i==0){
object.position.x = - 100;
object.position.y = - 100;
object.position.z = - 100;
}else{
object.position.x = 200;
object.position.y = -359;
object.position.z = -50;
}
scene.add( object );
scenes.push(scene);
cubesL0.push(object);
}
raycaster = new THREE.Raycaster();
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0xf0f0f0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false;
container.appendChild(renderer.domElement);
stats = new Stats();
container.appendChild( stats.dom );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
document.addEventListener( 'mouseup', onDocumentMouseUp, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
var aspect = window.innerWidth / window.innerHeight;
camera.left = - frustumSize * aspect / 2;
camera.right = frustumSize * aspect / 2;
camera.top = frustumSize / 2;
camera.bottom = - frustumSize / 2;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = (- ( event.clientY / window.innerHeight) * 2 + 1);
console.log("mouse x:"+mouse.x+", mouse y:"+mouse.y);
raycaster.setFromCamera( mouse, camera );
if ( SELECTED ) {
if ( raycaster.ray.intersectPlane( plane, intersection ) ) {
SELECTED.position.copy( intersection.sub( offset ) );
}
return;
}
var intersects = raycaster.intersectObjects( cubesL0 );
if ( intersects.length > 0 ) {
console.log(" there is object going to move");
if ( INTERSECTED != intersects[ 0 ].object ) {
if ( INTERSECTED ) INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
INTERSECTED = intersects[ 0 ].object;
INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
plane.setFromNormalAndCoplanarPoint(
camera.getWorldDirection( plane.normal ),
INTERSECTED.position );
}
container.style.cursor = 'pointer';
} else {
if ( INTERSECTED ) INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
INTERSECTED = null;
container.style.cursor = 'auto';
}
}
function onDocumentMouseDown( event ) {
event.preventDefault();
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( cubesL0 );
var getTarget = true;
if ( intersects.length > 0 ) {
SELECTED = intersects[ 0 ].object;
if ( raycaster.ray.intersectPlane( plane, intersection ) ) {
offset.copy( intersection ).sub( SELECTED.position );
}
container.style.cursor = 'move';
}
}
function onDocumentMouseUp( event ) {
event.preventDefault();
console.log(" in mouse up event ");
if ( INTERSECTED ) {
SELECTED = null;
}
container.style.cursor = 'auto';
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
var width = renderer.domElement.width;
var height = renderer.domElement.height;
//var height = renderer.domElement.height/2; //only want to divide into two layers
var left = 0;
var bottom = 0;
renderer.setViewport( left, bottom, width, height );
renderer.setScissor( left, bottom, width, height );
renderer.render( scene, camera );
}
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - interactive cubes</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>
<script src="http://threejs.org/build/three.min.js"></script>
<script src="http://threejs.org/examples/js/libs/stats.min.js"></script>
</body>
</html>
Three.js offers a special renderer, examples/js/renderers/CSS2DRenderer, that allows html overlays on a standard WebGL-rendered scene (see the official demo, here.)
The CSS2DRenderer accomplishes the positioning of the html item with CSS transforms. Here is how the renderer relates world space to screen space:
vector.setFromMatrixPosition( object.matrixWorld );
vector.applyProjection( viewProjectionMatrix );
var element = object.element;
var style = 'translate(-50%,-50%) translate(' + ( vector.x * _widthHalf + _widthHalf ) + 'px,' + ( - vector.y * _heightHalf + _heightHalf ) + 'px)';
element.style.WebkitTransform = style;
element.style.MozTransform = style;
element.style.oTransform = style;
element.style.transform = style;
In the live snippet, below, I have positioned several text elements, alongside a grid, like axis labels in a data plot. My problem is to choose a position in three.js world space for the html labels that accounts for their pixel width. I have framed each label with a plane to show the gap to the edge of the grid – I need to eliminate that gap!
var renderer, labelRenderer, scene, camera, controls, sprite, stats, rot, planes, ctx, fontFamily, fontSize;
rot = 0; // this drives load(?)
init();
//animate();
render();
function init() {
fontFamily = "monospace";
fontSize = "10px";
stats = new Stats();
stats.showPanel(1);
document.body.appendChild(stats.dom);
var canvas = document.createElement('canvas')
ctx = canvas.getContext('2d')
ctx.font = fontSize + " " + fontFamily;
// renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
labelRenderer = new THREE.CSS2DRenderer();
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0';
labelRenderer.domElement.style.pointerEvents = 'none';
document.body.appendChild(labelRenderer.domElement);
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(20, 20, 20);
// controls
controls = new THREE.OrbitControls(camera);
// ambient
scene.add(new THREE.AmbientLight(0x222222));
// light
var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(20, 20, 0);
scene.add(light);
// axes
scene.add(new THREE.AxisHelper(20));
var size = 5;
var step = 5;
var gridHelper = new THREE.GridHelper(size, step);
gridHelper.translateX(5);
gridHelper.translateZ(5);
scene.add(gridHelper);
var geometry, material, text, label;
planes = new Array(5);
var texts = ["one", "two", "three", "four", "five"];
for (var i = 0; i < 5; i++) {
geometry = new THREE.PlaneGeometry(2, 2);
material = new THREE.MeshBasicMaterial({
transparent: true,
opacity: 0
});
planes[i] = new THREE.Mesh(geometry, material);
planes[i].position.set(10 + 1, 1, i * 2 + 1)
planes[i].lookAt(camera.position)
scene.add(planes[i]);
scene.add(new THREE.EdgesHelper(planes[i]))
text = document.createElement('div');
text.className = 'label';
text.style.color = "white";
text.style["font-family"] = fontFamily;
text.style["font-size"] = fontSize;
text.textContent = texts[i];
var textWidth = ctx.measureText(texts[i]).width;
console.log("textWidth", textWidth);
label = new THREE.CSS2DObject(text);
label.position.copy(planes[i].position);
scene.add(label);
console.log("label", label);
}
}
function randomPos(scale) {
return scale * Math.random();
}
function render() {
renderer.render(scene, camera);
labelRenderer.render(scene, camera);
var x = camera.position.x;
var z = camera.position.z;
camera.position.x = x * Math.cos(rot) + z * Math.sin(rot);
camera.position.z = z * Math.cos(rot) - x * Math.sin(rot);
camera.lookAt(scene.position);
requestAnimationFrame(render);
planes.forEach(function(plane) {
plane.lookAt(camera.position);
});
stats.update();
}
function animate() {
requestAnimationFrame(animate);
//controls.update();
renderer.render(scene, camera);
stats.update();
}
body {
background-color: #000;
margin: 0px;
overflow: hidden;
}
<script src="https://rawgit.com/mrdoob/three.js/dev/build/three.min.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/controls/OrbitControls.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/renderers/CSS2DRenderer.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/libs/stats.min.js"></script>
Pixel Width isn't your issue here. It's origin. You're offsetting the position (10 + 1, i, i*2+1)... https://jsfiddle.net/7j4jypfL/1/
label = new THREE.CSS2DObject(text);
label.position.set(10+(0.5),0, (i * 2)+0.5);
scene.add(label);
You need to keep in mind that in OpenGL, things like billboards and the like are origin centered (i.e. -1,-1 to 1,1) and therefore (0,0) would be center.
YOU need you measure at scale 1 and when you zoom and the like it will line up correctly because of perspective math. Look at my fiddle where I change your positions to just pure (10, i, i*2) and look at how the text lines up. If you want to move it down from there, (10 + (0.5),0,(i*2)+0.5)
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.
I have a globe in threeJS that can be rotated in any direction. As it is currently, I can read the correct longitude and latitude located at the center of the globe regardless of rotation. The globe is orientated so that the raycaster.ray.origin.x and raycaster.ray.origin.y read 0,0 where the prime meridian and the equator cross. The raycaster.ray.origin.z reads 500.
Where I am trying to get to, however, is determining the latitude and longitude based on where the mouse is clicked on the globe. I was trying to calculate it off of the raycaster.ray.direction.x , y , z, but I am getting readings whether I am on the object or not.
If I read the direction from origin 0,0, my raycaster.ray.direction.x and y will both read 0 and the z will read -100 (I multiplied these by one hundred to work with better numbers). On the circumference the X will read 0 on the top and bottom of the globe and -40 to 40 left to right. The Y will read 40 to - 40 top to bottom and 0 left to right and the Z direction will be -90 around the entire circumference. However the numbers will continue increasing/ decreasing as all the way to the edge of the screen.
Same is true when the globe is orientated to the north pole/south pole, however, the y at the center is -100/100 and -90/90 on the circumference and the x would be - 100/100 quarter way around the globe and -90/90 around the circumference.
Is a way to get the value of where I am clicking that is just on the object and not the entire screen? I can't wrap my head how to correlate the direction to the origin. I feel like I am getting somewhere, but then just leave confused. Here is everything I got. Any help would be hugely appreciated.
<!DOCTYPE html>
<html lang="en">
<head>
<title>latAndLong</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 {
color: #808080;
font-family:Monospace;
font-size:13px;
text-align:center;
background-color: #ffffff;
margin: 0px;
overflow: hidden;
}
#info {
position: absolute;
top: 0px; width: 100%;
padding: 5px;
}
a {
color: #0080ff;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="info">three.js - earth demo</div>
<script src="javascript/mrdoob-three.js-ad419d4/build/three.min.js"></script>
<script src="javascript/TrackballControls.js"></script>
<script>
var container, stats;
var camera, scene, renderer;
var group;
var mouseX = 0, mouseY = 0;
var mesh;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var earthgeometry;
var earthmaterial;
var earthloader;
var vector;
var raycaster;
projector = new THREE.Projector();
var text, plane;
init();
animate();
function init() {
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 500;
scene = new THREE.Scene();
group = new THREE.Object3D();
scene.add( group );
// earth
earthloader = new THREE.TextureLoader();
earthloader.load( 'images/oMap.png', function ( texture ) {
earthgeometry = new THREE.SphereGeometry( 200, 50, 50 );
earthmaterial = new THREE.MeshBasicMaterial( { map: texture, overdraw: true } );
mesh = new THREE.Mesh( earthgeometry, earthmaterial );
group.add( mesh );
} );
//init renderer
renderer = new THREE.CanvasRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
// trackball mouse controls
// Control Camera with Mouse
controls = new THREE.TrackballControls( camera, container );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = true;
controls.noPan = true;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( 'change', render );
}
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 animate() {
requestAnimationFrame( animate );
// Update the Camera Controls
controls.update();
render();
}
function render() {
/*camera.position.x += ( mouseX -camera.position.x) * 0.05;
camera.position.y += ( - mouseY - camera.position.y ) * 0.05;
camera.lookAt( scene.position );*/
renderer.render( scene, camera );
}
container.onmouseup = function(event) {
vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5);
projector.unprojectVector( vector, camera );
raycaster = new THREE.Raycaster(camera.position, vector.sub( camera.position ).normalize());
// var intersects = raycaster.intersectObjects(earthgeometry, true);
// not working
// console.log(intersects[0].position.x + ' intersect x');
// not working
// console.log(intersects[0].postion.y + ' intersect y');
// console.log(vector.x + 'vector x');
// console.log(raycaster.ray.origin.x + ' origin x');
// console.log(vector.y + 'vector y');
// console.log(raycaster.ray.origin.y + ' origin y');
// console.log(vector.z + 'vector z');
// console.log(raycaster.ray.origin.z + ' origin z');
// console.log(vector);
// console.log(raycaster);
// console.log(earthgeometry);
// console.log(earthmaterial);
// console.log(earthloader);
// console.log(raycaster.ray.origin.z + 'ray origin z');
// console.log(group);
// console.log(scene);
//Gets latitude and longitude for the center of globe facing you
var long;
if(raycaster.ray.origin.x <= 0 && raycaster.ray.origin.z >=0 ) {
long = raycaster.ray.origin.x * -.18;
console.log('West');
console.log(long);
console.log(raycaster.ray.direction.x * 100 + 'ray x');
} else if (raycaster.ray.origin.x <= 0 && raycaster.ray.origin.z <=0 ) {
long = (raycaster.ray.origin.x + 500) * .18 + 90;
console.log('West');
console.log(long);
console.log(raycaster.ray.direction.x * 100 + 'ray x');
} else if (raycaster.ray.origin.x >= 0 && raycaster.ray.origin.z >=0 ) {
long = raycaster.ray.origin.x * .18;
console.log('East');
console.log(long);
console.log(raycaster.ray.direction.x * 100 + 'ray x');
} else {
long = (raycaster.ray.origin.x - 500) * -.18 + 90;
console.log('East');
console.log(long);
console.log(raycaster.ray.direction.x * 100 + 'ray x');
}
var lat;
if (raycaster.ray.origin.y >= 0) {
lat = raycaster.ray.origin.y * .18;
console.log('North');
console.log(lat);
console.log(raycaster.ray.direction.y * 100 + 'ray y');
console.log(raycaster.ray.direction.z * 100 + 'ray z');
} else {
lat = raycaster.ray.origin.y * -.18;
console.log('South');
console.log(lat);
console.log(raycaster.ray.direction.y * 100 + 'ray y');
console.log(raycaster.ray.direction.z * 100 + 'ray z');
}
}
</script>
</body>
</html>
I'm trying to set up a website that runs in a single WebGL element. I'm trying to get it to tell when the mouse is over an object. I'm using a couple of planes with a with transparent textures on them as links, but it won't pick up on any intersections at all. It does absolutely nothing. I've even added a cube and used the same code as the interactive cubes example (http://mrdoob.github.com/three.js/examples/webgl_interactive_cubes.html) and it does absolutely nothing. No changing the color, absolutely nothing.
I've gone through and tried about 8 different examples online having to do with this and not a single one has worked for me.
Here is my code:
<meta charset="utf-8">
<style type="text/css">
body {
margin: 0px;
overflow: hidden;
background: #000000;
}
div {
margin: 0px;
}
</style>
</head>
<div>
<script src="http://www.html5canvastutorials.com/libraries/Three.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
//Variable Declarations
var camera, scene, renderer, aspect, projector;
var INTERSECTED;
var mouseX = 0, mouseY = 0;
init();
//Method Declarations
function init() {
aspect = window.innerWidth / window.innerHeight;
//aspect = 1.25;
//Sets the camera variable to a new Perspective Camera with a field of view of 45 degrees, an aspect ratio
//of the window width divided by the window height, the near culling distance of 1 and the far culling distance of 2000
camera = new THREE.PerspectiveCamera(45, aspect, 1, 2000);
//Sets the height of the camera to 400
camera.position.z = 700;
//Sets a variable "lookat" to a new 3d vector with a position of 0, 0, 0,
//or the center of the scene/center of the menu plane
var lookat = new THREE.Vector3(0, 0, 0);
//Uses a function built in to every camera object to make it look at a given coordinate
camera.lookAt(lookat);
//Makes a new scene object to add everything to
scene = new THREE.Scene();
//Adds the camera object to the scene
scene.add(camera);
//Sets the renderer varialbe to a new WebGL renderer object
//Change "WebGLRenderer" to "CanvasRenderer" to use canvas renderer instead
renderer = new THREE.WebGLRenderer({antialias:true});
//renderer.sortObjects = false;
//Sets the size of the renderer object to the size of the browser's window
//renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setSize(window.innerWidth, window.innerHeight);
//Adds an event listener to the webpage that "listens" for mouse movements and when the mouse does move, it runs the
//"onMouseMove" function
document.addEventListener('mousemove', onMouseMove, false);
//Force canvas to stay proportional to window size
window.addEventListener('resize', onWindowResize, false);
document.addEventListener('mousedown', onDocumentMouseDown, false);
//Sets the update frequency of the webpage in milliseconds
setInterval(update, 1000/60);
//Makes a variable "texture" and sets it to the returned value of the method "loadTexture"
//supplied by the THREE.js library
var texture = THREE.ImageUtils.loadTexture('imgs/backgrounds.png');
//Makes a variable "geometry" and sets it to a "PlaneGeometry" object with a width of 645 and a height of 300
var geometry = new THREE.PlaneGeometry(645, 300);
//Makes a variable "material" and sets it to a "MeshBasicMaterial" object with it's map set to
//the "texture" object, it also makes it transparent
var material = new THREE.MeshBasicMaterial({map: texture, transparent: true});
//Makes a variable "plane" and sets it to a "Mesh" object with its geometry set to the "geometry" object
//and it's material set to the "material" object
var plane = new THREE.Mesh(geometry, material);
//Background Texture
var backgroundTexture = THREE.ImageUtils.loadTexture('imgs/gears.png');
var backgroundGeo = new THREE.PlaneGeometry(3500, 2500);
var backgroundMat = new THREE.MeshBasicMaterial({map: backgroundTexture});
var backgroundPlane = new THREE.Mesh(backgroundGeo, backgroundMat);
backgroundPlane.position.z = -1000;
backgroundPlane.overdraw = true;
//Home Button
//Makes a "homeTexture" variable and sets it to the returned value of the makeTextTexture function when
//the text passed in is set to "Home", the width is set to 300, height of 150, font set to 80pt Arial,
//fillStyle set to white, textAlign set to center, textBaseLine set to middle, and the color of the
//background set to red = 0, green = 0, blue = 0 and alpha = 0
var homeTexture = makeTextTexture("Home", 300, 150, '80pt Arial', 'white', "center", "middle", "rgba(0,0,0,0)");
var homeGeom = new THREE.PlaneGeometry(50, 25);
var homeMaterial = new THREE.MeshBasicMaterial({map: homeTexture, transparent: true});
var homeTest = new THREE.Mesh(homeGeom, homeMaterial);
homeTest.position.x -= 270;
homeTest.position.y += 120;
homeTest.position.z = 40;
homeTest.castShadow = true;
//Gallery Button
var galleryTexture = makeTextTexture("Gallery", 340, 150, '80pt Arial', 'white', "center", "middle", "rgba(0,0,0,0)");
var galleryGeom = new THREE.PlaneGeometry(50, 25);
var galleryMaterial = new THREE.MeshBasicMaterial({map: galleryTexture, transparent: true});
var galleryTest = new THREE.Mesh(galleryGeom, galleryMaterial);
galleryTest.position.x -= 270;
galleryTest.position.y += 90;
galleryTest.position.z = 40;
galleryTest.castShadow = true;
//The Team Button
var theTeamTexture = makeTextTexture("Company", 510, 150, '80pt Arial', 'white', "center", "middle", "rgba(0,0,0,0)");
var theTeamGeom = new THREE.PlaneGeometry(80, 25);
var theTeamMaterial = new THREE.MeshBasicMaterial({map: theTeamTexture, transparent: true});
var theTeamTest = new THREE.Mesh(theTeamGeom, theTeamMaterial);
theTeamTest.position.x -= 260;
theTeamTest.position.y += 60;
theTeamTest.position.z = 40;
theTeamTest.castShadow = true;
projector = new THREE.Projector();
var cubeGeom = new THREE.CubeGeometry(20, 20, 20);
var cubeMat = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
var cubeMesh = new THREE.Mesh(cubeGeom, cubeMat);
cubeMesh.position.z = 15;
//scene.add(cubeMesh);
//Adds all of the previously created objects to the scene
scene.add(plane);
scene.add(backgroundPlane);
scene.add(homeTest);
scene.add(theTeamTest);
scene.add(galleryTest);
//Adds the renderer to the webpage
document.body.appendChild(renderer.domElement);
}
function update() {
camera.position.x = (mouseX - (window.innerWidth / 2)) * 0.1;
camera.position.y = -((mouseY - (window.innerHeight / 2)) * 0.15);
camera.lookAt(new THREE.Vector3(0, 0, 0));
render();
}
function render() {
renderer.render(scene, camera);
}
function onMouseMove(event) {
mouseX = event.clientX;
mouseY = event.clientY;
}
function onDocumentMouseDown(event) {
event.preventDefault();
var vector = new THREE.Vector3(
( event.clientX / window.innerWidth ) * 2 - 1,
- ( event.clientY / window.innerHeight ) * 2 + 1,
0.5
);
projector.unprojectVector( vector, camera );
var ray = new THREE.Ray( camera.position,
vector.subSelf( camera.position ).normalize() );
var intersects = ray.intersectObjects( objects );
if ( intersects.length > 0 ) {
intersects[ 0 ].object.materials[ 0 ].color.setHex( Math.random() * 0xffffff );
}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function makeTextTexture(text, width, height, font, fillStyle, textAlign, textBaseline, backgroundColor)
{
//Makes a new canvas element
var bitmap = document.createElement('canvas');
//Gets its 2d css element
var g = bitmap.getContext('2d');
//Sets it's width and height
bitmap.width = width;
bitmap.height = height;
//Takes "g", it's 2d css context and set's all of the following
g.font = font;
g.fillStyle = backgroundColor;
g.fillRect(0, 0, width, height);
g.textAlign = "center";
g.textBaseline = "middle";
g.fillStyle = fillStyle;
g.fillText(text, width / 2, height / 2);
//Rendered the contents of the canvas to a texture and then returns it
var texture = new THREE.Texture(bitmap);
texture.needsUpdate = true;
return texture;
}
</script>
</div>
</html>
Thanks to anyone that can help out. I wish I could figure out what's going on myself, it seems like I'm using StackOverflow to answer my questions way too much.
You have not declared the variable objects. Do this:
var objects = [];
Then populate it:
objects.push( plane );
objects.push( backgroundPlane );
Now, this line will work:
var intersects = ray.intersectObjects( objects );