Search code examples
javascriptnode.jsthree.jsrotationtransform

Rotation around a point causes weird glitches in THREE.js


Every element from my scene is made of a chain of 3 Object3Ds. The order parent-to-child is cellPivot -> modifier -> setup

setup's purpose is to permanently align a loaded object by resizing / giving some padding that must always be there. It is not supposed to be changed once set

modifier's purpose is to actually perform the real transformation on the object

cellPivot's purpose is to allow me to drag modifier into a cell grid

An example why all this is needed: let's say I have a vertical door in an orthographic perspective that I wanna fit in a 1x1 space, so I give some padding on the x-axis to align the door in the center, similar to the picture below where the orange block is the door

enter image description here

Since I want to move this in any cell in the map, I use cellPivot's position to decide where. I can't use right away modifier since sometimes I wanna rotate the model inside the cell, which requires to modify both position and rotation (since my models are not built around (0, 0, 0), but along +X and +Z)

I have succesfully managed to rotate these doors by rotating modifier around the center of the model (which acts as a pivot). Here's the functions that does the rotation:

three.Object3D.prototype.pivot = function(pivot, f) {
  pivot = lib.VecToVector3(three, pivot); // just a conversion between libs
  this.position.sub(pivot);
  f(this);
  this.position.add(pivot);
  return this;
};
three.Object3D.prototype.pivotRotate = function(pivot, axis, theta, rotational = false, abs = false) {
  if(abs)
    theta -= this.rotation.y; /// not good, handles only y
  this.pivot(pivot, () => this.position.applyAxisAngle(axis, theta));
  if(rotational)
    this.rotateOnAxis(axis, theta);
  return this;
};

The line that rotates the door and works:

this.o3d.userData.modifier.pivotRotate(this.o3d.userData.center, new three.Vector3(0, 1, 0), this.rot, true);

I'm now trying to do something similar with the player too. I record what keys are pressed, I calculate the normal of the vector of desired direction (if I press W and D I'll get (1, 1), if I press just W I'll get (0, 1)), after which I use the following line to detect the angle at which the user wanna move:

Math.atan2(-normal[1], normal[0]);

I have already tested that the angle is correct. On top of that, the codebase before "rotating around a pivot" used the same code and it worked fine

Everytime there's actually a direction the user wanna go, I'll run the following line:

this.o3d.userData.modifier.pivotRotate(this.o3d.userData.center, new three.Vector3(0, 1, 0), Math.atan2(-normal[1], normal[0]), true, true);

If the user just keeps a key pressed, then abs will make sure that no visible rotation is made (since theta will be 0)

Here's the problem: everytime I press A, be it in combination with W or S or not, the character will rotate like insane. I put after the line from above the following code to see what's happening:

com.log(new three.Euler().setFromQuaternion(this.o3d.userData.setup.getWorldQuaternion(new three.Quaternion())));

I'm getting this:

enter image description here

As you can see, x and z are reaching -pi, and y bouces back and forth. This does not happen for any other combination that does not contain key A


Solution

  • Update after 2 days:

    I have rewrote my function like this: enter image description here

    I got these in console while trying to move in the problematic positions: enter image description here

    As it can be seen in the first log, my target is at rotation 0 and is going for -2.35..., but rotAfterRot is showing weird results..: -pi and -.78...

    This is the result of running this.rotateOnAxis(axis, theta). I have changed this exact line with this.rotation.y += theta. Now everything is working as it should be: no weird -pi and rotAfterRot.y is actually theta

    My guess is that rotateOnAxis is also counting other features of the object, like position, but still can't figure how it spits that -pi