diff --git a/examples/lookAtExample.js b/examples/lookAtExample.js new file mode 100644 index 0000000000..6340feda00 --- /dev/null +++ b/examples/lookAtExample.js @@ -0,0 +1,122 @@ +// +// lookAtExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 2/6/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates use of the Camera class's lookAt(), keepLookingAt(), and stopLookingAt() +// features. +// +// To use the script, click on a voxel, and the camera will switch into independent mode and fix it's lookAt on the point +// on the face of the voxel that you clicked. Click again and it will stop looking at that point. While in this fixed mode +// you can use the arrow keys to change the position of the camera. +// +// + +var lookingAtSomething = false; +var oldMode = Camera.getMode(); + +function cancelLookAt() { + if (lookingAtSomething) { + lookingAtSomething = false; + Camera.stopLooking(); + Camera.setMode(oldMode); + releaseMovementKeys(); + } +} + +function captureMovementKeys() { + Controller.captureKeyEvents({ text: "up" }); // Z- + Controller.captureKeyEvents({ text: "down" }); // Z+ + Controller.captureKeyEvents({ text: "UP" }); // Y+ + Controller.captureKeyEvents({ text: "DOWN" }); // Y- + Controller.captureKeyEvents({ text: "left" }); // X+ + Controller.captureKeyEvents({ text: "right" }); // X- +} + +function releaseMovementKeys() { + Controller.releaseKeyEvents({ text: "up" }); // Z- + Controller.releaseKeyEvents({ text: "down" }); // Z+ + Controller.releaseKeyEvents({ text: "UP" }); // Y+ + Controller.releaseKeyEvents({ text: "DOWN" }); // Y- + Controller.releaseKeyEvents({ text: "left" }); // X+ + Controller.releaseKeyEvents({ text: "right" }); // X- +} + +var cameraPosition = Camera.getPosition(); +function moveCamera() { + if (lookingAtSomething) { + Camera.setPosition(cameraPosition); + } +} + +Script.willSendVisualDataCallback.connect(moveCamera); + + +function mousePressEvent(event) { + if (lookingAtSomething) { + cancelLookAt(); + } else { + var pickRay = Camera.computePickRay(event.x, event.y); + var intersection = Voxels.findRayIntersection(pickRay); + if (intersection.intersects) { + + // remember the old mode we were in + oldMode = Camera.getMode(); + + print("looking at intersection point: " + intersection.intersection.x + ", " + + intersection.intersection.y + ", " + intersection.intersection.z); + + // switch to independent mode + Camera.setMode("independent"); + + // tell the camera to fix it's look at on the point we clicked + Camera.keepLookingAt(intersection.intersection); + + // keep track of the fact that we're in this looking at mode + lookingAtSomething = true; + + captureMovementKeys(); + cameraPosition = Camera.getPosition(); + } + } +} +Controller.mousePressEvent.connect(mousePressEvent); + +function keyPressEvent(event) { + if (lookingAtSomething) { + + if (event.text == "ESC") { + cancelLookAt(); + } + + var MOVE_DELTA = 0.5; + + if (event.text == "UP" && !event.isShifted) { + cameraPosition.z -= MOVE_DELTA; + } + if (event.text == "DOWN" && !event.isShifted) { + cameraPosition.z += MOVE_DELTA; + } + if (event.text == "LEFT" && !event.isShifted) { + cameraPosition.x += MOVE_DELTA; + } + if (event.text == "RIGHT" && !event.isShifted) { + cameraPosition.x -= MOVE_DELTA; + } + if (event.text == "UP" && event.isShifted) { + cameraPosition.y += MOVE_DELTA; + } + if (event.text == "DOWN" && event.isShifted) { + cameraPosition.y -= MOVE_DELTA; + } + } +} +Controller.keyPressEvent.connect(keyPressEvent); + +function scriptEnding() { + cancelLookAt(); +} +Script.scriptEnding.connect(scriptEnding); + diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index 87725967b9..694fd30f50 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -58,7 +58,9 @@ Camera::Camera() : _modeShift(1.0f), _linearModeShift(0.0f), _modeShiftRate(1.0f), - _scale(1.0f) + _scale(1.0f), + _lookingAt(0.0f, 0.0f, 0.0f), + _isKeepLookingAt(false) { } @@ -94,8 +96,12 @@ void Camera::updateFollowMode(float deltaTime) { t = 1.0; } - // Update position and rotation, setting directly if tightness is 0.0 + // handle keepLookingAt + if (_isKeepLookingAt) { + lookAt(_lookingAt); + } + // Update position and rotation, setting directly if tightness is 0.0 if (_needsToInitialize || (_tightness == 0.0f)) { _rotation = _targetRotation; _idealPosition = _targetPosition + _scale * (_rotation * glm::vec3(0.0f, _upShift, _distance)); @@ -155,6 +161,15 @@ void Camera::setMode(CameraMode m) { } } +void Camera::setTargetPosition(const glm::vec3& t) { + _targetPosition = t; + + // handle keepLookingAt + if (_isKeepLookingAt) { + lookAt(_lookingAt); + } + +} void Camera::setTargetRotation( const glm::quat& targetRotation ) { _targetRotation = targetRotation; @@ -222,6 +237,19 @@ void Camera::setFrustumWasReshaped() { _frustumNeedsReshape = false; } +void Camera::lookAt(const glm::vec3& lookAt) { + glm::vec3 up = IDENTITY_UP; + glm::mat4 lookAtMatrix = glm::lookAt(_targetPosition, lookAt, up); + glm::quat rotation = glm::quat_cast(lookAtMatrix); + rotation.w = -rotation.w; // Rosedale approved + setTargetRotation(rotation); +} + +void Camera::keepLookingAt(const glm::vec3& point) { + lookAt(point); + _isKeepLookingAt = true; + _lookingAt = point; +} CameraScriptableObject::CameraScriptableObject(Camera* camera, ViewFrustum* viewFrustum) : _camera(camera), _viewFrustum(viewFrustum) diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 075f1a7ed6..b4ba3dbe05 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -37,7 +38,7 @@ public: void setUpShift(float u) { _upShift = u; } void setDistance(float d) { _distance = d; } void setPosition(const glm::vec3& p) { _position = p; } - void setTargetPosition(const glm::vec3& t) { _targetPosition = t; } + void setTargetPosition(const glm::vec3& t); void setTightness(float t) { _tightness = t; } void setTargetRotation(const glm::quat& rotation); @@ -68,6 +69,17 @@ public: bool getFrustumNeedsReshape() const; // call to find out if the view frustum needs to be reshaped void setFrustumWasReshaped(); // call this after reshaping the view frustum. + + // These only work on independent cameras + /// one time change to what the camera is looking at + void lookAt(const glm::vec3& value); + + /// fix what the camera is looking at, and keep the camera looking at this even if position changes + void keepLookingAt(const glm::vec3& value); + + /// stops the keep looking at feature, doesn't change what's being looked at, but will stop camera from + /// continuing to update it's orientation to keep looking at the item + void stopLooking() { _isKeepLookingAt = false; } private: @@ -99,6 +111,9 @@ private: float _linearModeShift; float _modeShiftRate; float _scale; + + glm::vec3 _lookingAt; + bool _isKeepLookingAt; void updateFollowMode(float deltaTime); }; @@ -119,6 +134,17 @@ public slots: void setOrientation(const glm::quat& value) { _camera->setTargetRotation(value); } glm::quat getOrientation() const { return _camera->getRotation(); } + // These only work on independent cameras + /// one time change to what the camera is looking at + void lookAt(const glm::vec3& value) { _camera->lookAt(value);} + + /// fix what the camera is looking at, and keep the camera looking at this even if position changes + void keepLookingAt(const glm::vec3& value) { _camera->keepLookingAt(value);} + + /// stops the keep looking at feature, doesn't change what's being looked at, but will stop camera from + /// continuing to update it's orientation to keep looking at the item + void stopLooking() { _camera->stopLooking();} + PickRay computePickRay(float x, float y); private: