Long time first time and all that - I'm new to Three.js and Tween.js and was hoping to see if it's possible to simultaneously tween 200k Three.js vertices from one position to another. Please excuse my interchangeable use of the words pixels and vertices.
I would like to display 200k pixels in a grid. A user can decide to sort these 200k pixels in a number of ways causing them to rearrange in the grid. I would like all pixels to simultaneously tween between their initial position and final position. Currently, I have the vertices simultaneously moving with tweens but I'm having severe performance issues once the animation nears completion. I hope someone can help!
For each of the 200k vertices I have a tween object associated with them living in this list that I create after drawing vertices in the scene,
var scPartVerts = scene.children[0].geometry.vertices;
var dataSetLen = 200000;
tweenList = []
for (i=0; i<dataSetLen; i ++){
tweenList.push(new TWEEN.Tween(scPartVerts[i]))
}
Using D3 (just what I was familiar with for handling click events), I provide each tween with a new XY position to move to
d3.select("#key").on("click", function() {
for (i = 0; i < dataSetLen; i ++){
var newX = desiredXPostionList[i]; //grab the new X from presorted list
var newY = desiredYPositionList[i]; //grab the new Y from presorted list
tweenList[i].to( {
x: newX,
y: newY
}, 2500)
.start();
}
I then update the tweens while rendering,
function render() {
scene.children[0].geometry.verticesNeedUpdate = true;
TWEEN.update();
renderer.render( scene, camera );
}
The animation appears to run fine for ~75% of the tween and then it comes to a grinding, stuttering, 0 FPS, screeching halt for around 30 seconds once the vertices are close to their final positions. I tried to look at the animation timeline and it appears that all of that time is being dumped into updating the tweens.
Am I somehow supplying redundant tween updates using my d3.select method? (Does javascript register one click as 10 and try to update the tween 10x?) Or can tween.js not smoothly tween 200k positions simultaneously? Thank you so much for any help!
My approach from scratch is to use loops for vertices. The solution is not the ultimate truth, of course.
The plan: set duration and current time of animation,
var duration = 10; // seconds
var currentTime = 10;
var clock = new THREE.Clock();
remember the end position of a vertex, set a random start position for it, find the vector between those positions (direction),
fieldGeom = new THREE.PlaneGeometry(500, 500, 500, 500);
fieldGeom.vertices.forEach(function(vertex){
vertex.startPosition = new THREE.Vector3(THREE.Math.randInt(-500,500),THREE.Math.randInt(-500,500),THREE.Math.randInt(-500,500));
vertex.endPosition = vertex.clone();
vertex.direction = vertex.startPosition.clone().sub(vertex.endPosition);
vertex.copy(vertex.startPosition);
});
then in animation loop add the result vector, multiplied with proportion of currentTime / duration
var delta = clock.getDelta();
currentTime -= delta;
if (currentTime < 0) currentTime = 0;
fieldGeom.vertices.forEach(function(vertex){
vertex.addVectors(vertex.endPosition,vertex.direction.clone().multiplyScalar(currentTime / duration));
});
fieldGeom.verticesNeedUpdate = true;
jsfiddle example with 250K points.
Related
I am trying to capture a FreeCamera's location and pan angle or rotation so I can reposition the camera later with the exact same view.
(I am working with an altered version of the Collision example at http://www.babylonjs-playground.com/)
I seem to be able to get camera.position.x, camera.position.y and camera.position.z ok but camera.cameraRotation.y always yields zero.
According to
http://www.html5gamedevs.com/topic/11380-rotate-camera-around-z-axis/
with upVector:
camera.noRotationConstraint=true;
camera.upVector = new BABYLON.Vector3(1, 0.2, -1);
Playground example:
http://www.babylonjs-playground.com/#2FNBTG#1
The method I've come up with that seems to work reliably is to first collect the various camera stats:
var firstPosition=camera.position.x.toFixed(2)+"/"+camera.position.z.toFixed(2)+"/"+camera.rotation.x.toFixed(2)+"/"+camera.rotation.y.toFixed(2)+"/"+camera.rotation.z.toFixed(2);
which I store in a database. Then, when needed, I get the camera position by:
var firstArray=firstPositionL.split("/");
var firstX=Number(firstArray[0]);
var firstZ=Number(firstArray[1]);
var firstRx=Number(firstArray[2]);
var firstRy=Number(firstArray[3]);
var firstRz=Number(firstArray[4]);
Then when recreating the scene I add this just at the last of the scene creation. I put it into a delay function to give the scene time to establish itself:
var myVar = setInterval(function(){ myTimer() }, 1000);
function myTimer() {
camera.position.x=firstX;
camera.position.z=firstZ;
camera.rotation.x=firstRx;
camera.rotation.y=firstRy;
camera.rotation.z=firstRz;
clearInterval(myVar);
}
EDIT : I have rephrased my question to help users with the same problem.
I have a three.js scene on which I have added some spheres.
I want to move the camera towards a specific direction until all the objects (which are randomly positioned inside the scene) are "fitting exactly" the user's screen.
I have found the answer to my problem!
1. I move the camera (zooming to the desired direction) inside a loop, and in every repeat I create a new frustum using the camera's matrix
2. I check if any of my spheres intersects with a plane of the frustum. If it does, that means that part of one of my objects is outside the frustum so I break the loop and move the camera to its last position.
The above might also works for any object (not only spheres) because every object has a boundingSphere that can be calculated (it might not be very precise the result though).
It also works when zooming out, you 'd just have to move the camera from the object until none of the has a negative distance from all the planes (negative distance means object is "outside" the plane of the frustum).
Code (only for zooming out - r72) :
var finished = false;
var camLookingAt = /* calc. */ ;
while( finished === false ){
var toDirection= camera.position.clone().sub(camLookingAt.clone());
toDirection.setLength(vec.length() - 1); // reduce length for zooming out
camera.position.set(toDirection.x, toDirection.y, toDirection.z);
camera.updateMatrix(); // make sure camera's local matrix is updated
camera.updateMatrixWorld(); // make sure camera's world matrix is updated
var frustum = new THREE.Frustum();
frustum.setFromMatrix( new THREE.Matrix4().multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ) );
for (var j = frustum.planes.length - 1; j >= 0; j--) {
var p = frustum.planes[j];
for (var i = myMeshSpheres.length - 1; i >= 0; i--) {
var sphere = new THREE.Sphere(myMeshSpheres[0].position.clone(), myMeshSpheres[0].radius);
if( p.distanceToSphere(sphere) < 1 ){ // if is negative means part of sphere is outside plane/frustum
finished = true;
}
}
}
Using THREE.js I would like to animate the size of an object so it slowly shrinks into nothingness.
Using answers from Three.js - Animate object size I can change the size of an object, but this size change is instant and I want the change to happen over a period of time (3 seconds). Plus the question is old :)
This is my current code. backwardMeshOct is simply a THREE.Mesh(geometry, material):
var time = 20;
function animate() {
backwardMeshOct.scale.x = Math.abs( Math.sin( time * 50 ) );
}
requestAnimationFrame(animate);
I've tried altering var time and what time is multiplied by but the result is still the same, an instant size scale on x.
Thanks for your help doods and doobs.
You might find it easier to use a library like Tween.JS. You'll be able to define a starting value, a target value, and a time delta for the change!
Check out the answer to this question for how it can work with Three: Tween JS basics on three JS cube
If you aren't able to use Tween, try a THREE.Clock object to keep track of the time and scale your mesh appropriately. I made one in a global scope (before init) and then used it to count to 3 seconds:
function render() {
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
var t = clock.getElapsedTime();
if (t >= 3.0)
{
clock = new THREE.Clock;
mesh.scale.set(1,1,1);
}
else
{
mesh.scale.x = 1-(t/3.0);
mesh.scale.y = 1-(t/3.0);
mesh.scale.z = 1-(t/3.0);
}
renderer.render(scene, camera);
}
full example here: http://jsfiddle.net/adamfinley/ac6zxgt6/1/
I was checking out the demo of greensock. You can see that the result is a firefly animation, unfortunately the more time passes the slower the browser gets. Even if set to a total of 15-30 dots.
Is there any efficient way to stop the script from accumulating somehow?
function RN(x){
return Math.random()*x;
}
var total = 100 ;
var w = window.innerWidth;
var h = window.innerHeight;
var target = document.getElementById('container');
for (i=0; i<total; i++){
var e = document.createElement('div');
e.className = "dot";
e.opacity = 0;
e.style.left = RN(w)+'px';
e.style.top = RN(h)+'px';
target.appendChild(e);
animm(e);
}
function animm(elm){
TweenMax.killTweensOf(elm);
TweenMax.to(elm, (RN(80))+40, {
bezier:{
curviness:3,
values:[
{left:RN(w), top:RN(h)},
{left:RN(w), top:RN(h)}
]
},
onComplete:function(){
TweenMax.delayedCall(RN(2), animm, [elm]);
}
});
TweenMax.to(elm, (RN(4)+2),{
force3D:true,
opacity:RN(.7)+.1,
repeat:-1,
scale:RN(1.25)+.25,
yoyo:true,
ease:Bounce.easeInOut
});
};
http://codepen.io/maelfyn/pen/GgRLbg
Animate only X and Y properties, which are cheap for browser to render, not left and right. X and Y do not trigger recalculation of layout. Best solution would be proper redistribution of memory usage between CPU and GPU. Use CSS3 animations/transitions, which consume GPU instead of CPU (CPU is standardly used by javascript animations).
The browser is handling this just fine. If you change the speed to 3 temporarily you can see that the animation speed is getting slower as the dots settle towards their final destination finally coming to a stop, before starting the next animation.
TweenMax.to(elm, 3, {
bezier:{
curviness:3,
values:[
{left:RN(w), top:RN(h)},
{left:RN(w), top:RN(h)}
]
}
You should try playing around with various settings to get the dots to have a more even speed throughout the animation.
In three.js I have a space ship at xyz, And id like it to fly towards a mesh object of a planet at xyz.
I cannot for the life of me figure this out.
Needs to travel in a straight line, at a speed constant towards the planet.
updateFcts.push(function(delta, now){
if (shipArr[0]===undefined){
}else{
//create two vector objects
var xd = new THREE.Vector3(marsMesh.position.x,marsMesh.position.y,marsMesh.position.z);
var yd = new THREE.Vector3(shipArr[0].position.x,shipArr[0].position.y,shipArr[0].position.z);
//find the distance / hypotnuse to the xyz location
var dicks = shipArr[0].position.distanceTo(marsMesh.position);
var subvec = new THREE.Vector3();
subvec = subvec.subVectors(xd,yd);
//sub subtrac the 3 vectors.
var hypotenuse = dicks;
console.log(hypotenuse);
//1.5 stops it at 1.5 distance from the target planet
if(hypotenuse > 1.5){
//console.log(hypotenuse);
shipArr[0].position.y += .0001*200*(subvec.y/hypotenuse);
shipArr[0].position.x += .0001*200*(subvec.x/hypotenuse);
shipArr[0].position.z += .0001*200*(subvec.z/hypotenuse);
}else{
//within fire range
alert ("FIIIIIRE");
}
}
})
I tried tween.js and was unhappy so i coded a function myself.
You could use https://github.com/sole/tween.js which is focused on that.
A very basic example http://jsfiddle.net/qASPe (square will fly towards sphere after 5s) with mainly this code:
new TWEEN.Tween(ship.position)
.to(planet.position, 700) // destination, duration
.start();
Later, you might want to use a THREE.Curve, or other Path mechanism, as a "flying" path like here http://jsfiddle.net/aevdJ/12
// create a path
var path = new THREE.SplineCurve3([
ship.position,
// some other points maybe? representing your landing/takeoff trajectory
planet.position
]);
new TWEEN.Tween({ distance:0 })
.to({ distance:1 }, 3000) // destination, duration
.onUpdate(function(){
var pathPosition = path.getPointAt(this.distance);
ship.position.set(pathPosition.x, pathPosition.y, pathPosition.z);
})
.start();
In all cases, do not forget to add this line in your update function
TWEEN.update();