I’m trying to lerp a mesh programmatically, from one point to another. Most importantly, I need to be able to chain these lerps, such that movement from B to C starts after A to B has finished.
In Unity, I’d do this with coroutines, as folows.
IEnumerator Lerp(Transform target, Vector3 start, Vector3 end, float duration)
{
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
target.position = Vector3.Lerp(start, end, elapsed / duration);
yield return null;
}
}
IEnumerator LerpThroughPositions(Transform target, Vector3[] positions, float totalDuration)
{
float individualDuration = totalDuration / positions.Count;
// The code below will chain the individual lerps one after another.
foreach (var position in positions)
{
var lerp = Lerp(target, target.position, position, individualDuration);
yield return StartCoroutine(lerp);
}
}
Unfortunately, my version of Babylon—4.2—doesn’t have the spiffy new coroutines yet, so I’m trying to figure out a workaround.
In Unity, there exists an alternative workflow: that of async/await. You replace yield return null
with await Task.Yield()
and yield return StartCoroutine(Lerp())
with await Lerp()
. Given the popularity of async/await in the world of JavaScript, I was hoping for a similar alternative here.
Sadly, I haven’t been able to figure out how to replicate await Task.Yield()
. I need each “step” of my lerp function to run in each frame, but I don’t know how to do that. The closest I’ve managed is to register it as a callback for scene.onBeforeRenderObservable
:
private static LerpPosition(
target: IPositionable, // Any object with a position member variable.
start: Vector3,
end: Vector3,
duration: number,
scene: Scene
) {
const timer = new Timer(duration); // A custom implementation.
const lerp = () => {
const deltaTime = scene.deltaTime;
if (!deltaTime) return; // I do this check because, sometimes, scene.deltaTime is undefined, and that could lead to NaN propagation.
timer.Increment(deltaTime);
target.position = Vector3.Lerp(start, end, timer.elapsed / duration);
};
scene.onBeforeRenderObservable.add(lerp);
timer.onComplete.addOnce(() =>
scene.onBeforeRenderObservable.removeCallback(lerp)
);
}
This works fine for a single lerp (probably; I haven’t tested it ), but I don’t know how to chain multiple ones together. It may be possible to start a new lerp as a result of the
timer.onComplete
notification, but that’s a recursive solution so clunky it stinks like rotten fish.
Is there not a simpler, more idiomatic way?