I have my ThreeJS running fine with 4 different animation models moving about in it. However, one of the models when it LERPs freezes the whole scene until it has completed the LERP, but only if the camera has is moving!
So I pulled out the other 3 models out of the scene (2 GLTF
models, and one FBX
model), and it still happens. Therefor it is not the number of models in the scene causing the problem and at this point, I don’t think it’s a performance issue either. I think there is some weird interaction between this one particular model and the ThreeJS r124
LERP code.
I can have the other models LERP after each other, or after the camera even while it is moving the problem does not occur. Any combination performs just fine. But this one model, an FBX model I bought off TurboSquid called the Tarologist Robot has the problem. If it is LERP’ing and while doing so I move the camera, the whole scene freezes until the LERP operation finishes (i.e. - the model has caught up to me and therefore stops LERP’ing). By “freeze” I mean not a single frame renders until the Tarologist Robot stops LERP’ing.
I have two keystrokes in my world that instantly transport me between two different points in the scene that are about 200 virtual units apart in the Z coordinate. If the robot is LERP’ing after me and I hit one of those keys, I don’t see a single frame until the robot is done chasing me (i.e. - chasing the camera). As long as I stand still and don’t move the camera, the robot LERPs over to me just fine, smooth as silk.
Can anyone guess what is happening here and how I might fix it? I would really hate to ditch the model since I really like it.
Here is the animation model page on TurboSquid, in case there are any clues on the page:
It’s an FBX format model but as I said, I have another FBX model in the scene, the Mixamo:samba dancing model, and that model does not have this problem.
Here is my code that does the LERP’ing:
this.animateModel = function () {
const methodName = self.constructor.name + '::' + `animateModel`;
const errPrefix = '(' + methodName + ') ';
if (self.modelAnimationMixer) {
self.modelAnimationMixer.update(g_MyAnimationClock.getDelta());
// Facilitate lerping.
self._doLerp();
}
}
this._doLerp = function () {
const methodName = self.constructor.name + '::' + `_doLerp`;
const errPrefix = '(' + methodName + ') ';
// If we don't have a LERP parameters object or that
// object doesn't have a valid LERP targeting function,
// then we can't LERP.
if (!(self.lerpParamsObj instanceof LerpParameters && typeof self.lerpParamsObj.funcGetTargetPos === 'function'))
return;
if (!self.isLerping)
// LERP'ing is currently suppressed. Just get out.
return;
// Get the current position of what we should chase.
const vecPositionToChase3D = self.lerpParamsObj.funcGetTargetPos(self, self.lerpParamsObj.lerpTargetObj);
// If the target position function returned NULL, then just exit,
// chasing is not desired at the moment.
if (vecPositionToChase3D === null)
return;
// We should have a THREE.Vector3 object.
if (!(vecPositionToChase3D instanceof THREE.Vector3))
throw new Error(`${errPrefix}The value in the vecPositionToChase variable is not NULL, yet it is not a THREE.Vector3 object either.`);
self.vecCameraWorldDirBefore = g_ThreeJsCamera.getWorldDirection(self.vecCameraWorldDirBefore);
if (bVerbose) {
const vecCameraWorldDirAfterStr = vec3ToString(self.vecCameraWorldDirBefore);
console.info(`${errPrefix}self.vecCameraWorldDirBefore: ${vecCameraWorldDirAfterStr}`);
}
// Look at the target.
self.modelInstance.lookAt(vecPositionToChase3D);
// Move towards the target along the direction the robot is now looking
// self.vecWorldToChase3D = self.lerpTargetThreeJsAvatar.threeJsAvatar.getWorldPosition(self.vecWorldToChase3D);
// TODO: Is this right?
self.vecWorldToChase3D = vecPositionToChase3D;
if (bVerbose) {
const vecCameraWorldDirAfterStr = vec3ToString(self.vecCameraWorldDir);
console.info(`${errPrefix}self.vecCameraWorldDir(after)): ${vecCameraWorldDirAfterStr}`);
}
// Stop Lerping when we are at a comfortable distance from the leader.
const trueDistanceForLerp3D = trueDistanceFromObject3D(self.modelInstance, vecPositionToChase3D);
if (bVerbose)
console.info(`${errPrefix} Animation model: "${self.modelName}": trueDistance3D between animation model and target: ${trueDistanceForLerp3D}`);
if (trueDistanceForLerp3D > self.lerpComfortZoneDistance) {
// Not close enough to the target. Lerp to the target.
self.modelInstance.position.lerp(self.vecWorldToChase3D, self.lerpAlpha);
// Clear the LERP target reached flag now that we need to start LERP'ing
// again.
self.isLerpTargetReached = false;
// Restore the last used LERP'ing animation if not done so already.
const useLastLerpAnimationName =
self.lastLerpAnimationName ? self.lastLerpAnimationName : defaultLerpAnimName;
if (self.activeAction.getClip().name !== useLastLerpAnimationName)
self.fadeToAction(useLastLerpAnimationName);
} else {
// Don't execute redundant target-reached events.
if (self.isLerpTargetReached) {
// We are still in the state of having reached the
// target. Do nothing.
} else {
if (bVerbose) {
console.info(`${errPrefix}Animation model: "${self.modelName}":LERP target reached.`);
}
// Set the flag that lets us know we reached our target.
self.isLerpTargetReached = true;
// Save the LERP animation we were using so we can
// resume it if the LERP target moves away from us.
self.lastLerpAnimationName = self.activeAction.getClip().name;
// Do we have any event handlers?
if (self.proximityEventManagerObj.isEmpty()) {
// Execute the default IDLE animation.
self.doDefaultIdleAnimation();
} else {
// Notify all event handler listeners we reached the target.
const proxEventObj = new CommonProximityEvent()
proxEventObj.eventReason = EnumProximityEventType.LERP_FINISHED;
self.proximityEventManagerObj.checkAllEventHandlers(
EnumProximityObjectType.NPC,
self.lerpComfortZoneDistance,
proxEventObj);
}
}
}
}