javascriptthree.jswebglrender

How i can make the object steady 180 degrees?


i have clothe object where user can rotation the cloth, i am using orbit control to rotation the object here, and the interface, user can change the color of clothe, there is a button to see the back of clothe, so i want to make sure when user click it it gonna show the back on cloth

here is how i animate is with anime js

this is how i init 3D and call function doSmething for show the back of cloth object

let _scene;
let _camera;
let _control;

function init3D() {
    _scene = new THREE.Scene();
    _camera = new THREE.PerspectiveCamera(75, container.value.clientWidth / container.value.clientHeight, 0.1, 1000);
    _control = new OrbitControls(_camera, container.value);
}

init3D()

function doSometing() {
    gltfloader.load(
    url,
     (gltfModelOnLoad) => {
       _scene.add(gltfModelOnLoad.scene);
      
       anime({
          targets: _scene.rotation,
          y: _scene.rotation.y - Math.PI,
          duration: 2000,
          easing: 'easeInOutQuad',
          update: () => {
            renderSceneAndCamera();
          }
        });
   }, 
  undefined, 
  undefined

  )}
}

const renderSceneAndCamera = () => {
  _renderer.render(_scene, _camera);
}


  anime({
          targets: _scene.rotation,
          y: _scene.rotation.y - Math.PI,
          duration: 2000,
          easing: 'easeInOutQuad',
          update: () => {
            renderSceneAndCamera();
          }
        });

when i do this _scene.rotation.y - Math.PI it is not always rotation the back of clothe, also when i am on position 180 degree already, the scene becomes rotation the front of cloth ,

is that any way to make always the object rotation 180 degree what ever the rotation y changed ?


Solution

  • In general, the solution to this problem is not as simple as it may seem... Interfering with the camera with the transformations that @rookie proposes will cause a 180 degree rotation every time, not respecting the user's input via OrbitControls. The solution below is not perfect but it may give you the direction of the output. I assume that the red face of the cube is the front of the shirt. I transformed the camera position to spherical coordinates by blocking the x-axis, because as I understood from your question and comments, you will simply rotate the shirt, something like a 3D gallery, so activating the entire transformation is probably not the best approach. As for the animation itself, always showing the front of the shirt, you can use a quaternion, which will rotate the shirt so that its front (which by default is directed towards the Z axis, example (0, 0, 1)) points towards the camera.

    <script type="importmap">
      {
        "imports": {
          "three": "https://unpkg.com/three@0.170.0/build/three.module.js",
          "three/addons/": "https://unpkg.com/three@0.170.0/examples/jsm/"
        }
      }
    </script>
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.2/anime.min.js" integrity="sha512-aNMyYYxdIxIaot0Y1/PLuEu3eipGCmsEUBrUq+7aVyPGMFH8z0eTP0tkqAvv34fzN6z+201d3T8HPb1svWSKHQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    
    <script type="module">
    import * as THREE from "three";
    import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
    
    const s = new THREE.Scene();
    const c = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    c.position.set(0, 0, 5);
     
    const r = new THREE.WebGLRenderer({ antialias: true });
    r.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(r.domElement);
    const partOfShirt = [
    new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }),
    new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }),
    new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }),
    new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }),
    new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true }), // Front of your shirt
    new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }) 
    ];
    
    const g = new THREE.BoxGeometry();
    const shirt = new THREE.Mesh(g, partOfShirt);
    s.add(shirt);
    
    const controls = new OrbitControls(c, r.domElement);
    controls.enableDamping = true;
    
    controls.addEventListener('change', () => {
    const spherical = new THREE.Spherical();
    spherical.setFromVector3(c.position.clone().sub(controls.target));
    spherical.phi = Math.PI / 2; // "Blocker" camera x
    c.position.copy(controls.target.clone().add(new THREE.Vector3().setFromSpherical(spherical)));
    });
    
    const b = document.createElement('button');
    b.innerText = 'Rotate to Front Face';
    b.style.position = 'absolute';
    b.style.top = '10px';
    b.style.left = '10px';
    b.style.padding = '10px 20px';
    b.style.backgroundColor = '#28a745';
    b.style.color = 'white';
    b.style.border = 'none';
    b.style.cursor = 'pointer';
    b.style.fontSize = '16px';
    document.body.appendChild(b);
    
    b.addEventListener('click', () => {
    const direction = new THREE.Vector3();
    c.getWorldDirection(direction);
    const targetQuaternion = new THREE.Quaternion();
    targetQuaternion.setFromUnitVectors(
        new THREE.Vector3(0, 0, 1),
        direction.negate()
    );
    anime({
        targets: shirt.quaternion,
        x: targetQuaternion.x,
        y: targetQuaternion.y,
        z: targetQuaternion.z,
        w: targetQuaternion.w,
        duration: 2000,
        easing: 'easeInOutQuad',
        update: () => shirt.quaternion.normalize(),
    });
    });
    
    function animate() {
    controls.update();
    r.render(s, c);
    requestAnimationFrame(animate);
    }
    animate();
    
    </script>