Reputation: 1413
So, I'm staring myself blind on this problem and the solution is probably dead easy. I just can't see it.
tl;dr: I need to know on which axis my Object3D instance has moved, completely disregarding orientation.
// Init code.
let last_position = new THREE.Vector3(0, 0, 0);
// Update code (called every frame)
if (object.position.distanceTo(last_position) > 0) {
// The object has moved.
let delta = object.position.clone().sub(last_position);
// ... what do I do now?
// - The object can rotate around its Y-axis. (looking around)
// - I'm trying to "remove" the rotation from the delta vector.
// Desired result:
if (delta.z > 0) // Object moved forward!
if (delta.z < 0) // Object moved backward!
if (delta.x > 0) // Object moved right
if (delta.x < 0) // Object moved left
}
last_position = object.position.clone();
Basically, all I'm trying to accomplish is to effectively 'remove' the rotation from the delta vector. It's basically how Object3D.translateX/Y/Z functions work, but instead of updating the object, I'm trying to read these values.
Thanks in advance!
Upvotes: 1
Views: 1073
Reputation: 3025
You need to change the basis of the delta vector from world space to object space (local space).
The THREE.Object3D.matrixWorld
, which is computed every time the scene graph is updated, represent the transformation from object space to world space.
To get the transformation from world space to object space, you need to invert the matrix.
In your case, we're only interested in the rotation/scaling which is the upper-left 3x3 matrix of the matrixWorld
.
So I would do something like this, step by step:
// compute delta ...
// transform delta in local space
var localToWorld = obj.matrixWorld;
var localToWorldRot = new THREE.Matrix3().setFromMatrix4(localToWorld);
var invLocalToWorldRot = new THREE.Matrix3().getInverse(localToWorldRot);
delta.applyMatrix3(invLocalToWorldRot);
Check this fiddle. (Use WASDQE keys)
If your idea is to use this for checking how players move inside a network game so you can play the appropriate animation, then I would say this is the wrong way to do for the following reasons :
Instead, you should consider sending opcodes for strafing/rotating, and using some sort of motion prediction such that the client can see units moving smoothly.
You may want to read this.
Upvotes: 1
Reputation: 3025
I bet what you're looking for is THREE.Object3D.getWorldPosition
.
This function will give you the world position of an object, regardless of its rotation, and the rotation of its parent.
I can think of several ways to do that, here's one of those:
scene.autoUpdate = false
scene.updateMatrixWorld()
Here's the code for computeDelta
:
function computeDelta(obj) {
if (!obj.userData.lastPosition) {
obj.userData.lastPosition = new THREE.Vector3();
obj.getWorldPosition(obj.userData.lastPosition);
}
if (!obj.userData.delta) {
obj.userData.delta = new THREE.Vector3();
}
obj.userData.delta.copy(obj.userData.lastPosition);
obj.getWorldPosition(obj.userData.lastPosition);
return obj.userData.delta.sub(obj.userData.lastPosition).negate();
}
Note that the computations for each object are saved in userData
. It's a bad thing to call THREE.Vector3.clone()
every frame because it will create a new vector.
Check this fiddle.
This function is not really convenient when you have to compute the delta of several objects. In this case, computing the delta in obj.onBeforeRender
would be more appropriate.
Upvotes: 0