围绕一个点旋转会导致 THREE.js 出现奇怪的故障

Rotation around a point causes weird glitches in THREE.js

我场景中的每个元素都由 3 Object3D 的链组成。父子顺序为cellPivot -> modifier -> setup

setup 的目的是通过调整大小/提供一些必须始终存在的填充来永久对齐加载的对象。设置后不应更改

modifier的目的是真正对对象进行真正的变换

cellPivot 的目的是允许我将 modifier 拖到单元格网格中

为什么需要这一切的一个例子:假设我有一个正交透视的垂直门,我想放在 1x1 space 中,所以我在 x 轴上填充一些以对齐门在中间,类似于下图橙色块是门

因为我想将其移动到地图中的任何单元格,所以我使用 cellPivot 的位置来决定位置。我不能马上使用 modifier 因为有时我想在单元格内旋转模型,这需要同时修改位置和旋转(因为我的模型不是围绕 (0, 0, 0) 构建的,而是沿着 + X 和 +Z)

我通过围绕模型中心(充当枢轴)旋转 modifier 成功地旋转了这些门。下面是执行旋转的函数:

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;
};

旋转门工作的线路:

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

我现在也在尝试对播放器做类似的事情。我记录按下的键,我计算所需方向的向量的法线(如果我按 W 和 D 我会得到(1, 1),如果我只按 W 我会得到(0, 1)),之后我使用以下行来检测用户想要移动的角度:

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

我已经测试过角度是正确的。最重要的是,“围绕枢轴旋转”之前的代码库使用了相同的代码并且运行良好

每当用户真正想去的方向时,我都会运行以下行:

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

如果用户只是按住一个键,那么 abs 将确保没有进行可见的旋转(因为 theta 将是 0

问题来了:每次我按A,不管是不是连着W或者S,角色都会疯狂的旋转。我在上面的行之后添加了以下代码以查看发生了什么:

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

我明白了:

如您所见,x 和 z 正在达到 -pi,而 y 来回摆动。对于不包含密钥 A

的任何其他组合,不会发生这种情况

2天后更新:

我重写了我的函数,如下所示:

我在尝试移动到有问题的位置时在控制台中得到了这些:

正如在第一个日志中看到的那样,我的目标是轮换 0 并打算 -2.35...,但是 rotAfterRot 显示奇怪的结果..:-pi-.78...

这是 运行 this.rotateOnAxis(axis, theta) 的结果。我已经用 this.rotation.y += theta 更改了这一行。现在一切正常:没有奇怪的 -pirotAfterRot.y 实际上是 theta

我的猜测是 rotateOnAxis 也在计算对象的其他特征,比如位置,但仍然无法弄清楚它是如何吐出 -pi