Strange behavior by tween.js animation - javascript

I am rotating a cube by 90 degrees using tween.js but there are these things that are not working as expected,
I am using this code:
var start = {x:cube[1].rotation.x, y:cube[1].rotation.y, z:cube[1].rotation.z};
var end = {x:cube[1].rotation.x, y:cube[1].rotation.y + degreeToRadians(90),z:cube[1].rotation.z};
var tween = new TWEEN.Tween(start)
.to(end, 1000)
.easing( TWEEN.Easing.Exponential.InOut )
.onUpdate(function(){
cube[1].rotation.x = this.x;
cube[1].rotation.y = this.y;
cube[1].rotation.z = this.z;
})
.start()
console.log(cube[1].rotation.y);
by using this the cube rotates by 90 degress along y axis
but in the console it gives value for rotation 0
and when i rotate it again as many times it adds 1.570796 to the rotation value every time.So, I am confused why it does not add this value only the first time.
So to solve the problem i did this
cube[1].rotation.y+=1.57079632679; //1
i added this after every rotation but instead of increasing the value every time which should create problem after first rotation it only increases it once.Value of cube[1].rotation.y goes like this
rotation 1st
before rotation 0
after rotation 1.57.... //because of 1
rotation 2nd
before rotation 1.57....
after rotation 3.14.... //when it should have been 3.14...+ 1.57...
and so my code works even though it shouldn't
and if i add 1.57 only after the first rotation,the rotation lags by 1.
So can anyone explain this.
And when I added this
cube[1].rotation.y+=1.57079632679;
Shouldn't it supposed to not only increase its value but rotate the cube without any animation as well but instead it only does the first part.Why?
EDIT
Actually i haven't placed it in render function but instead I am rotating it on mouse click,here is the code for it:
var mouseDown = false,
pageX = 0;
function onMouseDown(evt)
{
evt.preventDefault();
mouseDown = true;
pageY = evt.pageY;
pageX = evt.pageX;
var x = event.x;
var y = event.y;
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 ) {
if(intersects[ 0 ].object==cube[1])
{
move();
}
}
}
function move (){
var start = {x:cube[1].rotation.x, y:cube[1].rotation.y, z:cube[1].rotation.z};
var end = {x:cube[1].rotation.x , y:cube[1].rotation.y + degreeToRadians(90),
z:cube[1].rotation.z};
var tween = new TWEEN.Tween(start)
.to(end, 1000)
.easing( TWEEN.Easing.Exponential.InOut )
.onUpdate(function(){
cube[1].rotation.x = this.x;
cube[1].rotation.y = this.y;
cube[1].rotation.z = this.z;
})
.start()
console.log(cube[1].rotation.y);
cube[1].rotation.y+=1.57079632679;
console.log(cube[1].rotation.y);
}
function degreeToRadians(degrees) {
return degrees * Math.PI / 180;
}
function onMouseUp(evt)
{
evt.preventDefault();
mouseDown = false;
}
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
TWEEN.update();
renderer.render( scene, camera );
}

You try to move the increment of Y rotation before the tween
function move (){
cube[1].rotation.y+=1.57079632679; // HERE !!!
var start = {x:cube[1].rotation.x, y:cube[1].rotation.y, z:cube[1].rotation.z};
var end = {x:cube[1].rotation.x , y:cube[1].rotation.y + degreeToRadians(90),
z:cube[1].rotation.z};
var tween = new TWEEN.Tween(start)
.to(end, 1000)
.easing( TWEEN.Easing.Exponential.InOut )
.onUpdate(function(){
cube[1].rotation.x = this.x;
cube[1].rotation.y = this.y;
cube[1].rotation.z = this.z;
})
.start()
console.log(cube[1].rotation.y);
// NOT HERE !!!!
console.log(cube[1].rotation.y);
}
In your code you correct rotate the cube by 90°, but at the first render loop the rotation is forced to the tween count value.
Moving first you start the tween with the correct value (rotation + 90°)
I hope I understood your problem!

Related

three.js TubeBufferGeometry position change for camera animation

I'am trying to change position/translate dinamicaly of TubeBufferGeometry.parameters.path which i will use for camera animation
(all are based on this example: webgl_geometry_extrude_splines)
here is my peace of code so far
full code: Link to codesandbox with full code
Important part of code:
function init(){
// initial scene render etc. for full code take a look at codesanbox
// tube
var extrudePath = new Curves.GrannyKnot();
tubeGeometry = new THREE.TubeBufferGeometry( extrudePath, 50, 2, 1, true );
tubeGeometry.dynamic = true;
//unsuccessful approach to change the parameters of position
tubeGeometry.translate(100, 0, 0)
tubeGeometry.attributes.position.needsUpdate = true;
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
// animate camera along spline
var time = Date.now();
var looptime = 20 * 1000;
var t = ( time % looptime ) / looptime;
var pos = tubeGeometry.parameters.path.getPointAt( t );
pos.multiplyScalar( 1 );
splineCamera.position.copy( pos );
renderer.render( scene, splineCamera );
}
The resault is giving me no translation of camera its moving around GrannyKnot curve in default position.
What i'm expecting is to dynamic change position/translate of TubeBufferGeometry.parameters.path in some direction (for example position X + 100) which affects on my camera movement
EDIT
I just realised that i could change camera by adding
function render() {
// animate camera along spline
var time = Date.now();
var looptime = 20 * 1000;
var t = ( time % looptime ) / looptime;
var pos = tubeGeometry.parameters.path.getPointAt( t );
pos.x += 100; //<----this line added
pos.multiplyScalar( 1 );
splineCamera.position.copy( pos );
renderer.render( scene, splineCamera );
}
But if someone would show me how to change TubeBufferGeometry.parameters.path without changing definite pos.x like in code above it. I would be very glad for that.

Three.js - Getting the X, Y, and Z coordinates of mouse click

I'm using version 68 of three.js.
I would like to click somewhere and get the X, Y, and Z coordinates. I followed the steps here, but they give me a Z value of 0: Mouse / Canvas X, Y to Three.js World X, Y, Z
Basically, if I have a mesh in the scene, and I click in the middle of it, I'm hoping to be able to calculate the same values as the position of that mesh. This is just an example. I know I could use raycasting and see if I collided with a mesh and then just check its position. However, I want this to work even if I didn't click a mesh.
Is this possible? Here is a jsfiddle: http://jsfiddle.net/j9ydgyL3/
In that jsfiddle, if I could manage to click in the center of that square, I'm hoping to calculate 10, 10, 10 for the X, Y, and Z values respectively because those are the coordinates of the square's position. Here are the two functions of concern:
function getMousePosition(clientX, clientY) {
var mouse2D = new THREE.Vector3();
var mouse3D = new THREE.Vector3();
mouse2D.x = (clientX / window.innerWidth) * 2 - 1;
mouse2D.y = -(clientY / window.innerHeight) * 2 + 1;
mouse2D.z = 0.5;
mouse3D = projector.unprojectVector(mouse2D.clone(), camera);
return mouse3D;
//var vector = new THREE.Vector3(
//( clientX / window.innerWidth ) * 2 - 1,
//- ( clientY / window.innerHeight ) * 2 + 1,
//0.5 );
//projector.unprojectVector( vector, camera );
//var dir = vector.sub( camera.position ).normalize();
//var distance = - camera.position.z / dir.z;
//var pos = camera.position.clone().add( dir.multiplyScalar( distance ) );
//return pos;
}
function onDocumentMouseUp(event) {
event.preventDefault();
var mouse3D = getMousePosition(event.clientX, event.clientY);
console.log(mouse3D.x + ' ' + mouse3D.y + ' ' + mouse3D.z);
}
I left some of the other code I tried commented out. Please note that this commented-out code didn't work in the jsfiddle website, maybe because they're still using version 54 of three.js. It works fine on my machine with version 68.
Edit: To clarify, I would like to be able to get the coordinates no matter where the mouse is. I just used a mesh in this example because it's easy to verify if it works by seeing if the calculated coordinates are the same as the mesh's. What I would really like is for it to work without using raycasting on a mesh. For example, we could have it always printing the calculated coordinates to the console every time the mouse moves, no matter what is in the scene.
You should use a THREE.Raycaster for this. When you set a list of intersectObjects you will be able to get an array of objects that intersected with the ray. So you can get the position from the 'clicked' object from returned list. Check the updated fiddle here.
I also changed your Three.js to version R68
For more advanced use of THREE.RayCaster check the examples at Threejs.org/examples like this example with interactive cubes.
Relevant code from the updated fiddle:
function getMousePosition(clientX, clientY) {
var mouse2D = new THREE.Vector3();
var mouse3D = new THREE.Vector3();
mouse2D.x = (clientX / window.innerWidth) * 2 - 1;
mouse2D.y = -(clientY / window.innerHeight) * 2 + 1;
mouse2D.z = 0.5;
mouse3D = projector.unprojectVector(mouse2D.clone(), camera);
return mouse3D;
var vector = new THREE.Vector3(
(clientX / window.innerWidth) * 2 - 1, -(clientY / window.innerHeight) * 2 + 1,
0.5);
projector.unprojectVector(vector, camera);
var dir = vector.sub(camera.position).normalize();
var distance = -camera.position.z / dir.z;
var pos = camera.position.clone().add(dir.multiplyScalar(distance));
return pos;
}
function onDocumentMouseUp(event) {
event.preventDefault();
var mouse3D = getMousePosition(event.clientX, event.clientY);
console.log(mouse3D.x + ' ' + mouse3D.y + ' ' + mouse3D.z);
var vector = new THREE.Vector3( mouse3D.x, mouse3D.y, 1 );
raycaster.set( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects(scene.children );
if(intersects.length > 0){
console.log(intersects[0].object.position);
}
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}

threejs manipulate particlesystem

I have a problem to detect if my mouse is over a particle to push it away from the mouse
with a particlesystem in threejs
I use a raycaster but nothing hits.
I also try to add a hitbox to the particle but the hitbox doesn't follow the particle.
for (var p = 0; p < particleCount; p++) {
// create a particle with random
// position values, -250 -> 250
var pX = Math.random() * 200 - 200 / 2,
pY = Math.random() * 150-150/2,
pZ = -5,
particle = new THREE.Vector3(pX, pY, pZ);
// create a velocity vector
particle.velocity = new THREE.Vector3(
0, // x
Math.random()*maxVelocity, // y: random vel
0); // z
// add it to the geometry
// add hitbox on particle
var material = new THREE.MeshBasicMaterial({color : 0x45a314});
var circleGeometry = new THREE.CircleGeometry(maxDistance, 8);
particle.hitbox = new THREE.Mesh(circleGeometry,material);
particle.hitbox.position.set(pX,pY,pZ);
particles.vertices.push(particle);
}
the onmousemove function
function handleMouseMove(event) {
event.preventDefault();
mousePos.x = (event.clientX / window.innerWidth) * 2 - 1;
mousePos.y = -(event.clientY / window.innerHeight) * 2 + 1;
//controle
mouse_vector.set(mousePos.x,mousePos.y,mousePos.z);
projector.unprojectVector(mouse_vector,camera);
var direction = mouse_vector.sub(camera.position).normalize();
ray.set(camera.position, direction);
}
the update function
function update() {
var pCount = particleCount;
while(pCount --){
// get the particle
var particle = particles.vertices[pCount];
// check if we need to reset
if (particle.y > 80 ) {
particle.z = -4;
particle.x = Math.random() * 200 - 200 / 2;
particle.y = -75;
particle.velocity.y = Math.random()*maxVelocity;
// particle.velocity.x = Math.random()*0.4-0.2;
// particle.velocity.y = Math.random()*0.4-0.2;
}
intersects = ray.intersectObject(particle.hitbox);
if(intersects.length){
console.log("hit");
}
// and the position
particle.add(
particle.velocity);
}
// flag to the particle system
// that we've changed its vertices.
particleSystem.geometry.__dirtyVertices = true;
// draw
renderer.render(scene, camera);
// set up the next call
requestAnimationFrame(update);
}
a fiddle of my code
For your hitbox to function, make sure that you add it to the particle with particle.add(particle.hitbox). In that case, the hitbox position is fine as (0, 0, 0) because it will be added to the exact location of its parent object. When the hitbox is a child of the parent particle, it can also be referenced as such with particle.children.
Otherwise, as opposed to using a raycaster, you might try checking to see whether any particles fall within a certain range of the cursor's 2D position, and iterate through those within the bounds to push away. When needing to interact with multiple particles at once, that might be the easier option. Hope that helps!

JS Canvas - draw line at a specified angle

I'd like to make an app where a ball moves at the angle your mouse hits it. So if you swipe your mouse down from top left quadrant at 30 degrees (I guess that would be 180-30 = angle of 150 degrees), it will knock the ball that way. I've been drawing my lines as such:
function drawAngles () {
var d = 50; //start line at (10, 20), move 50px away at angle of 30 degrees
var angle = 80 * Math.PI/180;
ctx.beginPath();
ctx.moveTo(300,0);
ctx.lineTo(300,600); //x, y
ctx.moveTo(0,300);
ctx.lineTo(600,300);
ctx.moveTo(300,300);
ctx.lineTo(600,100);
ctx.arc(300,300,300,0,2*Math.PI);
ctx.stroke();
}
But this doesn't give me an idea of what the angles are.
Then I move the ball at that angle (for now, I'm animating it without mouse interaction)
function getAngleX (x) {
return x = x + (50 * Math.cos(Math.PI/6));
}
function getAngleY(y) {
return y = y + (50 * Math.sin(Math.PI/6));
}
//just animate this box to move at an angle from center down at 30 degrees
$(".anotherBox").mouseenter(function(e) {
pos = $(this).position();
box2X = pos.left;
box2Y = pos.top;
$(this).animate({
//top : $(window).outerHeight(),
top : getAngleY(box2Y)+"px",
left: getAngleX(box2X)+"px",
}, "slow");
});
So how can I draw a line at a specified angle? I'd like to make sure my ball is following along that path.
You can use different approaches to achieve this but if you want to use the same basis to move and draw then this approach may suit well.
First we use a function to get step values for x and y based on the angle (in radians):
function getSteps(angle) {
var cos = Math.cos(angle),
sin = Math.sin(angle);
return {
x: cos -sin,
y: sin + cos
}
}
Then using these steps values we can scale them to get an end point, or scale them gradually to animate an object along the line. A simple loop could look like this (just for example):
function loop() {
var x = i * step.x, // scale using i
y = i * step.y;
ctx.fillRect(200 + x, 200 + y, 2, 2); // add to origin start point 200, 200
i += 1; // increase i
if (i < length) requestAnimationFrame(loop);
}
Live demo
If you just want to draw a line at a certain angle you can do the following instead:
function lineAtAngle(x1, y1, length, angle) {
ctx.moveTo(x1, y1);
ctx.lineTo(x1 + length * Math.cos(angle), y1 + length * Math.sin(angle));
}
then stroke it.
Hope this helps!
If i guess right, i think you want the mouse act like a baseball bat, and you need to measure the current mouse angle, that is to store previous mouse position and do some math.
You have also to keep track if you allready handled current collision, to avoid the ball being 'sticky' and follow the mouse.
http://jsfiddle.net/gamealchemist/z3U8g/
var ctx = cv.getContext('2d');
var ball = {
x:200, y:200,
r : 30,
vx : 0.4, vy:0.4
}
// when mouse moved that distance, ball speed norm will be 1
var speedNorm = 10;
var collisionOnGoing = false;
function collide() {
var dist = sq(ball.x - mx) + sq (ball.y-my);
// too far from ball ?
if (dist > sq(ball.r)) {
collisionOnGoing = false;
return;
}
// return if collision allready handled
if (collisionOnGoing) return;
var mouseDist =Math.sqrt( sq(mx-lastmx) + sq(my-lastmy) );
// no collision if mouse too slow
if (mouseDist<speedNorm/5) return;
// launch the ball in current direction
// with a speed relative to the mouse speed.
var mouseAngle = Math.atan2(my-lastmy, mx-lastmx);
ball.vx= (mouseDist / speedNorm ) * Math.cos(mouseAngle);
ball.vy= (mouseDist / speedNorm ) * Math.sin(mouseAngle);
collisionOnGoing = true;
}
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0,0,400,400);
// collide ball with mouse
collide();
// draw ball
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.r, 0, 6.3);
ctx.fill();
ctx.closePath();
// move
ball.x+=ball.vx;
ball.y+=ball.vy;
// collide with screen
if (ball.x>400) ball.vx=-Math.abs(ball.vx);
if (ball.x<0) ball.vx=Math.abs(ball.vx);
if (ball.y>400) ball.vy=-Math.abs(ball.vy);
if (ball.y<0) ball.vy=Math.abs(ball.vy);
}
animate();
// --- Mouse handling ---
addEventListener('mousemove', mouseMove);
var mx=-1, my=-1, lastmx=-1, lastmy=-1;
var cvRect = cv.getBoundingClientRect();
var cvLeft = cvRect.left;
var cvTop = cvRect.top;
function mouseMove(e) {
lastmx = mx; lastmy=my;
mx=e.clientX - cvLeft;
my=e.clientY - cvTop;
}
function sq(x) { return x*x; }

Detect clicked object in THREE.js

I have a THREE.js scene where a lot of elements appear, and I need to detect what object the user is clicking on.
What I have done so far is the following. The camera does not move to much - it only changes the vertical position by a limited amount, always looking towards the same point. My approximate method is the following:
I take the coordinates if the click relative to the canvas
I translate them into horizontal and vertical coordinates in the webGL scene by means of a simple rescaling, and add a Z coordinate which is sufficiently far away.
I take a horizontal ray starting from the point above, constructed by THREE.Ray()
I use ray.intersectObjects() to find the first element along the ray.
This method approximately works, but it is sometimes a few pixels away from the actual point.
Is there a more reliable technique to find out the object where a user has clicked?
Depends on what kind of camera are you using.
1) PerspectiveCamera: is ok link that Mr.doob provides.
2) OrthographicCamera: is quite different:
var init = function() {
camera = new THREE.OrthographicCamera( SCREEN_WIDTH / - 2, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, SCREEN_HEIGHT / - 2, NEAR, FAR);
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
}
function onDocumentMouseDown( e ) {
e.preventDefault();
var mouseVector = new THREE.Vector3();
mouseVector.x = 2 * (e.clientX / SCREEN_WIDTH) - 1;
mouseVector.y = 1 - 2 * ( e.clientY / SCREEN_HEIGHT );
var raycaster = projector.pickingRay( mouseVector.clone(), camera );
var intersects = raycaster.intersectObject( TARGET );
for( var i = 0; i < intersects.length; i++ ) {
var intersection = intersects[ i ],
obj = intersection.object;
console.log("Intersected object", obj);
}
}
Check out this one:
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 5000);
var object; //your object
document.addEventListener('mousedown', onMouseDown, false);
function onMouseDown(e) {
var vectorMouse = new THREE.Vector3( //vector from camera to mouse
-(window.innerWidth/2-e.clientX)*2/window.innerWidth,
(window.innerHeight/2-e.clientY)*2/window.innerHeight,
-1/Math.tan(22.5*Math.PI/180)); //22.5 is half of camera frustum angle 45 degree
vectorMouse.applyQuaternion(camera.quaternion);
vectorMouse.normalize();
var vectorObject = new THREE.Vector3(); //vector from camera to object
vectorObject.set(object.x - camera.position.x,
object.y - camera.position.y,
object.z - camera.position.z);
vectorObject.normalize();
if (vectorMouse.angleTo(vectorObject)*180/Math.PI < 1) {
//mouse's position is near object's position
}
}
Checks for intersection of the mouse and any of the Cubes in 3d space and alters it's color. Maybe this help you.
I ran into problems trying to implement this for a canvas which does not take up the entire width and height of the screen. Here is the solution I found works quite well.
Initialize everything on an existing canvas:
var init = function() {
var canvas_model = document.getElementById('model')
var viewSize = 50 // Depending on object size, canvas size etc.
var camera = new THREE.OrthographicCamera(-canvas_model.clientWidth/viewSize, canvas_model.clientWidth/viewSize, canvas_model.clientHeight/viewSize, -canvas_model.clientHeight/viewSize, 0.01, 2000),
}
Add an event listener to the canvas:
canvas_model.addEventListener('click', function(event){
var bounds = canvas_model.getBoundingClientRect()
mouse.x = ( (event.clientX - bounds.left) / canvas_model.clientWidth ) * 2 - 1;
mouse.y = - ( (event.clientY - bounds.top) / canvas_model.clientHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
// Do stuff
}
}, false)
Or for a 'touchstart' event, change the lines calculating the mouse.x and mouse.y into:
mouse.x = ( (event.touches[0].clientX - bounds.left) / canvas_model.clientWidth ) * 2 - 1;
mouse.y = - ( (event.touches[0].clientY - bounds.top) / canvas_model.clientHeight ) * 2 + 1;

Categories

Resources