Here is a fiddle hopefully demonstrating the effect you want to achieve. It can get quite jittery on different machines - likely because of javascript nuances I'm not familiar with - but I think it's close to what you're looking for.
https://jsfiddle.net/chaosed0/b4cqktan/37/
Here is an explanation of the math involved. We have two constraints:
- Keep the camera at a constant height.
- While the mouse cursor is down, "move" the ground to keep the same location under the cursor at all times.
"Move" is in quotes because we don't want to move the ground, we want to move the camera. Thing is, moving the camera in the opposite direction we would move the ground has the exact same effect, and I find it more intuitive to picture it as if the ground were moving.
With this knowledge, we move on to solving problem #1: what point the mouse is over? This is where the Raycaster might come in handy, but if you have variable-height terrain it won't work - it will break our first constraint above. With that in mind, I've chosen to calculate the intersection point myself.
First, we get the location of the mouse in world-space:
var mouseScreenPos = new THREE.Vector3();
mouseScreenPos.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouseScreenPos.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
var rayOrigin = (new THREE.Vector3(mouseScreenPos.x, mouseScreenPos.y, 0.0)).unproject(camera);
Then we obtain the direction of the ray under the mouse by adding a bit of depth to get a point a bit "more under" the mouse, and subtracting that from the world position:
var rayPos1 = (new THREE.Vector3(mouseScreenPos.x, mouseScreenPos.y, 1.0)).unproject(camera);
var rayDirection = rayPos1.sub(rayOrigin);
Finally, we can use these two points to get the location in space we want to hold constant. For my purposes I'm calculating where the ray intersects the XZ plane at y = -5, but this may differ for you depending on where your objects are located.
var ySlope = (planeY - rayOrigin.y) / rayDirection.y;
var xIntersect = rayDirection.x * ySlope + rayOrigin.x;
var zIntersect = rayDirection.z * ySlope + rayOrigin.z;
return new THREE.Vector3(xIntersect, planeY, zIntersect);
Now, we solve problem #2: how much to move the camera? This ends up being fairly easy now that we have the above formula. The algorithm goes like this:
- When mouse down is detected:
- Record
mouseDownPosition, the position under the mouse (calculated above)
- Record
mouseDownCameraPosition, the current position of the camera
- When a mouse move is detected:
- Obtain the new position under the mouse and subtract it from
mouseDownPosition.
- Subtract this result from
mouseDownCameraPosition.
- Set
camera.position from this new result.