Search code examples
three.jsonmousemove

Add max movement to onMouseMove for scene in Three.JS


I have successfully added a really nice mouse movement to a scene in three.js where the camera sits inside the scene, facing one of four walls. Moving the mouse left or right pans and rocks the scene slightly.

If you keep holding the mouse over one side of the screen though, you end up 'flying' outside of the scene and now viewing the entire 3D object from the outside, no longer inside it.

I was wondering how I can update my code below to limit the max amount you can move to the left or right, so that you can't end up outside the scene?

If I can avoid adding another library such as Orbit Controls that would be ideal, but if it's the only way then I will do that. I feel like I have been close with adding some kind of Math.Max to the camera position in my function animate() but haven't had any success so far.

Thanks in advance for any help!

window.addEventListener("load", loadGltf, false);
window.addEventListener("resize", onWindowResize, false);
const progressBar = document.querySelector("progress");

//store our imported glTF scene when loaded successfully
const gltfStore = {};

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
container = document.getElementById( 'container' );



//camera
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);

camera.position.set(0, 0, 0);
camera.lookAt(0, 0, 5);

windowHalfX = window.innerWidth / 2,
        windowHalfY = window.innerHeight / 2,
        mouseX = 0,
        mouseY = 0;

//re-establish camera view on window resize
function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

//lighting
const lightFill = new THREE.PointLight(0xffffff, 1, 500);
lightFill.position.set(0, 0, 5);
scene.add(lightFill);

const lightKey = new THREE.PointLight(0xffffff, 1, 500);
lightKey.position.set(20, 0, 20);
scene.add(lightKey);

const loader = new THREE.GLTFLoader();


function loadGltf() {
  loader.load(
    "<?php echo get_template_directory_uri();?>/objects/SM_LookDev_TextureTest_FromFBX.glb",
    //onLoad
    function(gltf) {
      scene.add(gltf.scene);
       mesh = gltf.scene;
      //set material emissive to 0
      gltf.scene.children[0];
      gltfStore.scene = gltf.scene;
       document.addEventListener("mousemove", onMouseMove);

    }

  );

  container.appendChild(renderer.domElement); 

// Mouse movement

function onMouseMove(event) {

          mouseX = (event.clientX - windowHalfX) / 10;
          mouseY = (event.clientY - windowHalfY) / 30;
}


function getMouseX(event) {
    if (event.type.indexOf("touch") == -1)
        return event.clientX;
    else
        return event.touches[0].clientX;
}

function getMouseY(event) {
    if (event.type.indexOf("touch") == -1)
        return event.clientY;
    else
        return event.touches[0].clientY;
}

  function animate() {
    requestAnimationFrame(animate);


    camera.position.x += (mouseX - camera.position.x) * .0005;
        camera.position.y += (-mouseY - camera.position.y) * .0003;
        camera.lookAt(0,0,10);
    renderer.render(scene, camera);

  }



  animate();


}

Solution

  • Three.js comes with some utility methods in its MathUtils class that can help with this. You could use THREE.MathUtils.clamp to limit the minimum and maximum values of mouseX and mouseY:

    function onMouseMove(event) {
        mouseX = THREE.MathUtils.clamp((event.clientX - windowHalfX) / 10, -20, 20);
        mouseY = THREE.MathUtils.clamp((event.clientY - windowHalfY) / 30, -10, 10);
    }
    

    The min & max values of -20, 20, etc depend on the bounds of your .gltf object, so you'll have to adjust those accordingly.

    This is just a helper method that uses JavaScript's native Math.min() and Math.max() internally to "clamp" the value.

    Notice for r112 and lower:

    Before Three.js revision 113, the THREE.MathUtils class used to be called THREE.Math but this could cause a conflict with JavaScript's native Math class. Make sure you're using the correct name based on the version of Three.js you're using, see here for more details.