Controlling speed of animation as point animates across line - javascript

I am trying to control the speed of an animation so it matches the actual speed that a vehicle took.
let counter = 0;
const animate = () => {
counter += 1;
// Update point geometry to a new position based on the animation
// And the distance the point has travelled along the route.
const updatedPoint = turf.along(lineString, (counter / 100) * lineDistance, 'kilometers');
moveVehicleToPoint(updatedPoint);
// updatedPoint represents a point between/along between origin and destination
if (updatedPoint.geometry.coordinates[0] !== destination) {
requestAnimationFrame(animate);
}
}
animate();
I'm nearly there but maths isn't my strongest asset.
lineDistance is roughly 0.01-0.02km on average.
lineString contains the start and end coordinate.
turf.along take the lineString, a set distance, and returns the distance from the start to your provided distance along the line in kilometers.
Currently, I've included an arbitrary value of 100 to divide by. If the vehicle move took 1 second, this is pretty good. It will move along to the next point by roughly a second.
If it took 2 seconds, it'll be too slow, and finish moving well before the vehicle would have.
How can I include my durationSeconds variable, so that if I say it took 2 seconds, the animate() will perfectly animate along the line over 2 seconds?

Try setting up a variable in the outer scope that relates to the speed you need. Change it's value accordingly to control it.

Related

THREE.js raycasting very slow against single > 500k poly (faces) object, line intersection with globe

in my project I have a player walk around a globe. The globe is not just a sphere, it has mountains and valleys, so I need the players z position to change. For this I'm raycasting a single ray from player's position against a single object (the globe) and I get the point they intersect and change players position accordingly. I'm only raycasting when the player moves, not on every frame.
For a complex object it takes forever. It takes ~200ms for an object with ~1m polys (faces) (1024x512 segments sphere). Does raycasting cast against every single face ?
Is there a traditional fast way to achieve this in THREE, like some acceleration structure (octree? bvh? -- tbh from my google searches I haven't seem to find such a thing included in THREE) or some other thinking-out-of-the-box (no ray casting) method?
var dir = g_Game.earthPosition.clone();
var startPoint = g_Game.cubePlayer.position.clone();
var directionVector = dir.sub(startPoint.multiplyScalar(10));
g_Game.raycaster.set(startPoint, directionVector.clone().normalize());
var t1 = new Date().getTime();
var rayIntersects = g_Game.raycaster.intersectObject(g_Game.earth, true);
if (rayIntersects[0]) {
var dist = rayIntersects[0].point.distanceTo(g_Game.earthPosition);
dist = Math.round(dist * 100 + Number.EPSILON) / 100;
g_Player.DistanceFromCenter = dist + 5;
}
var t2 = new Date().getTime();
console.log(t2-t1);
Thank you in advance
Do not use three.js Raycaster.
Consider Ray.js that offers function intersectTriangle(a, b, c, backfaceCulling, target)
Suggested optimizations:
If player starts from some known positions ⇒ you must know his initial height, − no need to raycast (or just do one time full mesh slow intersection)
if player moves with small steps ⇒ next raycast will most likely intersect the same face as before.
Optimization #1 − remember previous face, and raycast it first.
if player does not jump ⇒ next raycast will most likely intersect the adjacent face to the face where player was before.
Optimization #2 − build up a cache, so that given a face idx you could retrieve adjacent faces in O(1) time.
This cache may be loaded from the file, if your planet is not generated in real time.
So with my approach on each move you do O(1) read operation from cache and raycast 1-6 faces.
Win!
For a complex object it takes forever. It takes ~200ms for an object with ~1m polys (faces) (1024x512 segments sphere). Does raycasting cast against every single face ?
Out of the box THREE.js does check every triangle when performing a raycast against a mesh and there are no acceleration structures built into THREE.
I've worked with others on the three-mesh-bvh package (github, npm) to help address this problem, though, which may help you get up to the speeds your looking for. Here's how you might use it:
import * as THREE from 'three';
import { MeshBVH, acceleratedRaycast } from 'three-mesh-bvh';
THREE.Mesh.prototype.raycast = acceleratedRaycast;
// ... initialize the scene...
globeMesh.geometry.boundsTree = new MeshBVH(globeMesh.geometry);
// ... initialize raycaster...
// Optional. Improves the performance of the raycast
// if you only need the first collision
raycaster.firstHitOnly = true;
const intersects = raycaster.intersectObject(globeMesh, true);
// do something with the intersections
There are some caveats mentioned in the README so keep those in mind (the mesh index is modified, only nonanimated BufferGeometry is supported, etc). And there's still some memory optimization that could be done but there are some tweakable options to help tune that.
I'll be interested to hear how this works for you! Feel free to leave feedback in the issues on how to improve the package, as well. Hope that helps!
I think you should pre-render the height map of your globe into a texture, assuming your terrain is not dynamic. Read all of it into a typed array, and then whenever your player moves, you only need to back-project her coordinates into that texture, query it, offset and multiply and you should get what you need in O(1) time.
It's up to you how you generate that height map. Actually if you have a bumpy globe, then you should probably start with height map in the first place, and use that in your vertex shader to render the globe (with the input sphere being perfectly smooth). Then you can use the same height map to query the player's Z.
Edit: Danger! This may cause someone's death one day. The edge case I see here is the nearest collision will be not be seen because searchRange will not contain the nearest triangle but will contain the second nearest one returning it as the closest one. I.e. a robotic arm may stop nearby the torso instead of stopping at the arm right in front of it.
anyway
Here's a hack when raycasting not too far from the previous result i.e. during consecutive mousemove events. This will not work for completely random rays
Mesh raycast supports drawRange to limit how many triangles will be searched. Also each raycast result comes with faceIndex telling which triangle was hit. If you're continuously looking for raycasts i.e. with mousemove or there's a laser linearly scanning a mesh you can first search the area nearby* the previous hit.
triangles' distance in the data may look like they're neighbours but it's not guaranteed they are sorted in any way. Still it's very possible that the close ones in the data are close in space.
let lastFaceIndex = null
const searchRange = 2000 * 3
function raycast(mesh, raycaster) {
// limited search
if (lastFaceIndex !== null) {
const drawRange = mesh.geometry.drawRange
drawRange.start = Math.max(0, lastFaceIndex * 3 - searchRange)
drawRange.count = searchRange * 2
const intersects = raycaster.intersectObjects([mesh]);
drawRange.start = 0
drawRange.count = Infinity
if (intersects.length) {
lastFaceIndex = intersects[0].faceIndex
return intersects[0]
}
}
// regular search
const intersects = raycaster.intersectObjects([mesh]);
if (!intersects.length) {
lastFaceIndex = null
return null
}
lastFaceIndex = intersects[0].faceIndex
return intersects[0]
}

Any way to advance along Google Directions route by X minutes?

I have my route from the Google Directions API, it has its start and end and a bunch of points in between. Is there any way to figure out where you would be if you start at one point and follow that route for X minutes?
Basically, is there any way to get a segment of a route with a length of a specific amount of travel time and not just distance?
Let's say (as I presume from your question) that you've got a valid google.maps.DirectionsResult object.
This object has a routes property, which is an object of type google.maps.DirectionsRoute. Usually you will have just one route. Said route will have a legs property which is an array of google.maps.DirectionsLeg.
At this point, you could say that you have a variable legs which is
var legs = myDirectionsResult.routes[0].legs;
There can be one or more legs (according to your waypoints). Each leg will have a steps property, which is an array of google.maps.DirectionsStep.
Each step has properties such as start_location, end_location, duration and distance.
So, finally, you can get the steps of your route as:
var steps = myDirectionsResult
.routes[0]
.legs
.reduce(function(accum,leg) {
accum=accum.concat(leg.steps);
return accum;
},[]);
Now you can iterate over the steps until you reach your time limit:
var currentStep,
duration,
start_location,
end_location,
timeSpent,
totalSteps = steps.length,
timelimit = 900; // seconds.
for(var i=0; i < totalSteps; i++) {
currentStep = steps[i];
duration = currentStep.duration.value;
start_location= currentStep.start_location;
end_location = currentStep.end_location;
if(timeSpent + duration > timelimit) {
break;
}
timeSpent = timeSpent + duration;
}
So let's break this down. You have an array of steps and a time limit. You iterate over the steps. At the beginning of each step you check if completing this step will take your travel time beyond your time limit, in which case you break the loop. Otherwise, you add the duration of the step to your spent time and proceed to the next one.
Since you started declaring that your timelimit was 900 seconds, you will eventually reach a point in which you've spent 850 seconds, and the current step has a duration of 100 seconds, so you cannot complete this one. You've set the starting and ending locations of this step, so you break the loop and you know you can reach somewhere between those two locations until you complete your time limit.
In this case, the step has a duration of 100 seconds and you only have 50 seconds left, so you know you have reached the midpoint between the start and end location of the current step. You need to interpolate between those locations to know the final coordinates. You can do this manually in case you consider that, for small distances, the difference between coordinates behaves like they would on a cartesian plane. (See Small Angle Aproximation) but you could also use the geometry library of google maps (see google.maps.geometry.spherical.interpolate)

Three.js - Spawn an object every x seconds and move each object forward (along Z)

I am making an endless runner style game using Three.js. The basic set up of the scene and idea for the game is a long moving road with cars coming towards you that you have to dodge out of the way of. I am still at the early stages of creating this game, and so my first problem is that I need the hero character (who is dodging the cars) to seem like he is moving forwards, and at the same time have the cars seem like they are moving (faster) towards the hero character.
My thinking was to create road strip objects (the white lines in the middle of a road), and have them move towards the hero character, who is at (0, 0), at a certain speed.
I have successfully created a road strip object and positioned it at the very back of the road (RoadStrip.mesh.position.z = -5000;). Here is my code for that:
var objectRoadStrip = function() {
this.mesh = new THREE.Object3D();
this.mesh.name = "roadStrip";
geomRoadStrip = new THREE.BoxGeometry(20, 11, 300);
matRoadStrip = new THREE.MeshPhongMaterial({color: Colors.white});
RoadStrip = new THREE.Mesh(geomRoadStrip, matRoadStrip);
RoadStrip.name = 'roadStripName';
this.mesh.add(RoadStrip);
}
function createRoadStrip() {
new objectRoadStrip();
RoadStrip.position.y = -72.5;
RoadStrip.position.z = -5000;
scene.add(RoadStrip);
}
In the render() function, which is the function that loops over every frame and is called last to make sure the camera and scene update every frame, I am able to successfully move this strip forwards along the z axis by 10 every time render() is called. I also added some code so that when the RoadStrip touches (0,0), it is removed from the scene. See this below:
function render(){
// moves RoadStrip towards (0,0). When it reaches z = -150, remove that strip from the scene
if (RoadStrip.position.z <= -150) {
RoadStrip.position.z += 10;
} else {
scene.remove(RoadStrip);
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
I have also added the following code to the init() function which creates a RoadStrip when the scene is created, and the continues to create a RoadStrip every 10 seconds (roughly every time the RoadStrip reaches (0,0).
createRoadStrip();
setInterval( function() {
createRoadStrip();
}, 10000);
This is similar to the effect I'm going for, but read The Problem section below where I explain what I truly need.
The Problem
I need to spawn a RoadStrip every x amount of seconds (still to be decided once I get it working, but lets say 3 seconds for now) continuously. Each RoadStrip needs to move towards (0,0) with z += 10 independently. When a RoadStrip instance reaches (0,0), it should be removed from the scene, but other RoadStrips should continue to spawn regardless every 3 seconds at the original position (z = -5000).
My Attempts / Solution Ideas
I've done a lot of reading on this, trawling through code from other people's endless runner games and reading through SO answers but nothing seems to have worked. Below are some of the things I have tried, or some things that I feel would work but I am not doing right/don't have a good understanding of:
Idea: Instead of calling the createRoadStrip() function inside a setInterval, push a RoadStrip object to an array every 3 seconds, and then call that array and move the array along the z axis by += 10.
Possible solution help: I tried changing the setInterval to less than 2 seconds instead of 10 seconds. This caused the RoadStrip to move along the Z axis for 2 seconds as expected, but of course, after 2 seconds another RoadStrip was spawned, and so the first RoadStrip stopped moving along the Z axis, and the new one did instead (for 2 seconds as well) and this process repeated infinitely. This is so close to what I need, but I need each RoadStrip to continue moving, and be remove from the scene when it reaches (0,0)
Thanks for taking the time to read my Question, I look forward to your solutions!
Examples of similar style games: First, Second.
Thanks to #prisoner849 and his link to this thread, I managed to find the solution to the problem, and so I am writing an answer here for anyone who comes across this with the same problem in the future!
I read through the thread and found a link to this JSFiddle, that includes a successful animation similar to the one I was trying to achieve, and I would highly suggest studying the code of that JSFiddle to fully understand how to create an endless runner effect.
Here is a detailed explanation of how to do this:
Instead of infinitely creating objects and have them animate forwards until they reach the end point and disappear (like I originally thought was the right solution), you have to create an array of objects and animate that instead.
Here is my code for doing this:
var roadStripArray = []
function objectRoadStrip() {
for (var i = 0; i < 100; i++) {
geomRoadStrip = new THREE.BoxGeometry(20, 11, 500);
matRoadStrip = new THREE.MeshPhongMaterial({color: Colors.white});
RoadStrip = new THREE.Mesh(geomRoadStrip, matRoadStrip);
RoadStrip.position.set(0, -72.5, -150 - i * 1250);
RoadStrip.receiveShadow = true;
scene.add(RoadStrip);
roadStripArray.push(RoadStrip);
}
}
The for loop has the code i < 100 as my road is quite long and therefore needs a lot of strips
This code:
RoadStrip.position.set(0, -72.5, 0 - i * 1250);
sets the position of each strip to be different from each other, and the number 1250 is the distance between each strip
After creating the objects, you must animate them in the render() function. You have to set them to move along the Z axis, and then create an if statement that says "if any strip reaches the end point (where you want it to disappear), reset it's position back to the start (i.e. the start of the road for me). This means you are constantly looping through your array of objects, and therefore don't infinitely create them.
Here is the code that animates the strips:
// loop that runs every frame to render scene and camera
var clock = new THREE.Clock();
var time = 0;
var delta = 0;
var direction = new THREE.Vector3(0, 0, 1);
var speed = 2000; // units a second - 2 seconds
function render(){
requestAnimationFrame(render);
delta = clock.getDelta();
time += delta;
roadStripArray.forEach(function(RoadStrip){
RoadStrip.position.addScaledVector(direction, speed * delta);
if (RoadStrip.position.z >= 10000) {
RoadStrip.position.z = -10000;
} else {
}
});
renderer.render(scene, camera);
}
The code that moves each strip is:
RoadStrip.position.addScaledVector(direction, speed * delta);
You can read more about .addScaledVector here, but essentially this is the code that animates the strip.
The if statement then checks if the strip touches 10000 (i.e. the end of the road), and if it does, sets the position of that strip to -10000. That strip then moves back towards the end point along the Z axis.
We wrap this all in a forEach function to loop through each RoadStrip in the array and animate them all in the same way. We need to animate them individually so that we can detect when one of them reaches the end of the road.
Thanks, hope this helps!
Usually this kind of scenario is best handled with some kind of particle-system like approach: you don't insert/remove objects continuously to the scene but create a set of objects during initialization, let's say the player can only see 10 road stripes at a time, and your game logic is always moving those 10 stripes, updating positions as needed, once one strip goes out of the field of view, it is recycled at the begining and so on. I don't think you will find a canned solution that does exactly what you are looking for, you would need to come up with the update logic that suits best your game.
I have an example of custom particle system there. Once a particle is getting out of scope, it is made available for the system when it needs to emit a new particle. The number of particle in the pool is always constant and can be defined by the user here just for testing purpose. A similar approach can be used to manipulate your infinite stripes. The repo for that code is available at https://github.com/leefsmp/Particle-System but you can find many other particle system implementations out there, this one is a bit specific to my needs.
Hope that helps.

Three.js / ShaderParticleEngine — SPE.Group.tick bad delta argument?

I'm using the ShaderParticleEngine library for Three.js to create particle emitters.
I picked several code snippets on the Internet to have a working emitter.
Firstly I believed that is wasn't working.
But in fact, the emitter was displayed on the map, but a single motionless particle was on the screen.
After some debugging I undestood that the particle was moving but infinitely slowly. I need to use tick(delta * 1000) to see the emitter in action. And the result is quite ugly (full of gaps, alone particles).I have no problem of low FPS.
The only solution I found is to remove delta argument in the tick function call: particleGroup.tick().
The result is better but is still deceiving, judge by yourself:
Online Emitter Editor:
My result:
I can't understand. I use the same code proposed in the library examples and I use the export feature in the emitter editor.
If I try other variations (eg. on particle life/velocity) I get a very different result in my game, maybe the particle life is not computed correctly because delta argument isn't given?
My game loop:
var animate = function () {
requestAnimationFrame( animate );
render();
stats.update();
};
var render = function() {
time = ctx3d.clock.getElapsedTime();
delta = ctx3d.clock.getDelta();
particleGroup.tick(delta);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}
Delta value loop by loop:
30.0000010000003385357559
9.999985195463523e-7
30.0000020000006770715117
0.0000010000003385357559
30.0000020000006770715117
0.0000010000003385357559
0.0000020000006770715117
30.0000010000003385357559
0.000002999999196617864
0.0000010000003385357559
9.999985195463523e-7
0.000002999999196617864
0.0000010000003385357559
0.000001999998858082108
0.0000010000003385357559
20.0000020000006770715117
9.999985195463523e-7
0.0000010000003385357559
To solve smoothness, try the following:
function makeSmoothSPETick(simulator, timeDelta, maxSubstepSize){
var numSubsteps = Math.floor(timeDelta/maxSubstepSize);
var leftOverTime = timeDelta%maxSubstepSize;
while(numSubsteps-->0){
simulator.tick(maxSubstepSize);
}
if(leftOverTime> 0){
//handle the rest
simulator.tick(leftOverTime);
}
}
If you use this function in your code - it will allow you to essentially subdivide steps that are too large into smaller ones of fixed size. As SquareFeet pointed out, say 16ms for 60FPS - you could use something like this:
var render = function() {
time = ctx3d.clock.getElapsedTime();
delta = ctx3d.clock.getDelta();
makeSmoothSPETick(particleGroup, delta, 0.016);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}
You should get results visually similar to what you'd expect if you were running at smooth 60fps. Beware though, if target hardware can't handle these substeps - you may need to get more logic into your solver algorithm. I'd suggest keeping statistics for past 100 frames or so, and using that to decide how much you can split your incoming step value.
EDIT:
To make sure your timing isn't getting mangled, please try the following:
var lastFrameTime = Date.now()/1000;
var animate = function () {
requestAnimationFrame( animate );
render();
stats.update();
};
var render = function() {
time = Date.now()/1000; //getting current time in seconds since epoch
delta = time-lastFrameTime;
lastFrameTime = time;
particleGroup.tick(delta);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}
I hope posting this as an answer is okay...
I've bumped the particle engine up a minor version to 0.7.7, having implemented a fix for your issue of "not-very-smooth-looking" emitters.
What was happening before was this:
SPE.Emitter.tick() called with a dt value
This tick function determines how many particles should be marked alive based on the dt argument passed to it. For larger dt values, more particles are marked as alive, for smaller values fewer are marked as alice.
The emitter then resets these particles and waits for the next call.
Assuming more than one particle is going to be marked as alive per frame, and they all originate at the same position in space, then all the particles will be at the same place when they're activated. This is why you saw some "clumping" happening.
What happens now is this:
SPE.Emitter.tick() called with a dt value, just as before.
The tick function now determines how many particles should be marked as alive, and whilst marking them so, sets each particles age to be a fraction of the dt value passed in.
So (!), assuming 100 particles are emitted per frame, and a dt value of 0.016 is passed to the emitter's tick function, each of those 100 particles that will be marked as alive is assigned an age value of (0.016 / 100) * i where i is the particle index (in this case, a value of 0 to 100).
I hope that makes sense. You can see the changes here: https://github.com/squarefeet/ShaderParticleEngine/blob/master/src/ShaderParticleEmitter.js#L240-L246
Master branch has been updated.

How to make my slider do something at every specified pixel interval?

I have a nice working slider script (no libraries used). I need a math concept that would allow the slider to perform a callback on a specified pixel interval. So for example, my slider is 300px wide, and I specify the interval=10, then when the user slides the handle, the callback should happen at pixel positions 10,20,30,40 and so on. If the interval=3 then the callback should happen at pixel positions 3,6,9,12...etc.
It's the math formula that I am looking for specifically.
Any help would be appreciated.
Pat
So say you have the variables:
var width = 300; // px
var interval = 10; // px
var pos; // current position
And then you should do:
if ( pos % interval === 0 )
// do stuff
This is called modulus operator which returns the remainder of dividing left_var by right_var. If the remainder is 0 your check will return true, and so you do the callback (exactly at pixel 10, 20, 30, 40, etc.).
But please note that it's very naive to think that you will be able to handle every pixel change... What I'd rather do is to examine if the slider has entered to a given segment.
var segment = 1;
Then after the state of the slider changes you check for position like this:
if ( Math.floor( pos / interval ) === segment ) {
// do stuff because the slider
// has entered a new segment
// ...
segment++;
}
If the slider moves backwards and you want to fire the events accordingly just decrement the segment instead of incrementing.

Categories

Resources