Easter egg. Follow() function — moving 3D objects with a curve.
Introduction
Some days ago I needed to make my parrot fly on trajectory. First thing that I remembered was THREE.Curve that has getPoint(u) method, where “u” is a value between 0 and 1. It will return a THREE.Vector3 object that stores coordinates of point, which locates at “u” × 100 percentage of curve’s length.
Logic
If I can know a point owned by curve knowing just a percentage from curve’s start — I can make object move by this curve changing it’s position each frame. There is how WHS.loop will help us now. We can calculate the percentage of completion of the object’s movement. It will be:
clock.getElapsedTime() * 1000 / gEnd
Where clock.getElapsedTime() is a time since the loop was started (in seconds) and gEnd is the a duration of our movement (in milliseconds).
Implementing object rotation
Now we now that we can get any point of the curve having movement’s time that updates each frame. Our object that will move is a WHS.Shape object, so we can get it’s three.js analog (THREE.Mesh) simply by calling .getNative() method on it. THREE.Mesh has .lookAt() method that rotates object to look directly at the point passed to this method. Our point for that will be the next point of this curve. We can find it simply by adding 0.01 to “u” in getPoint() method:
curve.getPoint( u + 0.01 );
But if “u” will be 1, we’ll get 1.01 passed into getPoint method and returned vector will be “undefined” + you will see error message in the console, so we should calculate it’s remainder of the division by one.
curve.getPoint( (u + 0.01) % 1 ); // Next point(vector3) of the curve.
API part
WHS.Shape, WHS.Camera & WHS.Light
Follow() function is implemented using flexible WHS.loop function for making smooth animations in more performant way.
Using it with a WHS.Morph object will look like:
var curve = new THREE.CurvePath();curve.add(
new THREE.CubicBezierCurve3(
new THREE.Vector3( -100, 100, 50 ),
new THREE.Vector3( -100, 160, 300 ),
new THREE.Vector3( 200, 180, 30 ),
new THREE.Vector3( 100, 140, 80 )
)
);curve.add(
new THREE.CubicBezierCurve3(
new THREE.Vector3( 100, 140, 80 ),
new THREE.Vector3( 200, 80, 150 ),
new THREE.Vector3( -200, 60, -100 ),
new THREE.Vector3( 200, 100, 350 )
)
); curve.add(
new THREE.CubicBezierCurve3(
new THREE.Vector3( 200, 100, 350 ),
new THREE.Vector3( 200, 80, 150 ),
new THREE.Vector3( -200, 60, -100 ),
new THREE.Vector3( -100, 100, 50 )
)
);parrot.addTo( GAME, “wait” ).then(function (obj) {
obj.follow(
curve, // path of parrot.
20000, // 20 seconds (duration)
true // Loop mode.
);
});
Support
Now follow() function is supported in r9 dev version which is currently on github.