I am Trying to create an interactive Sphere with JavaScript for an assignment for HCI, the problem is that I am a novice to JavaScript and Three.js.
what I am after is to make it so when the sphere is clicked on that it displays the statistics of a specific subject. I have created the sphere and made it into an object but I am having trouble with the interaction of the sphere. I don't care if a div or a alert opens when the sphere is clicked on but I just need it to work as a dummy version
below is an example in JavaScript and THREE.js:
var sphere = new Object({}); //declared sphere as an object first.
var angularSpeed = 0.2;
var lastTime = 0;
function animate (){
//update
var time = (new Date()).getTime();
var timeDiff = time - lastTime;
var angleChange = angularSpeed * timeDiff * 0.1 * Math.PI / 1000;
sphere.rotation.y -= angleChange;
sphere2.rotation.sphere -=angleChange;
lastTime = time;
// render
renderer.render(scene, camera);
requestAnimationFrame(function(){ //request new frame
animate();
});
}
// renderer
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// camera
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 500;
// scene
var scene = new THREE.Scene();
//material
var material = new THREE.MeshLambertMaterial({
map:THREE.ImageUtils.loadTexture('images/earth2.jpg')});
//sphere geometry
sphere = new THREE.Mesh(new THREE.SphereGeometry( 100, 50, 50 ), material);
sphere.overdraw = true;
sphere.rotation.x = Math.PI * 0.1;
sphere.position.x= 0; // moves position horizontally (abscissa) + = right and - = left
sphere.position.y= 0; // moves position virtually (ordinate) + = right and - = left
sphere.position.z= 0; // moves position z (applicate) + = forwards and - = backwards
scene.add(sphere);
//animate
animate();
var sphere = new Object:({event});
function statistics(){
alert('You clicked on the div!') // displays the statistics before the rendering
};
sphere.onMouseDown=statistics(event);
.onMouseDown is only available for HTML element. You can't use this function for THREE.js objects, but Raycaster is exactly what you want!
jsfiddle
var container, stats;
var camera, scene, projector, raycaster, renderer, selected, sphere;
var mouse = new THREE.Vector2(), INTERSECTED;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
scene = new THREE.Scene();
var light = new THREE.DirectionalLight( 0xffffff, 2 );
light.position.set( 1, 1, 1 ).normalize();
scene.add( light );
var light = new THREE.DirectionalLight( 0xffffff );
light.position.set( -1, -1, -1 ).normalize();
scene.add( light );
sphere = new THREE.Mesh(new THREE.SphereGeometry( 20, 50, 50 ), new THREE.MeshNormalMaterial());
sphere.overdraw = true;
scene.add(sphere);
projector = new THREE.Projector();
raycaster = new THREE.Raycaster();
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0xf0f0f0 );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false;
container.appendChild(renderer.domElement);
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
window.addEventListener( 'resize', onWindowResize, false );
renderer.domElement.addEventListener( 'mousedown', onCanvasMouseDown, false);
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
camera.lookAt(new THREE.Vector3(0,0,0));
camera.position = new THREE.Vector3(0,100,100);
// find intersections
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, camera );
raycaster.set( camera.position, vector.sub( camera.position ).normalize() );
selected = raycaster.intersectObjects( scene.children );
renderer.render( scene, camera );
}
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;
}
//detect mouse click on the sphere
function onCanvasMouseDown( event ){
if(selected[0].object==sphere){
statistics();
}
}
function statistics(){
alert('You clicked on the div!') // displays the statistics before the rendering
};
Related
I have been working on implementing a gltf model into a three js project and would like to know if there is any way to reverse a gltf animation when the mouse hovers away from the gltf model. So far I have been able to get the gltf model to play its animation as the mouse hovers over the model, but I would like to reverse the animation once the mouse leaves the model. If anyone could please give me some insight on how I might go about this, it would be greatly appreciated. Thank you.
import * as THREE from 'three';
import { GLTFLoader } from 'GLTFLoader';
import { OrbitControls } from 'OrbitControls';
// Load 3D Scene
var scene = new THREE.Scene();
scene.background = new THREE.Color('black');
const pointer = new THREE.Vector2();
const raycaster = new THREE.Raycaster();
// Load Camera Perspective
var camera = new THREE.PerspectiveCamera( 25, window.innerWidth /
window.innerHeight );
camera.position.set( 10, 1, 25 );
// Load a Renderer
var renderer = new THREE.WebGLRenderer({ alpha: false });
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Load the Orbitcontroller
var controls = new OrbitControls( camera, renderer.domElement );
// Load Light
var ambientLight = new THREE.AmbientLight( 0xFFFFFF );
scene.add( ambientLight );
// Load gltf model and play animation
var mixer;
var mouse = new THREE.Vector2(1, 1);
var loader = new GLTFLoader();
loader.load( './assets/itc.glb', function ( gltf ) {
var object = gltf.scene;
scene.add( object );
mixer = new THREE.AnimationMixer( object );
var action;
gltf.animations.forEach((clip) => {
action = mixer.clipAction(clip);
action.setLoop(THREE.LoopOnce);
action.clampWhenFinished = true;
action.play();
});
object.scale.set( 2, 2, 2 );
object.rotation.y = 37.0;
object.position.x = -10; //Position (x = right+ left-)
object.position.y = -5; //Position (y = up+, down-)
object.position.z = -15; //Position (z = front +, back-)
});
// Animate function
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame( animate );
raycaster.setFromCamera( pointer, camera );
const intersects = raycaster.intersectObjects( scene.children );
if (intersects.length){
mixer.update(clock.getDelta());
}
else{
// How can i reverse the animation when mouse pointer leaves the gltf model
}
renderer.render( scene, camera );
}
// Render function
function render() {
requestAnimationFrame(render);
renderer.render( scene, camera );
}
// On window resize
var tanFOV = Math.tan( ( ( Math.PI / 180 ) * camera.fov / 2 ) );
var windowHeight = window.innerHeight;
window.addEventListener( 'resize', onWindowResize, false );
function onWindowResize( event ) {
camera.aspect = window.innerWidth / window.innerHeight;
// adjust the FOV
camera.fov = ( 360 / Math.PI ) * Math.atan( tanFOV * (
window.innerHeight / windowHeight ) );
camera.updateProjectionMatrix();
camera.lookAt( scene.position );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.render( scene, camera );
}
function onPointerMove( event ) {
pointer.x = ( event.clientX / window.innerWidth ) * 2 - 1;
pointer.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
window.addEventListener( 'pointermove', onPointerMove );
render();
animate();
An easy way to reverse an animation is to set the timeScale property to - 1. So in your case:
action.timeScale = - 1;
You can update the timeScale property at any point in your code.
I've dug around everywhere looking for a solution to this. It seems that on OrbitControls and TrackballControlls the camera wont stay horizontal! As you move around with TrackballControlls the scene starts to roll. OrbitControlls dragging from left to right only rolls the scene. I'd like to be able to use the TrackballControls but keep the camera level with the horizon when moving around the center of the scene. Is this possible?
My code:
// SETUP GLOBAL VARIABLES
var camera, controls, scene, renderer;
// PLANET PHYSICAL LOCATIONS
var sun, mercPL, venPL, earthPL, marsPL, jupPL, satPL, urPL, nepPL;
// TIME, AND SCALARS
var now, scalar, planetScalar;
init();
animate();
function init() {
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
var container = document.getElementById( 'container' );
container.appendChild( renderer.domElement );
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 100000 );
camera.position.z = 200;
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.8;
controls.enableZoom = true;
// ADD THE SUN PHYSICAL LOCATION
var geometry = new THREE.SphereGeometry(5, 30, 30, 0, Math.PI * 2, 0, Math.PI * 2);
var material = new THREE.MeshBasicMaterial({color: "Yellow"});
sun = new THREE.Mesh(geometry, material);
scene.add(sun);
var segmentCount = 32,
radius = 80,
geometry = new THREE.Geometry(),
material = new THREE.LineBasicMaterial({ color: 0xFFFFFF });
for (var i = 0; i <= segmentCount; i++) {
var theta = (i / segmentCount) * Math.PI * 2;
geometry.vertices.push(
new THREE.Vector3(
Math.cos(theta) * radius,
Math.sin(theta) * radius,
0));
}
scene.add(new THREE.Line(geometry, material));
var segmentCount2 = 32,
radius2 = 120,
geometry2 = new THREE.Geometry(),
material2 = new THREE.LineBasicMaterial({ color: 0xFFFFFF });
for (var i = 0; i <= segmentCount2; i++) {
var theta = (i / segmentCount2) * Math.PI * 2;
geometry2.vertices.push(
new THREE.Vector3(
Math.cos(theta) * radius2,
Math.sin(theta) * radius2,
0));
}
scene.add(new THREE.Line(geometry2, material2));
//
window.addEventListener( 'resize', onWindowResize, false );
//
render();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
render(); // MUST BE HERE FOR ANIMATION
}
function render() {
renderer.render( scene, camera );
}
Thanks!
Perhaps this can help you ( all the options are commented in the orbit library)
Level Horizon:
controls.minPolarAngle = Math.PI / 2;
controls.maxPolarAngle = Math.PI / 2;
Focus of Orbit:
controls.target = (cube.position);
Result: (Note: My mouse controls are inverted (Orbit Right / Pan Left) )
Three.js Lock Orbit Controls
Edit: After reviewing your comments code, I think this is what you want:
Three.js Orbit Controls pt 2.
The gist is that :
a) you need to provide the camera with an up vector:
camera.up.set( 0, 0, 1 );
And
b) Target the sun with the camera:
camera.lookAt(sun.position);
You can still play with the damping, and lock the angles ( there are also vertical constraints if you need them) and speed up down the yaw, but I hope this gets you closer.
The problem:
When I click on the cube, TransformControls is attached on the cube. But when I click on the light helper, nothing happen. I want the TransformControls to be attached on the light when I click the light helper. Can you help me with this?
This is the most minimal example.
http://codepen.io/kranzy/pen/ALOYZw
or if you need code:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var transformControls = new THREE.TransformControls(camera,renderer.domElement);
scene.add(transformControls);
var objects = [];
var cube = new THREE.Mesh( geometry, material );
cube.position.x = -10;
scene.add( cube );
objects.push(cube);
var light = new THREE.SpotLight();
scene.add(light);
var help = new THREE.SpotLightHelper(light);
scene.add(help);
objects.push(help);
camera.position.z = 5;
var render = function () {
requestAnimationFrame( render );
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render(scene, camera);
};
render();
window.addEventListener( 'resize', onWindowResize, false );
function onWindowResize(){
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
document.addEventListener( 'touchstart', onDocumentTouchStart, false );
function onDocumentTouchStart(event){
event.preventDefault();
event.clientX = event.touches[0].clientX;
event.clientY = event.touches[0].clientY;
onDocumentMouseDown(event);
}
function onDocumentMouseDown(event){
event.preventDefault();
mouse = new THREE.Vector2();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(objects);
if(intersects.length > 0){
SELECTED = intersects[ 0 ].object;
console.log(SELECTED);
if (SELECTED.constructor === THREE.SpotLightHelper) {
transformControls.attach(SELECTED.light);
} else {
transformControls.attach(SELECTED);
}
}
}
The problem is raycaster.
Add the second parameter of raycaster.intersectObjects() to true.
So the final code is
var intersects = raycaster.intersectObjects(objects, true);
Here is the working sample for attach the transformControls to the light:
http://codepen.io/anon/pen/PGJxBj
I hope this should helps you..
I made a reflection cube and I am trying to put an animated model inside. But something happen in my function render and I can not see anything.
I am making my first steps using javascript and playing with three.js. If you can help me would be amazing.
//var scene, camera, etc
var container, loader;
var camera, scene, projector, renderer;
var controls;
var mesh, mixer;
var pointLight;
var mouseX = 0;
var mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var height = 300; // of camera frustum
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
//renderer
renderer = new THREE.WebGLRenderer( { alpha: true } );
renderer.setClearColor(0xffffff, 1);
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
renderer.gammaInput = true;
renderer.gammaOutput = true;
//set up the scene
scene = new THREE.Scene();
var aspect = window.innerWidth / window.innerHeight;
//set up the Orthographic Camera
camera = new THREE.OrthographicCamera( - height * aspect, height * aspect, height, - height, 1, 10000 );
camera.position.z = 1500;
scene.add( camera );
//set up the controls
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enableZoom = true;
controls.enableDamping = true;
//set up the lights
var ambientLight = new THREE.AmbientLight( 0x111111 );
scene.add( ambientLight );
pointLight = new THREE.PointLight( 0x030303, 0.5 );
pointLight.position.z = 2500;
scene.add( pointLight );
var pointLight2 = new THREE.PointLight( 0x030303, 1 );
camera.add( pointLight2 );
var pointLight3 = new THREE.PointLight( 0xe8e4e4, 0.5 );
pointLight3.position.x = - 1000;
pointLight3.position.z = 1000;
scene.add( pointLight3 );
//create the environment map
var imgAr = [
'sources/instagram2/image1.jpg',
'sources/instagram2/image2.jpg',
'sources/instagram2/image3.jpg',
'sources/instagram2/image4.jpg',
'sources/instagram2/image5.jpg',
'sources/instagram2/image6.jpg',
'sources/instagram2/image7.jpg',
'sources/instagram2/image8.jpg',
'sources/instagram2/image9.jpg',
'sources/instagram2/image10.jpg',
'sources/instagram2/image11.jpg',
'sources/instagram2/image12.jpg',
'sources/instagram2/image13.jpg',
'sources/instagram2/image14.jpg',
'sources/instagram2/image15.jpg',
'sources/instagram2/image16.jpg'
];
var urls = imgAr.sort(function(){return .6 - Math.random()}).slice(0,6);
var reflectionCube = THREE.ImageUtils.loadTextureCube( urls, THREE.CubeReflectionMapping );
//Load the animation
var loader = new THREE.JSONLoader();
loader.load( "sources/models/animated/horse.js", function ( geometry ) {
var material = new THREE.MeshPhongMaterial( {
morphTargets: true,
overdraw: 0.5,
envMap: reflectionCube,
combine: THREE.AddOperation,
reflectivity: 1,
shininess: 0,
side: THREE.DoubleSide
} );
mesh = new THREE.Mesh( geometry, material );
mesh.scale.set( 1.5, 1.5, 1.5 );
mesh.position.set(0,-150,0);
scene.add( mesh );
mixer = new THREE.AnimationMixer( mesh );
var clip = THREE.AnimationClip.CreateFromMorphTargetSequence( 'gallop', geometry.morphTargets, 30 );
mixer.addAction( new THREE.AnimationAction( clip ).warpToDuration( 1 ) );
} );
// window resize
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
var aspect = window.innerWidth / window.innerHeight;
camera.left = - height * aspect;
camera.right = height * aspect;
camera.top = height;
camera.bottom = - height;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//set up the background
var backgroundMesh = new THREE.Mesh(
new THREE.MeshBasicMaterial({
map: texture
}));
backgroundMesh .material.depthTest = false;
backgroundMesh .material.depthWrite = false;
var backgroundScene = new THREE.Scene();
var backgroundCamera = new THREE.Camera();
backgroundScene .add(backgroundCamera );
backgroundScene .add(backgroundMesh );
function animate() {
requestAnimationFrame( animate );
controls.update();
render();
}
var radius = 600;
var theta = 0;
var prevTime = Date.now();
function render() {
theta += 0.1;
camera.position.x = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.z = radius * Math.cos( THREE.Math.degToRad( theta ) );
camera.lookAt( camera.target );
if ( mixer ) {
var time = Date.now();
mixer.update( ( time - prevTime ) * 0.001 );
prevTime = time;
}
renderer.render( scene, camera );
renderer.render(backgroundScene , backgroundCamera );
mixer.update();
}
</script>nter code here
I am making my first steps learning JavaScript and playing with Three.js.
I made a reflection cube with a tween animation and i would like to make this tween runs everytime that I reload my site without clicking.
I have two days trying to make it and cant. Can you tell me which is the problem with my code please? I tryied to verify it in the JavaScript console in Chrome and it didnt say anything. If you can help would be amazing because i am doing my best and it's something really hard.
Here is my code with some comments i made:
<script>
// set up the first variables scene, the camera, etc, etc
var container;
var camera, scene, renderer;
var raycaster;
var mouse;
init();
animate();
function init() {
// My scene is a div inside the html
container = document.createElement( 'div' );
document.body.appendChild( container );
//Set up the camera and make an scene
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
camera.target = new THREE.Vector3( 0, 50, 0 );
camera.position.y = 300;
camera.position.z = 500;
scene = new THREE.Scene();
//environment map
var imgAr = [
'sources/cube_sides/0.jpg',
'sources/cube_sides/02.jpg',
'sources/cube_sides/03.jpg',
'sources/cube_sides/04.jpg',
'sources/cube_sides/05.jpg',
'sources/cube_sides/06.jpg',
'sources/cube_sides/07.jpg',
'sources/cube_sides/08.jpg',
'sources/cube_sides/09.jpg',
'sources/cube_sides/010.jpg',
'sources/cube_sides/011.jpg',
'sources/cube_sides/012.jpg',
'sources/cube_sides/013.jpg',
'sources/cube_sides/014.jpg',
'sources/cube_sides/015.jpg',
'sources/cube_sides/016.jpg',
'sources/cube_sides/017.jpg',
'sources/cube_sides/018.jpg'
];
var urls = imgAr.sort(function(){return .6 - Math.random()}).slice(0,6);
var reflectionCube = THREE.ImageUtils.loadTextureCube( urls, THREE.CubeReflectionMapping );
//load the model
var loader = new THREE.BinaryLoader();
loader.load( "sources/obj/mmlogo/mm_logo.js", function ( geometry ) {
var material = new THREE.MeshPhongMaterial( {
color: 0x515151,
morphTargets: true,
overdraw: 0.5,
envMap: reflectionCube,
combine: THREE.AddOperation,
reflectivity: 1,
shininess: 0,
side: THREE.DoubleSide,
} );
//assign a mesh to the geometry
mesh = new THREE.Mesh( geometry, material );
mesh.scale.set( 120, 120, 120 );
mesh.position.y = 50;
mesh.position.x = 0;
mesh.position.z = 700;
mesh.rotation.y = 10;
mesh.rotation.x = 10;
scene.add( mesh );
//mixer = new THREE.AnimationMixer( mesh );
//var clip = THREE.AnimationClip.CreateFromMorphTargetSequence( 'gallop', geometry.morphTargets, 30 );
//mixer.addAction( new THREE.AnimationAction( clip ).warpToDuration( 1 ) );
} );
//set up the Raycaster
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2();
//set up the renderer
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0xffffff );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild(renderer.domElement);
document.addEventListener( 'load', onDocumentLoad, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentLoad( event ) {
event.preventDefault();
var intersects = raycaster.intersectObjects( scene.children );
new TWEEN.Tween( intersects[ 0 ].object.position ).to( {
x: 0,
y: 50,
z: 70 }, 20000 )
.easing( TWEEN.Easing.Sinusoidal.In).start();
new TWEEN.Tween( intersects[ 0 ].object.rotation ).to( {
x: 0,
y: 0,
z: 0 }, 20000 )
.easing( TWEEN.Easing.Sinusoidal.In).start();
}
function animate() {
requestAnimationFrame( animate );
render();
}
var radius = 600;
var theta = 0;
function render() {
TWEEN.update();
theta += 0;
camera.position.y = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.z = radius * Math.cos( THREE.Math.degToRad( theta ) );
camera.lookAt( camera.target );
renderer.render( scene, camera );
}
</script>