How to stop animation at a particular keyframe - three.js

I am very new to three.js and blender, I am trying to have a 3D image of a robot arm that rotates from 0 to 180 and vice versa,I have used blender2.8.1 for animation and have exported the glb file and called it in an html file with three.js module, till here things are fine but now I want to stop it at some degree let's say if I give in the value 30 then the robot arm should move by 30, and if the previous state was at 60 then it should add 30 and move to 90. The referred example is https://threejs.org/examples/#webgl_animation_skinning_morph
Can someone please help?
Thank you in advance.
Here is the code :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
<style>
body {
color: #222;
}
a {
color: #2fa1d6;
}
p {
max-width: 600px;
margin-left: auto;
margin-right: auto;
padding: 0 2em;
}
</style>
</head>
<body>
<script type="module">
import * as THREE from '../build/three.module.js';
import Stats from '../examples/jsm/libs/stats.module.js';
import { GUI } from '../examples/jsm/libs/dat.gui.module.js';
import { GLTFLoader } from '../examples/jsm/loaders/GLTFLoader.js';
var container, stats, clock, gui, mixer, actions, activeAction, previousAction;
var camera, scene, renderer, model, face;
var api = { state: 'middle' };
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 120, window.innerWidth / window.innerHeight, 0.25, 100 );
camera.position.set( 50, 30, -70 );
camera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xe0e0e0 );
scene.fog = new THREE.Fog( 0xe0e0e0, 20, 100 );
clock = new THREE.Clock();
// lights
var light = new THREE.HemisphereLight( 0xffffff, 0x444444 );
light.position.set( 0, 20, 0 );
scene.add( light );
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 0, 20, 10 );
scene.add( light );
// ground
var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2000, 2000 ), new THREE.MeshPhongMaterial( { color: 0x999999, depthWrite: false } ) );
mesh.rotation.x = - Math.PI / 2;
scene.add( mesh );
var grid = new THREE.GridHelper( 200, 40, 0x000000, 0x000000 );
grid.material.opacity = 0.2;
grid.material.transparent = true;
scene.add( grid );
// model
var loader = new GLTFLoader();
loader.load( 'robo.glb', function ( gltf ) {
model = gltf.scene;
scene.add( model );
createGUI( model, gltf.animations );
}, undefined, function ( e ) {
console.error( e );
} );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.gammaOutput = true;
renderer.gammaFactor = 2.2;
container.appendChild( renderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
// stats
stats = new Stats();
//container.appendChild( stats.dom );
}
function createGUI( model, animations ) {
var states = [ 'negative', 'positive'];
gui = new GUI();
mixer = new THREE.AnimationMixer( model );
actions = {};
for ( var i = 0; i < animations.length; i ++ ) {
var clip = animations[ i ];
var action = mixer.clipAction( clip );
actions[ clip.name ] = action;
if ( states.indexOf( clip.name ) >= 4 ) {
action.clampWhenFinished = true;
action.loop = THREE.LoopOnce;
}
}
// states
var statesFolder = gui.addFolder( 'States' );
var clipCtrl = statesFolder.add( api, 'state' ).options( states );
clipCtrl.onChange( function () {
fadeToAction( api.state, 1 );
} );
statesFolder.close();
// emotes
//var emoteFolder = gui.addFolder( 'Emotes' );
}
function restoreState() {
mixer.removeEventListener( 'finished', restoreState );
fadeToAction( api.state, 0 );
}
face = model.getObjectByName( 'Head_2' );
var expressions = Object.keys( face.morphTargetDictionary );
var expressionFolder = gui.addFolder( 'Expressions' );
for ( var i = 0; i < expressions.length; i ++ ) {
expressionFolder.add( face.morphTargetInfluences, i, 0, 1, 0.01 ).name( expressions[ i ] );
}
activeAction = actions[ 'middle' ];
activeAction.play();
//expressionFolder.open();
function fadeToAction( name, duration ) {
previousAction = activeAction;
activeAction = actions[ name ];
if ( previousAction !== activeAction ) {
previousAction.fadeOut( duration );
}
activeAction
.reset()
.fadeIn( duration )
.setDuration(10)
.play();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
function animate() {
var dt = clock.getDelta();
if ( mixer ) mixer.update( dt );
requestAnimationFrame( animate );
renderer.render( scene, camera );
stats.update();
}
</script>
</body>
</html>

Related

Inital Y Axi Rotation after applying THREE.DeviceOrientationControls on Camera Object

my scene has a simple Cube on position: {x: 0, y:0, z: -20} and the camera has the inital position {x: 0, y:0, z: 0} and rotation: {x: 0, y:0, z: 0}. at this point my cube is visible in “north” direction. now when i apply THREE.DeviceOrientationControls on my camera object the camera rotates on the Y Axi by 90 degrees. Now iam able to rotate the camera by rotating the mobile device but the origin has changed (“north” is now “west”). i would to keep my inital direction after the THREE.DeviceOrientationControls apply. how could i reach that? I noticed there is a a variable in the DeviceOrientationControls.js:
this.alphaOffset = 0; // radians
but changes to this variable seems not work properly.
UPDATE:
i have attached the source code.
This is the Scene before the DeviceOrientationControls is applied
This is the Scene after DeviceOrientationControls is applied
document.addEventListener("DOMContentLoaded", function() {
var camera, scene, renderer, controls,
northMesh,southMesh,westMesh,eastMesh,
northMeshDiv,southMeshDiv,westMeshDiv,eastMeshDiv,
northMeshLabel,southMeshLabel,westMeshLabel,eastMeshLabel;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000 );
setTimeout(function(){
controls = new THREE.DeviceOrientationControls( camera );
}, 3000);
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0x000000, 0 ); // the default
document.body.appendChild( renderer.domElement );
northMesh = new THREE.Mesh( new THREE.BoxGeometry( 2, 2, 2 ), new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe: true } ) );
northMesh.position.x = 0;
northMesh.position.y = 0;
northMesh.position.z = -20;
scene.add( northMesh );
northMeshDiv = document.createElement( 'div' );
northMeshDiv.className = 'label';
northMeshDiv.style.marginTop = '-1em';
northMeshDiv.style.color = '#ff0000';
northMeshDiv.textContent = 'NORTH';
northMeshLabel = new THREE.CSS2DObject( northMeshDiv );
northMeshLabel.position.set( 0, 1, 0 );
northMesh.add( northMeshLabel );
eastMesh = new THREE.Mesh( new THREE.BoxGeometry( 2, 2, 2 ), new THREE.MeshBasicMaterial( { color: 0x00ff00, wireframe: true } ) );
eastMesh.position.x = 20;
eastMesh.position.y = 0;
eastMesh.position.z = 0;
scene.add( eastMesh );
eastMeshDiv = document.createElement( 'div' );
eastMeshDiv.className = 'label';
eastMeshDiv.style.marginTop = '-1em';
eastMeshDiv.style.color = '#00ff00';
eastMeshDiv.textContent = 'EAST';
eastMeshLabel = new THREE.CSS2DObject( eastMeshDiv );
eastMeshLabel.position.set( 0, 1, 0 );
eastMesh.add( eastMeshLabel );
southMesh = new THREE.Mesh( new THREE.BoxGeometry( 2, 2, 2 ), new THREE.MeshBasicMaterial( { color: 0xff00ff, wireframe: true } ) );
southMesh.position.x = 0;
southMesh.position.y = 0;
southMesh.position.z = 20;
scene.add( southMesh );
southMeshDiv = document.createElement( 'div' );
southMeshDiv.className = 'label';
southMeshDiv.style.marginTop = '-1em';
southMeshDiv.style.color = '#ff00ff';
southMeshDiv.textContent = 'SOUTH';
southMeshLabel = new THREE.CSS2DObject( southMeshDiv );
southMeshLabel.position.set( 0, 1, 0 );
southMesh.add( southMeshLabel );
westMesh = new THREE.Mesh( new THREE.BoxGeometry( 2, 2, 2 ), new THREE.MeshBasicMaterial( { color: 0x0000ff, wireframe: true } ) );
westMesh.position.x = -20;
westMesh.position.y = 0;
westMesh.position.z = 0;
scene.add( westMesh );
westMeshDiv = document.createElement( 'div' );
westMeshDiv.className = 'label';
westMeshDiv.style.marginTop = '-1em';
westMeshDiv.style.color = '#0000ff';
westMeshDiv.textContent = 'WEST';
westMeshLabel = new THREE.CSS2DObject( westMeshDiv );
westMeshLabel.position.set( 0, 1, 0 );
westMesh.add( westMeshLabel );
labelRenderer = new THREE.CSS2DRenderer();
labelRenderer.setSize( window.innerWidth, window.innerHeight );
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = 0;
document.body.appendChild( labelRenderer.domElement );
//FPS
var container = document.getElementById( 'stats' );
stats = new Stats();
container.appendChild( stats.dom );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
if(controls) controls.update();
stats.update();
render();
}
function render() {
renderer.render( scene, camera );
labelRenderer.render( scene, camera );
}
});
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/index.css">
<title></title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>
<body>
<script type="text/javascript" src="js/three.min.js"></script>
<script type="text/javascript" src="js/CSS2DRenderer.js"></script>
<script type="text/javascript" src="js/WebGL.js"></script>
<script type="text/javascript" src="js/stats.min.js"></script>
<script type="text/javascript" src="js/DeviceOrientationControls.js"></script>
<script type="text/javascript" src="js/index.js"></script>
<div id="stats"></div>
</body>
</html>

Javascript threejs 3D draw solid cubic with border

I follow threejs example, webgl_interactive_draggablecubes.html. My project is to use Threejs to make a container loading plan. So I want to make solid cubic with a border. something we could see with/without border difference.
,
I could use multi-material, but then my drag and drop is broken. The code snippet in creating Geometry3 is commented.
My question is: how to make solid cubic with border and at the same time could be drag and drop?
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - draggable 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="js/three.js"></script>
<script src="js/TrackballControls.js"></script>
<script src="js/stats.min.js"></script>
<script>
var container, stats;
var camera, controls, scene, renderer;
var cubes = [];
var plane = new THREE.Plane();
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2(),
offset = new THREE.Vector3(),
intersection = new THREE.Vector3(),
INTERSECTED, SELECTED;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 10;
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
scene = new THREE.Scene();
scene.add( new THREE.AmbientLight( 0x505050 ) );
var geometry = new THREE.BoxGeometry( 2, 5, 7);
var hex = 0xff0000;
for ( var i = 0; i < geometry.faces.length; i++ ) {
geometry.faces[ i ].color.setHex( hex );
}
var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, overdraw: 0.5,wireframe:true } );
var cube = new THREE.Mesh( geometry, material );
cubes.push( cube );
var geometry2 = new THREE.BoxGeometry(2,4, 5);
var hex2 = 0x009fff;
for ( var i = 0; i < geometry2.faces.length; i++ ) {
geometry2.faces[ i ].color.setHex( hex2 );
}
var material2 = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, overdraw: 0.5,wireframe:true } );
var cube2 = new THREE.Mesh( geometry2, material2 );
cubes.push( cube2 );
var geometry3 = new THREE.BoxGeometry(1,3,4);
var hex3 = 0x0f0ff0;
for ( var i = 0; i < geometry3.faces.length; i++ ) {
geometry3.faces[ i ].color.setHex( hex3 );
}
/* var darkMaterial3= new THREE.MeshBasicMaterial( { color: 0xffffcc } );
var wireframeMaterial3= new THREE.MeshBasicMaterial( { color: 0x0f0000, wireframe: true, transparent: false } );
var multiMaterial3= [ darkMaterial3, wireframeMaterial3 ];
var cube3 = THREE.SceneUtils.createMultiMaterialObject(geometry3,multiMaterial3);*/
var material3 = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, overdraw: 0.5,wireframe:true } );
var cube3 = new THREE.Mesh( geometry3, material3 );
cubes.push( cube3 );
scene.add(cube);
scene.add(cube2);
scene.add(cube3);
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setClearColor( 0xf0f0f0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFShadowMap;
container.appendChild( renderer.domElement );
stats = new Stats();
container.appendChild( stats.dom );
renderer.domElement.addEventListener( 'mousemove', onDocumentMouseMove, false );
renderer.domElement.addEventListener( 'mousedown', onDocumentMouseDown, false );
renderer.domElement.addEventListener( 'mouseup', onDocumentMouseUp, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
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;
raycaster.setFromCamera( mouse, camera );
if ( SELECTED ) {
if ( raycaster.ray.intersectPlane( plane, intersection ) ) {
SELECTED.position.copy( intersection.sub( offset ) );
}
return;
}
var intersects = raycaster.intersectObjects( cubes );
if ( intersects.length > 0 ) {
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( cubes );
if ( intersects.length > 0 ) {
controls.enabled = false;
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();
controls.enabled = true;
if ( INTERSECTED ) {
SELECTED = null;
}
container.style.cursor = 'auto';
}
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
controls.update();
renderer.render( scene, camera );
}
</script>
</body>
</html>
Add a WireframeGeometry or EdgesGeometry as a child of each draggable object.
scene.add( object );
objects.push( object );
// wireframe
var geo = new THREE.EdgesGeometry( object.geometry );
var mat = new THREE.LineBasicMaterial( { color: 0x000000 } );
var wireframe = new THREE.LineSegments( geo, mat );
object.add( wireframe );
Also see this related answer.
three.js r.144
I suggest using EdgesHelper
this.scene.add(image3D);
edges = new THREE.EdgesHelper(image3D, 0x808080);
edges.material.linewidth = 3;
this.scene.add(edges);
Example:

three.js - webgl ocean demo

If you are familiar with the examples for three.js - there is an an example called webgl ocean demo
I can't see the actual materials when I run it locally or when I change the paths to the local resources. Has anyone else resolved this problem? I want to experiment with a different image instead of the globe that is floating in the ocean.
Here is the code:
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - geometry - terrain</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: #000;
font-family:Monospace;
font-size:13px;
margin: 0px;
overflow: hidden;
}
#info {
position: absolute;
top: 0px; width: 100%;
text-align:center;
padding: 5px;
}
a {
color: #a06851;
}
</style>
</head>
<body>
<div id="info">three.js - webgl ocean demo</div>
<script src="../build/three.min.js"></script>
<script src="js/controls/OrbitControls.js"></script>
<script src="js/Mirror.js"></script>
<script src="js/WaterShader.js"></script>
<script src="js/Detector.js"></script>
<script src="js/libs/stats.min.js"></script>
<script>
if ( ! Detector.webgl ) {
Detector.addGetWebGLMessage();
document.getElementById( 'container' ).innerHTML = "";
}
var container, stats;
var camera, scene, renderer;
var sphere;
var parameters = {
width: 2000,
height: 2000,
widthSegments: 250,
heightSegments: 250,
depth: 1500,
param: 4,
filterparam: 1
};
var waterNormals;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.5, 3000000 );
camera.position.set( 2000, 750, 2000 );
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enablePan = false;
controls.minDistance = 1000.0;
controls.maxDistance = 5000.0;
controls.maxPolarAngle = Math.PI * 0.495;
controls.center.set( 0, 500, 0 );
scene.add( new THREE.AmbientLight( 0x444444 ) );
var light = new THREE.DirectionalLight( 0xffffbb, 1 );
light.position.set( - 1, 1, - 1 );
scene.add( light );
waterNormals = new THREE.ImageUtils.loadTexture( 'textures/waternormals.jpg' );
waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping;
water = new THREE.Water( renderer, camera, scene, {
textureWidth: 512,
textureHeight: 512,
waterNormals: waterNormals,
alpha: 1.0,
sunDirection: light.position.clone().normalize(),
sunColor: 0xffffff,
waterColor: 0x001e0f,
distortionScale: 50.0,
} );
mirrorMesh = new THREE.Mesh(
new THREE.PlaneBufferGeometry( parameters.width * 500, parameters.height * 500 ),
water.material
);
mirrorMesh.add( water );
mirrorMesh.rotation.x = - Math.PI * 0.5;
scene.add( mirrorMesh );
// load skybox
var cubeMap = new THREE.CubeTexture( [] );
cubeMap.format = THREE.RGBFormat;
var loader = new THREE.ImageLoader();
loader.load( 'textures/skyboxsun25degtest.png', function ( image ) {
var getSide = function ( x, y ) {
var size = 1024;
var canvas = document.createElement( 'canvas' );
canvas.width = size;
canvas.height = size;
var context = canvas.getContext( '2d' );
context.drawImage( image, - x * size, - y * size );
return canvas;
};
cubeMap.images[ 0 ] = getSide( 2, 1 ); // px
cubeMap.images[ 1 ] = getSide( 0, 1 ); // nx
cubeMap.images[ 2 ] = getSide( 1, 0 ); // py
cubeMap.images[ 3 ] = getSide( 1, 2 ); // ny
cubeMap.images[ 4 ] = getSide( 1, 1 ); // pz
cubeMap.images[ 5 ] = getSide( 3, 1 ); // nz
cubeMap.needsUpdate = true;
} );
var cubeShader = THREE.ShaderLib[ 'cube' ];
cubeShader.uniforms[ 'tCube' ].value = cubeMap;
var skyBoxMaterial = new THREE.ShaderMaterial( {
fragmentShader: cubeShader.fragmentShader,
vertexShader: cubeShader.vertexShader,
uniforms: cubeShader.uniforms,
depthWrite: false,
side: THREE.BackSide
} );
var skyBox = new THREE.Mesh(
new THREE.BoxGeometry( 1000000, 1000000, 1000000 ),
skyBoxMaterial
);
scene.add( skyBox );
var geometry = new THREE.IcosahedronGeometry( 400, 4 );
for ( var i = 0, j = geometry.faces.length; i < j; i ++ ) {
geometry.faces[ i ].color.setHex( Math.random() * 0xffffff );
}
var material = new THREE.MeshPhongMaterial( {
vertexColors: THREE.FaceColors,
shininess: 100,
envMap: cubeMap
} );
sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );
}
//
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
var time = performance.now() * 0.001;
sphere.position.y = Math.sin( time ) * 500 + 250;
sphere.rotation.x = time * 0.5;
sphere.rotation.z = time * 0.51;
water.material.uniforms.time.value += 1.0 / 60.0;
controls.update();
water.render();
renderer.render( scene, camera );
}
</script>
</body>
</html>
Here is my code - just changed the relative paths
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</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: #000;
font-family:Monospace;
font-size:13px;
margin: 0px;
overflow: hidden;
}
#info {
position: absolute;
top: 0px; width: 100%;
text-align:center;
padding: 5px;
}
a {
color: #a06851;
}
</style>
</head>
<body>
<div id="info">AA ocean demo</div>
<script src="js/three.min.js"></script>
<script src="js/controls/OrbitControls.js"></script>
<script src="js/Mirror.js"></script>
<script src="js/WaterShader.js"></script>
<script src="js/Detector.js"></script>
<script src="js/libs/stats.min.js"></script>
<script>
if ( ! Detector.webgl ) {
Detector.addGetWebGLMessage();
document.getElementById( 'container' ).innerHTML = "";
}
var container, stats;
var camera, scene, renderer;
var sphere;
var parameters = {
width: 2000,
height: 2000,
widthSegments: 250,
heightSegments: 250,
depth: 1500,
param: 4,
filterparam: 1
};
var waterNormals;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.5, 3000000 );
camera.position.set( 2000, 750, 2000 );
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enablePan = false;
controls.minDistance = 1000.0;
controls.maxDistance = 5000.0;
controls.maxPolarAngle = Math.PI * 0.495;
controls.center.set( 0, 500, 0 );
scene.add( new THREE.AmbientLight( 0x444444 ) );
var light = new THREE.DirectionalLight( 0xffffbb, 1 );
light.position.set( - 1, 1, - 1 );
scene.add( light );
waterNormals = new THREE.ImageUtils.loadTexture( 'js/waternormals.jpg' );
waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping;
water = new THREE.Water( renderer, camera, scene, {
textureWidth: 512,
textureHeight: 512,
waterNormals: waterNormals,
alpha: 1.0,
sunDirection: light.position.clone().normalize(),
sunColor: 0xffffff,
waterColor: 0x001e0f,
distortionScale: 50.0,
} );
mirrorMesh = new THREE.Mesh(
new THREE.PlaneBufferGeometry( parameters.width * 500, parameters.height * 500 ),
water.material
);
mirrorMesh.add( water );
mirrorMesh.rotation.x = - Math.PI * 0.5;
scene.add( mirrorMesh );
// load skybox
var cubeMap = new THREE.CubeTexture( [] );
cubeMap.format = THREE.RGBFormat;
var loader = new THREE.ImageLoader();
loader.load( 'js/skyboxsun25degtest.png', function ( image ) {
var getSide = function ( x, y ) {
var size = 1024;
var canvas = document.createElement( 'canvas' );
canvas.width = size;
canvas.height = size;
var context = canvas.getContext( '2d' );
context.drawImage( image, - x * size, - y * size );
return canvas;
};
cubeMap.images[ 0 ] = getSide( 2, 1 ); // px
cubeMap.images[ 1 ] = getSide( 0, 1 ); // nx
cubeMap.images[ 2 ] = getSide( 1, 0 ); // py
cubeMap.images[ 3 ] = getSide( 1, 2 ); // ny
cubeMap.images[ 4 ] = getSide( 1, 1 ); // pz
cubeMap.images[ 5 ] = getSide( 3, 1 ); // nz
cubeMap.needsUpdate = true;
} );
var cubeShader = THREE.ShaderLib[ 'cube' ];
cubeShader.uniforms[ 'tCube' ].value = cubeMap;
var skyBoxMaterial = new THREE.ShaderMaterial( {
fragmentShader: cubeShader.fragmentShader,
vertexShader: cubeShader.vertexShader,
uniforms: cubeShader.uniforms,
depthWrite: false,
side: THREE.BackSide
} );
var skyBox = new THREE.Mesh(
new THREE.BoxGeometry( 1000000, 1000000, 1000000 ),
skyBoxMaterial
);
scene.add( skyBox );
var geometry = new THREE.IcosahedronGeometry( 400, 4 );
for ( var i = 0, j = geometry.faces.length; i < j; i ++ ) {
geometry.faces[ i ].color.setHex( Math.random() * 0xffffff );
}
var material = new THREE.MeshPhongMaterial( {
vertexColors: THREE.FaceColors,
shininess: 100,
envMap: cubeMap
} );
sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );
}
//
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
var time = performance.now() * 0.001;
sphere.position.y = Math.sin( time ) * 500 + 250;
sphere.rotation.x = time * 0.5;
sphere.rotation.z = time * 0.51;
water.material.uniforms.time.value += 1.0 / 60.0;
controls.update();
water.render();
renderer.render( scene, camera );
}
</script>
</body>
</html>
If you check your browser's console, you'll see error messages regarding the local access of files: by default, modern browsers do not allow this. This problem isn't three.js related at all, it is a common browser security policy.
Basically, there are two workarounds:
run a local server or put your code online
modify the browser settings to allow local file access.
As a lot of people trying their hands on three.js run into this issue when loading textures, there is a dedicated Three.js Wiki page on the topic.

Rotate a moving model to be parallel on a plane geometry

I am trying to rotate a model on a plane geometry that represents a hill. I use the following code. My problem is that though the model see to have the correct rotation when start animating and is parallel to the face it is moving when getting near and overcoming the point(0,0,0) it is rotating weirdly. Maybe the problem that I have set the up of the model to be the vector(0,0,1) (you can copy paste to an editor and view the example on your browser):
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - trackball controls</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: #000;
font-family:Monospace;
font-size:13px;
text-align:center;
font-weight: bold;
background-color: #fff;
margin: 0px;
overflow: hidden;
}
#info {
color:#000;
position: absolute;
top: 0px; width: 100%;
padding: 5px;
}
a {
color: red;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="info">
three.js - trackball controls example</br>
MOVE mouse & press LEFT/A: rotate, MIDDLE/S: zoom, RIGHT/D: pan
</div>
<script src="http://threejs.org/build/three.min.js"></script>
<script src="http://threejs.org/examples/js/controls/TrackballControls.js"></script>
<script src="http://threejs.org/examples/js/Detector.js"></script>
<script src="http://threejs.org/examples/js/libs/stats.min.js"></script>
<script>
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
var container, stats;
var camera, controls, scene, renderer,mesh,animation,morph;
var cross;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 500;
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( 'change', render );
// world
scene = new THREE.Scene();
var geometry = new THREE.PlaneBufferGeometry(100,100,2,2);
var material = new THREE.MeshPhongMaterial({color: 0xff0000,side:THREE.DoubleSide,
polygonOffset: true,
polygonOffsetFactor: 1, // positive value pushes polygon further away
polygonOffsetUnits: 1});
var vertices = geometry.attributes.position.array;
vertices[ 14 ] =10;
mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
// wireframe
var helper1 = new THREE.WireframeHelper( mesh, 0x000000 ); // or THREE.WireframeHelper
helper1.material.linewidth = 2;
scene.add( helper1 );
// lights
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
light = new THREE.DirectionalLight( 0x002288 );
light.position.set( -1, -1, -1 );
scene.add( light );
light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColor( 0xffffff );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container = document.getElementById( 'container' );
container.appendChild( renderer.domElement );
container.addEventListener( 'mousemove', onMouseMove, false );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
container.addEventListener( 'mousemove', onMouseMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
//
render();
}
var material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
var geometry = new THREE.Geometry();
geometry.vertices.push(
new THREE.Vector3( 0, 0, -20 ),
new THREE.Vector3( 0, 0, 20 )
);
var helper = new THREE.Line( geometry, material );
scene.add( helper );
//////////
var loader = new THREE.JSONLoader( true );
loader.load( "http://threejs.org/examples/models/animated/horse.js", function( geometry ) {
morph = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: 0x606060, morphTargets: true } ) );
morph.scale.set( 0.02, 0.02, 0.02 );
//morph.rotation.set(Math.PI/2,Math.PI/2+Math.PI/4,0);//rotate to look at the direction moving.
morph.position.set(-50,-50,0);
scene.add( morph );
animation = new THREE.MorphAnimation( morph );
animation.play();
} );
/////////
//raycaster function
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
function onMouseMove( event ) {
mouse.x = ( event.clientX / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.height ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
// See if the ray from the camera into the world hits one of our meshes
var intersects = raycaster.intersectObject( mesh );
// Toggle rotation bool for meshes that we clicked
if ( intersects.length > 0 ) {
helper.position.set( 0, 0, 0 );
helper.lookAt( intersects[ 0 ].face.normal );
document.body.style.cursor = "crosshair";
helper.position.copy( intersects[ 0 ].point );
render();
}
else{document.body.style.cursor = "auto";}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
controls.handleResize();
render();
}
function animate() {
requestAnimationFrame( animate );
render();
controls.update();
}
var prevTime = Date.now();
function render() {
if ( animation ) {
var time = Date.now();
animation.update( time - prevTime );
prevTime = time;
}
if(morph){
if(morph.position.x>50){morph.position.x=-50;morph.position.y = -50;}
morph.position.x+=0.3;
morph.position.y+=0.3;
var help = helper.clone();
help.position.set(morph.position.x,morph.position.y,-10);
var ray= new THREE.Raycaster();
ray.set(help.position,new THREE.Vector3(0,0,1).normalize());
var intersect = ray.intersectObject( mesh );
// Toggle rotation bool for meshes that we clicked
if ( intersect.length > 0 ) {
morph.up.set(0,0,1);
morph.position.copy( intersect[ 0 ].point );
morph.lookAt( intersect[ 0 ].face.normal );
}
}
renderer.render( scene, camera );
stats.update();
}
</script>
</body>
</html>
Any ideas to keep the models rotation parallel to the face it is on?
Similar question with this one that has no answer.
i have take the code above and after playing with it for a while was able to get the effect you were going for but maybe not the way that answers your question... here is what i have found anyway... and another note i think i was working with the code that was first posted..
so it's been a long time scene i have dealt with 3d code (2001-2002 time frame) so my knowledge may be both rusty and out of date with newer trends. plus i am new to this frame work.
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - trackball controls</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: #000;
font-family:Monospace;
font-size:13px;
text-align:center;
font-weight: bold;
background-color: #fff;
margin: 0px;
overflow: hidden;
}
#info {
color:#000;
position: absolute;
top: 0px; width: 100%;
padding: 5px;
}
a {
color: red;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="info">
three.js - trackball controls example</br>
MOVE mouse & press LEFT/A: rotate, MIDDLE/S: zoom, RIGHT/D: pan
</div>
<script src="http://threejs.org/build/three.min.js"></script>
<script src="http://threejs.org/examples/js/controls/TrackballControls.js"></script>
<script src="http://threejs.org/examples/js/Detector.js"></script>
<script src="http://threejs.org/examples/js/libs/stats.min.js"></script>
<script>
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
var container, stats;
var camera, controls, scene, renderer,mesh,animation,morph;
var cross;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 500;
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( 'change', render );
// world
scene = new THREE.Scene();
var geometry = new THREE.PlaneBufferGeometry(100,100,2,2);
var material = new THREE.MeshPhongMaterial({color: 0xff0000,side:THREE.DoubleSide,
polygonOffset: true,
polygonOffsetFactor: 1, // positive value pushes polygon further away
polygonOffsetUnits: 1});
var vertices = geometry.attributes.position.array;
vertices[ 14 ] =10;
mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
// wireframe
var helper1 = new THREE.WireframeHelper( mesh, 0x000000 ); // or THREE.WireframeHelper
helper1.material.linewidth = 2;
scene.add( helper1 );
// lights
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
light = new THREE.DirectionalLight( 0x002288 );
light.position.set( -1, -1, -1 );
scene.add( light );
light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColor( 0xffffff );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container = document.getElementById( 'container' );
container.appendChild( renderer.domElement );
container.addEventListener( 'mousemove', onMouseMove, false );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
container.addEventListener( 'mousemove', onMouseMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
//
render();
}
var material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
var geometry = new THREE.Geometry();
geometry.vertices.push(
new THREE.Vector3( 0, 0, -20 ),
new THREE.Vector3( 0, 0, 20 )
);
var helper = new THREE.Line( geometry, material );
scene.add( helper );
//////////
var loader = new THREE.JSONLoader( true );
loader.load( "http://threejs.org/examples/models/animated/horse.js", function( geometry ) {
morph = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: 0x606060, morphTargets: true } ) );
morph.scale.set( 0.02, 0.02, 0.02 );
//morph.rotation.set(Math.PI/2,Math.PI/2+Math.PI/4,0);//rotate to look at the direction moving.
morph.position.set(-50,-50,0);
scene.add( morph );
animation = new THREE.MorphAnimation( morph );
animation.play();
} );
/////////
//raycaster function
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
function onMouseMove( event ) {
mouse.x = ( event.clientX / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.height ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
// See if the ray from the camera into the world hits one of our meshes
var intersects = raycaster.intersectObject( mesh );
// Toggle rotation bool for meshes that we clicked
if ( intersects.length > 0 ) {
helper.position.set( 0, 0, 0 );
helper.lookAt( intersects[ 0 ].face.normal );
document.body.style.cursor = "crosshair";
helper.position.copy( intersects[ 0 ].point );
render();
}
else{document.body.style.cursor = "auto";}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
controls.handleResize();
render();
}
function animate() {
requestAnimationFrame( animate );
render();
controls.update();
}
var prevTime = Date.now();
function render() {
var DelX,DelY,DelZ,LastZ;
DelX=0.3;DelY=0.3;
if ( animation ) {
var time = Date.now();
animation.update( time - prevTime );
prevTime = time;
}
if(morph){
LastZ=morph.position.z;
if(morph.position.x>50){morph.position.x=-50;morph.position.y = -50.1;}
morph.position.x+=DelX;
morph.position.y+=DelY;
var help = helper.clone();
help.position.set(morph.position.x,morph.position.y,-10);
var ray= new THREE.Raycaster();
ray.set(help.position,new THREE.Vector3(0,0,1).normalize());
var intersect = ray.intersectObject( mesh );
// Toggle rotation bool for meshes that we clicked
if ( intersect.length > 0 ) {
morph.up.set(0,0,1);
morph.position.copy( intersect[ 0 ].point );
DelZ=morph.position.z-LastZ;
var PointToLookat = new THREE.Vector3(morph.position.x+DelX,morph.position.y+DelY,morph.position.z+DelZ);
morph.lookAt( PointToLookat );
// old morph.lookAt( intersect[ 0 ].face.normal );
}
}
renderer.render( scene, camera );
stats.update();
}
</script>
</body>
</html>
so basically your LookAt is "pointing" the horse at some point in 3d space at first i assumed it was a vector direction and maybe that what you were assuming too, i at least was wrong, so you have to put your point "in front" of the current position of the object. i added some delta vars and kept track of the lastZ position value (as i write this i realized i could have used a vector for that) so i found the "next spot" the object will be at and used that as the PointToLookat.

three.js - identify point on tube circumference and rotate from that point

Below is my code for the scene of tube geometry. I've loaded 200 co-ordinates as JSON data from external file.
<!DOCTYPE html>
<html lang="en">
<head>
<title>3d Model using HTML5 and three.js</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
#info {
color:#000;
position: absolute;
top: 0px; width: 100%;
padding: 5px;
}
</style>
</head>
<body>
<div id="info">
WASD-move, RF-up/down, QE-roll, mouse-look around, mouse left/right click- zoom-in/out
</div>
<script src="three.min.js" type="text/javascript"></script>
<script src="Curve.js" type="text/javascript"></script>
<script src="Stats.js" type="text/javascript"></script>
<script src="Detector.js" type="text/javascript"></script>
<script src="path.js" type="text/javascript"></script>
<script>
// variables
var container, stats;
var camera, scene, renderer, controls;
var text, plane, tube, tubeMesh, parent;
var targetRotation = 0;
var targetRotationOnMouseDown = 0;
var mouseX = 0, mouseY = 0; var radius = 6371;
var mouseXOnMouseDown = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var clock = new THREE.Clock();
function plotPath()
{
var obj = getPath();
var segments = 60;
var closed = false;
var debug = true;
var radiusSegments = 12;
var tube;
var points = [];
var x=0,y=0,z=0;
var extrudePath;
for(var i=0; i<obj.path.length; i++)
{
console.log(obj.path[i].point);
points.push(obj.path[i].point);
}
extrudePath = new THREE.SplineCurve3(points);
tube = new THREE.TubeGeometry(extrudePath, segments, 2, radiusSegments, closed, debug);
tubeMesh = new THREE.Mesh(tube ,new THREE.MeshBasicMaterial({
color: 0x000000, side: THREE.DoubleSide,
opacity: 0.5, transparent: true, wireframe: true}));
if ( tube.debug ) tubeMesh.add( tube.debug );
scene.add( tubeMesh );
}
init();
animate();
function init(){
// container
container = document.createElement( 'div' );
document.body.appendChild( container );
// scene
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2( 0xcccccc, 0.002 );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColor( scene.fog.color, 1 );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
// camera
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
// light
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
light = new THREE.DirectionalLight( 0x002288 );
light.position.set( -1, -1, -1 );
scene.add( light );
light = new THREE.AmbientLight( 0x555555 );
scene.add( light );
// CONTROLS
controls = new THREE.RollControls( camera );
controls.movementSpeed = 50;
controls.lookSpeed = 3;
controls.constrainVertical = [ -0.5, 0.5 ];
// Grid
geometry = new THREE.Geometry();
geometry.vertices.push( new THREE.Vector3( - 500, 0, 0 ) );
geometry.vertices.push( new THREE.Vector3( 500, 0, 0 ) );
for ( var i = 0; i <= 20; i ++ ) {
line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2 } ) );
line.position.z = ( i * 50 ) - 500;
scene.add( line );
line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2 } ) );
line.position.x = ( i * 50 ) - 500;
line.rotation.y = 90 * Math.PI / 180;
scene.add( line );
}
// projector
projector = new THREE.Projector();
plotPath();
// stats
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
// events
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
update();
}
function update(){
controls.update(clock.getDelta());
stats.update();
}
function render() {
renderer.render( scene, camera );
}
</script>
</body>
</html>
How can I identify a point on tube circumference and how to rotate a tube from that point ?
OrbitControls, for example, has a property target which is both the center of rotation and the camera look-at position.
controls = new THREE.OrbitControls( camera );
You can change the center of rotation of the camera using picking.
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.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var intersects = ray.intersectObjects( objects );
if ( intersects.length > 0 ) {
controls.target.copy( intersects[0].point );
}
}
EDIT: Here is an updated fiddle: http://jsfiddle.net/eVkgs/30/
three.js r.65

Resources