11

I'm playing around with Three.js and WebGL and can't quite get the controls the way I want. I chose to try to "roll my own" controls since Three.js's FirstPersonControls do not use pointer lock.

Anyway, I took most of my code from the built-in FirstPersonControls, converted it to use pointer lock (movementX instead of pageX - offset), but I am having trouble smoothing the look motion.

Here is my onMouseMove (using originalEvent since it is a jquery event):

onMouseMove: function(e) {
    if(!document.pointerLockElement) return;

    var moveX = e.originalEvent.movementX       ||
                    e.originalEvent.mozMovementX    ||
                    e.originalEvent.webkitMovementX ||
                    0,
        moveY = e.originalEvent.movementY       ||
                    e.originalEvent.mozMovementY    ||
                    e.originalEvent.webkitMovementY ||
                    0;

    //Update the mouse movement for coming frames
    this.mouseMovementX = moveX;
    this.mouseMovementY = moveY;
}

And my Controls.update() (called on each animation frame, with the THREE.Clock delta):

update: function(delta) {            
    if(this.freeze) {
        return;
    }

    //movement, works fine
    if(this.moveForward) this.camera.translateZ(-(actualMoveSpeed + this.autoSpeedFactor));
    if(this.moveBackward) this.camera.translateZ(actualMoveSpeed);

    if(this.moveLeft) this.camera.translateX(-actualMoveSpeed);
    if(this.moveRight) this.camera.translateX(actualMoveSpeed);

    /////////
    //ISSUES ARE WITH THIS CODE:
    /////////
    //look movement, really jumpy
    this.lon += this.mouseMovementX;
    this.lat -= this.mouseMovementY;

    this.lat = Math.max(-85, Math.min(85, this.lat));
    this.phi = (90 - this.lat) * Math.PI / 180;
    this.theta = this.lon * Math.PI / 180;

    this.target.x = this.camera.position.x + 100 * Math.sin(this.phi) * Math.cos(this.theta);
    this.target.y = this.camera.position.y + 100 * Math.cos(this.phi);
    this.target.z = this.camera.position.z + 100 * Math.sin(this.phi) * Math.sin(this.theta);

    this.camera.lookAt(this.target);
}

This code does work, but moving the camera is jumpy as the mouse moves around. I could really use some help figuring out how to smooth it.

You can see what I mean by "jumpy" here. I'm new to Three.js, WebGL, and just 3D in general so any help is appreciated.

Thanks,

-Chad


EDIT After working with @przemo_li, here is the working code he came up with:

onMouseMove: function(e) {
    if(!document.pointerLockElement) return;

    var moveX = e.originalEvent.movementX       ||
                    e.originalEvent.mozMovementX    ||
                    e.originalEvent.webkitMovementX ||
                    0,
        moveY = e.originalEvent.movementY       ||
                    e.originalEvent.mozMovementY    ||
                    e.originalEvent.webkitMovementY ||
                    0;

    //Update the initial coords on mouse move
    this.mouseMovementX += moveX; //aggregate mouse movements as a total delta delta
    this.mouseMovementY += moveY;
},
update: function(delta) {            
    if(this.freeze) {
        return;
    }

    //movement
    if(this.moveForward) this.camera.translateZ(-(actualMoveSpeed + this.autoSpeedFactor));
    if(this.moveBackward) this.camera.translateZ(actualMoveSpeed);

    if(this.moveLeft) this.camera.translateX(-actualMoveSpeed);
    if(this.moveRight) this.camera.translateX(actualMoveSpeed);

    //look movement
    this.lon += this.mouseMovementX;
    this.lat -= this.mouseMovementY;

    this.mouseMovementX = 0; //reset mouse deltas to 0 each rendered frame
    this.mouseMovementY = 0;

    this.phi = (90 - this.lat) * Math.PI / 180;
    this.theta = this.lon * Math.PI / 180;

    if(this.constrainVertical) {
        this.phi = THREE.Math.mapLinear(this.phi, 0, Math.PI, this.verticalMin, this.verticalMax);
    }

    this.target.x = this.camera.position.x + 100 * Math.sin(this.phi) * Math.cos(this.theta);
    this.target.y = this.camera.position.y + 100 * Math.cos(this.phi);
    this.target.z = this.camera.position.z + 100 * Math.sin(this.phi) * Math.sin(this.theta);

    this.camera.lookAt(this.target);
}
Community
  • 1
  • 1
Chad
  • 19,219
  • 4
  • 50
  • 73
  • How much FPS do you get? – przemo_li Sep 19 '12 at 18:50
  • @przemo_li It is definately not an FPS issue, I am getting 60FPS and like I mentioned movement works fine. If I WASD around it is perfectly smooth. I just have to make large gestures with the mouse to produce choppy movement. I obviously just don't get the math necessary to do the calculation properly. – Chad Sep 19 '12 at 18:52
  • What range will mouseMovementX/Y take in extremes? – przemo_li Sep 19 '12 at 18:58
  • Looks like the extremes are around the `+-200` range. Though they can be as low as `+-1` or even `0` (no movement). – Chad Sep 19 '12 at 19:04
  • Hmm. Read documentation and check what are maximas. Than scale mouse movement input to your range for lat and lon. Maybe that is cause. Or maybe its just another problem hidden by current one ;) – przemo_li Sep 19 '12 at 19:06
  • Pretty sure it is constrained only by how fast you can move your mouse (and the size of `long`). – Chad Sep 19 '12 at 19:09
  • But than you crop it to mere +/- 85 for lat. Are you sure that math is correct? Also how often will you get mouse movement event? Once per frame (your code can not handle more) ? And 3rd question. Is mouse movement events contain only info about relative movement? – przemo_li Sep 19 '12 at 19:11
  • @przemo_li No, I am not at all; I pulled this math from FirstPersonControls in the three.js code. My question is whether or not the math is correct. – Chad Sep 19 '12 at 19:14
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/16891/discussion-between-chad-and-przemo-li) – Chad Sep 19 '12 at 19:15
  • Hello. A bit late in time, But do you have an example where you used this? – lesolorzanov Oct 06 '15 at 23:45
  • @ZloySmiertniy, Nope! I would use the official controls they have now instead of what I ended up doing anyway. They might have an example in their docs. – Chad Oct 07 '15 at 18:15
  • @Chad It's ok, do you happen to know an application built with Sim.js that uses poinerlockcontrols? – lesolorzanov Oct 07 '15 at 18:50
  • @ZloySmiertniy, never even heard of Sim.js :) – Chad Oct 07 '15 at 19:16
  • @Chad haha :) it is a very small application framework for ThreeJS, I learnt about it in the WebGL Book https://github.com/tparisi/Sim.js I find it helpful to make an organized application/game, to be able to use a model. – lesolorzanov Oct 07 '15 at 20:44

2 Answers2

20

'Official' version just added: https://github.com/mrdoob/three.js/blob/master/examples/js/controls/PointerLockControls.js

Air
  • 5,084
  • 5
  • 25
  • 19
4

1)Constraints? In your code you limit mouse X movement to -|+ 85 Its unlikely that such constraint is needed.

2)Aggregate all events that happen during frame In your code you override mouse movement with each new event. So if you get 3 events during frame only most recent will be stored.

Add those movements. Than after rendering frame you can clear count. And start gathering events again.

przemo_li
  • 3,932
  • 4
  • 35
  • 60