I just stumbled upon Three.js and I quite like it. I'm new to JavaScript but I would like to learn more about animation and such.
//UPDATE
I currently have this code, but it's not doing anything. If I try and do this without a custom texture then it renders a cube. So everything currently works except the texture.
var camera, scene, renderer;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 500;
scene = new THREE.Scene();
container = document.createElement( 'div' );
document.body.appendChild( container );
var cubeTexture = new THREE.texture();
var loader = new THREE.ImageLoader();
loader.addEventListener("load", function(event) {
cubeTexture.image = event.content;
cubeTexture.needsUpdate = true;
});
loader.load("crate.gif");
var geometry = new THREE.CubeGeometry(300, 300, 300);
var material = new THREE.MeshBasicMaterial({ map: cubeTexture, overdraw: true });
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
container.appendChild( renderer.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX );
mouseY = ( event.clientY - windowHalfY );
}
function animate() {
requestAnimationFrame( animate );
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 );
}
Any help would be greatly appreciated!
You should have seen your error in the Console.
var cubeTexture = new THREE.Texture();
Free tip: do this
var geometry = new THREE.CubeGeometry( 300, 300, 300, 4, 4, 4 );
otherwise, with CanvasRenderer you will get a lot of distortion.
So if I understand it correctly, you want to the image to stretch and move along with your mouse?
Why not use javascript for that?
You could easily read out the y & x positions of the mouse and then change the position of the image dynamically.
Correct me if i'm wrong, but if this is what you want, than I recommend using javascript.
Sincerly,
Harmen Brinkman.
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 have this really simple test scene to place a bunch of html labels on the position of a 3d object in my scene. But when ever i try converting the 3d coordinates to the screen location i get weird values that do not at all corrosponded with the position of my 3d object. See below for the code with which i generate a scene and place a label:
var CanvasContainer = document.getElementById("KettingContainer");
function CameraSetup(container){
var camera = new THREE.PerspectiveCamera( 50, container.offsetWidth / container.offsetHeight, 1, 300 );
camera.position.set( 0, 0, 100 );
camera.lookAt( 0, 0, 0 );
return camera;
}
function SceneSetup(){
var scene = new THREE.Scene();
return scene;
}
function RenderSetup(container) {
var renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.autoClear = false;
renderer.setClearColor( 0x000000, 0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( container.offsetWidth, container.offsetHeight );
container.appendChild( renderer.domElement );
return renderer;
}
function OnWindowResize(container){
camera.aspect = container.offsetWidth / container.offsetHeight;
camera.updateProjectionMatrix();
renderer.setSize( container.offsetWidth, container.offsetHeight );
}
var kraalLabels = document.getElementById('kraalLinkLabels').getElementsByTagName('div');
function Animate(){
requestAnimationFrame(Animate);
Render();
}
function Render(){
renderer.render( scene, camera );
}
function createScreenVector(pos, camera, width, height) {
const hw = width/2, hh = height/2;
var pos2d = pos.clone();
pos2d.project(camera);
console.log(pos2d);
pos2d.x = ( pos2d.x * hw ) + hw;
pos2d.y = - ( pos2d.y * hh ) + hh;
return pos2d;
}
function positionLabels(positions, camera, width, height){
for( var i = 0; i < kraalLabels.length; i++ )
{
const d = createScreenVector(positions, camera, width, height);
kraalLabels[i].style.left = d.x + "px";
kraalLabels[i].style.top = d.y + "px";
}
}
var camera = CameraSetup(CanvasContainer);
var scene = SceneSetup();
var renderer = RenderSetup(CanvasContainer);
console.log("fc: ", camera.position);
const geometry = new THREE.SphereGeometry( 10, 32, 32 );
const material = new THREE.MeshBasicMaterial( {color: 0xff0000} );
let sphere = new THREE.Mesh( geometry, material );
sphere.position.set(20, 20, 0.1);
scene.add( sphere );
positionLabels(new THREE.Vector3(20, 20, 0.1), camera, CanvasContainer.offsetWidth, CanvasContainer.offsetHeight);
window.addEventListener("resize", function(){ OnWindowResize(CanvasContainer); }, false);
Animate();
See below a screenshot of the camera position, the canvas container size, and the screen location for each of the labels:
As you can see the positions of the labels are huge way bigger then the canvas itself. And the positions are all in the negative...
Even doh my object is just a bit to the top and left of the center of the screen..
I am completely stuck at this time i have no clue what i am doing wrong :(
If extra information is needed i am happy to clarify!
After searching for a solution for houres upon hours i finally found the problem before you do the vector3.project(camera); you have to update the camera matrix like so: camera.updateMatrixWorld(); this will fix the problem with weird values.
Hopes this helps someone in the future!
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 just recently started creating a 3D model for a website I am creating.. and for some reason one of the arms of the character is transparent, but only partially as in you can see inside it. As seen here:
Transparent arm and I can't figure it out. I tried back tracking some of my code to no avail, and I tried re rendering it in blender (as it is a .obj import) and it still has the same issue. Does anyone happen to know a fix for the issue?
<body>
<script src="./JS/three.js"></script>
<script src="./JS/DDSLoader.js"></script>
<script src="./JS/MTLLoader.js"></script>
<script src="./JS/OBJLoader.js"></script>
<script src="./JS/OrbitControls.js"></script>
<script src="./JS/Detector.js"></script>
<script src="./JS/stats.min.js"></script>
<script>
var container, stats;
var camera, scene, renderer;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
renderer = new THREE.WebGLRenderer( {
alpha: true,
antialias: true
} );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( 200,300);
container.appendChild( renderer.domElement );
camera = new THREE.PerspectiveCamera( 14, window.innerWidth / window.innerHeight, .3, 1000 );
camera.position.z = 1;
camera.position.y = 8;
//camera.rotation.y = -90*(180/3.14159265)
var orbit = new THREE.OrbitControls( camera, renderer.domElement );
orbit.enableZoom = false;
orbit.enablePan = false;
orbit.autoRotate = true;
// scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0xffffff );
scene.add( ambient );
var dirLight = new THREE.DirectionalLight(0xffffff, .41);
dirLight.position.set(100, 100, 50);
scene.add(dirLight);
var light = new THREE.PointLight( 0xf8f8ff, 0.25, 10000 );
light.position.set( 0, 100,-75);
scene.add( light );
//var directionalLight = new THREE.DirectionalLight( 0xf8f8ff );
//directionalLight.position.set( 0, 0, 1 ).normalize();
//scene.add( directionalLight );
// model
var onProgress = function ( xhr ) {
if ( xhr.lengthComputable ) {
var percentComplete = xhr.loaded / xhr.total * 100;
console.log( Math.round(percentComplete, 2) + '% downloaded' );
}
};
var onError = function ( xhr ) { };
THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setBaseUrl( './Avatar/' );
mtlLoader.setPath( './Avatar/' );
mtlLoader.load( 'Avatar.mtl', function( materials ) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials( materials );
objLoader.setPath( './Avatar/' );
objLoader.load( 'Avatar.obj', function ( object )
{
object.alphaTest = 0;
object.transparent = false;
object.side = THREE.DoubleSide;
//object.rotation.y = -25*(180/3.14159265)
//object.rotation.x = -35*(180/3.14159265)
object.position.y = 0;
scene.add( object );
}, onProgress, onError );
});
//
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
//renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX ) / 2;
mouseY = ( event.clientY - windowHalfY ) / 2;
}
//
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
//camera.position.x += ( mouseX - camera.position.x ) * .005;
//camera.position.y += ( - mouseY - camera.position.y ) * .005;
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
</script>
I'm a bit lost at this point and could really use your help! Thanks in advance. If you have any questions, or not sure about something in my code let me know.
Face normals of the arm might be flipped backside. As you may know, if the face normal is facing backside, it will not be rendered, and only back-side will be rendered, even in 3d modeler too such as blender.
You can check normal direction in three.js using FaceNormalsHelper as well.
Docs for the helper:
http://threejs.org/docs/#Reference/Extras.Helpers/FaceNormalsHelper
http://threejs.org/examples/#webgl_helpers
If normal direction were wrong, flip them in your 3d modeler app.
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
};