Harold
Harold

Reputation: 1413

ThreeJS: Get object position vector without rotation

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

Answers (2)

neeh
neeh

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 :

  • This is computationally intensive
  • This does not rely on an explicit action based on the player (you might end up with character randomly strafing, because some packets were lost, or delayed)

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

neeh
neeh

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:

  • Disable automatic scene graph update with scene.autoUpdate = false
  • In your render loop:
    1. Update the position of your objects
    2. Update the scene graph with scene.updateMatrixWorld()
    3. Compute the delta position with the following function
    4. Do something with the delta

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

Related Questions