Не работает механика вращения колеса на кватернионах и rotation.order three.js

Надеюсь, я еще не сильно всех тут достал глупыми вопросами, но уже неделю борюсь с механикой вращения движущихся предметов... https://mrmadgav.github.io/wheel/ - gitpages; https://github.com/mrmadgav/wheel/settings - github; Переписал код для одного колеса авто. Две механики пробовал - на кватернионах, и на obj.rotation.order. В первом случае появляется ошибка - object.quaternion.multiplySelf is not a function, хотя в примере работает, туда передается кватернион. Вторая логика заставляет колесо вращаться, но оси все равно съезжают. Сильно прошу помочь может быть с одним из этих способов. С кватернионами совсем всё плохо, уже видео штук 20 посмотрел, но не помогает)

var keyboard = new THREEx.KeyboardState();
    window.onload = function () {
      // Присвоили значения канвасу
      let width = window.innerWidth;
      let height = window.innerHeight;
      let canvas = document.getElementById("canvas");
      canvas.setAttribute("width", width);
      canvas.setAttribute("height", height);
    
      // Рендерер
      let renderer = new THREE.WebGLRenderer({ canvas: canvas });
      renderer.setClearColor(0x000000);
      // Сцена
      let scene = new THREE.Scene();
      // Камера
      let camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 5000);
      camera.position.set(0, 0, 1000);
      // Свет
      let light = new THREE.AmbientLight(0xffffff);
      scene.add(light);
      // Настройки меша
      let geometry = new THREE.TorusGeometry(100, 30, 16, 100);
      let material = new THREE.MeshBasicMaterial({
        color: 0xffffff,
        vertexColors: THREE.FaceColors,
      });
      for (let i = 0; i < geometry.faces.length; i++) {
        geometry.faces[i].color.setRGB(Math.random(), Math.random(), Math.random());
      }
      // Меш
      let mesh = new THREE.Mesh(geometry, material);
      scene.add(mesh);
      mesh = new THREE.Object3D(); // ???
      mesh.rotation.y = Math.PI / 2;
      // mesh.rotation.order = "XYZ";
    
      // var clock = new THREE.Clock();
      // var elapsedTime = 0;
      // var angleSpeed = THREE.Math.degToRad(10);
    
      var rotationx = (rotationy = rotationz = 0);
    
      var _q1 = new THREE.Quaternion(); // CHANGED
      var axisX = new THREE.Vector3(1, 0, 0); // CHANGED
      var axisZ = new THREE.Vector3(0, 0, 1); // CHANGED
    
      function rotateOnAxis(object, axis, angle) {
        // CHANGED
    
        _q1.setFromAxisAngle(axis, angle);
        object.quaternion.multiplySelf(_q1);
      }
    
      window.addEventListener("keydown", function (event) {
        // elapsedTime = clock.getElapsedTime();
        console.log(event.code);
        switch (event.code) {
          case "KeyW":
            rotateOnAxis(mesh, axisX, 0.08); // CHANGED
            // mesh.rotation.x -= 0.5;
            break;
          case "KeyS":
            rotateOnAxis(mesh, axisX, -0.08); // CHANGED
            // mesh.rotation.x += 0.5;
            break;
          case "KeyA":
            rotateOnAxis(mesh, axisZ, 0.08); // CHANGED
            // mesh.rotation.y += Math.sin(elapsedTime) * THREE.Math.degToRad(23);
            break;
          case "KeyD":
            rotateOnAxis(mesh, axisX, -0.08); // CHANGED
            // mesh.rotation.y -= Math.sin(elapsedTime) * THREE.Math.degToRad(23);
            break;
        }
      });
      function loop() {
        renderer.render(scene, camera);
        requestAnimationFrame(function () {
          loop();
        });
      }
      loop();
    };   
      

Ответы (2 шт):

Автор решения: prisoner849

Все вполне работает, если задать порядок вращения "YXZ":

body{
  overflow: hidden;
  margin: 0;
}
<script type="module">
console.clear();

import * as THREE from "https://threejs.org/build/three.module.js";

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(2.5, 6, 6);
camera.lookAt(scene.position);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth,innerHeight);
document.body.appendChild(renderer.domElement);

scene.add(new THREE.GridHelper());

let g = new THREE.CylinderBufferGeometry(2, 2, 0.5, 32);
g.rotateZ(Math.PI * 0.5);
let m = new THREE.MeshBasicMaterial({wireframe: true, color: "red"});
let wheel = new THREE.Mesh(g, m);
wheel.rotation.order = "YXZ";
wheel.position.y = 2;
scene.add(wheel);

document.addEventListener("keydown", event => {
    //console.log(event)
  let val = 0.05;
  let angleTurn = event.key == "ArrowRight" ? -val : event.key == "ArrowLeft" ? val : 0;
  
  wheel.rotation.y = THREE.MathUtils.clamp(
    wheel.rotation.y + angleTurn, 
    -Math.PI * 0.25, 
    Math.PI * 0.25
  );
})


renderer.setAnimationLoop(() => {
    wheel.rotation.x += 0.01;
  renderer.render(scene, camera);
});
</script>

→ Ссылка
Автор решения: Evgeny Serebrennikov
  let leftWheel = car.parent.children[0].children[0].children[0].children[13];
  let rightWheel = car.parent.children[0].children[0].children[0].children[14];
  leftWheel.rotation.order = "YXZ";
  rightWheel.rotation.order = "YXZ";
  console.log(leftWheel.rotation.y);

  document.addEventListener("keydown", (event) => {
    console.log(event);
    let val = 0.05;
    let angleTurn = event.key == "d" ? -val : event.key == "a" ? val : 0;
    event.key == "w"
      ? ((rightWheel.rotation.x += 0.05), (leftWheel.rotation.x += 0.05))
      : event.key == "s"
      ? ((rightWheel.rotation.x -= 0.05), (leftWheel.rotation.x -= 0.05))
      : 0;
    leftWheel.rotation.y = THREE.MathUtils.clamp(
      leftWheel.rotation.y + angleTurn,
      -Math.PI * 0.25,
      Math.PI * 0.25
    );
    rightWheel.rotation.y = THREE.MathUtils.clamp(
      rightWheel.rotation.y + angleTurn,
      -Math.PI * 0.25,
      Math.PI * 0.25
    );
  });
→ Ссылка