661 lines · 18.3 KB · JavaScript
RawDownload
////  entityCameraTool.js//  examples////  Created by Ryan Huffman on 10/14/14.//  Copyright 2014 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// Script.include("overlayUtils.js"); var MOUSE_SENSITIVITY = 0.9;var SCROLL_SENSITIVITY = 0.05;var PAN_ZOOM_SCALE_RATIO = 0.4; var KEY_ORBIT_SENSITIVITY = 90;var KEY_ZOOM_SENSITIVITY = 3; // Scaling applied based on the size of the object being focused (Larger values focus further away)var FOCUS_ZOOM_SCALE = 2.3; // Minimum zoom level when focusing on an objectvar FOCUS_MIN_ZOOM = 0.5; // Scaling applied based on the current zoom levelvar ZOOM_SCALING = 0.02; var MIN_ZOOM_DISTANCE = 0.01; // The maximum usable zoom level is somewhere around 14km, further than that the edit handles will fade-out. (FIXME: MS17493)var MAX_ZOOM_DISTANCE = 14000; var MODE_INACTIVE = 'inactive';var MODE_ORBIT = 'orbit';var MODE_PAN = 'pan'; var EASING_MULTIPLIER = 8; var INITIAL_ZOOM_DISTANCE = 2;var INITIAL_ZOOM_DISTANCE_FIRST_PERSON = 3; var easeOutCubic = function(t) {    t--;    return t * t * t + 1;}; EASE_TIME = 0.5; function clamp(value, minimum, maximum) {    return Math.min(Math.max(value, minimum), maximum);} function mergeObjects(obj1, obj2) {    var newObj = {};    for (key in obj1) {        newObj[key] = obj1[key];    }    for (key in obj2) {        newObj[key] = obj2[key];    }    return newObj;} CameraManager = function() {    var that = {};     that.enabled = false;    that.mode = MODE_INACTIVE;    var EDIT_CAMERA_MANAGER_CHANNEL = "Edit-Camera-Manager-Channel";    Messages.sendLocalMessage(EDIT_CAMERA_MANAGER_CHANNEL, JSON.stringify({ enabled: false }));     var actions = {        orbitLeft: 0,        orbitRight: 0,        orbitUp: 0,        orbitDown: 0,        orbitForward: 0,        orbitBackward: 0,    }     var keyToActionMapping = {        65: "orbitLeft",     // "a"        68: "orbitRight",    // "d"        87: "orbitForward",  // "w"        83: "orbitBackward", // "s"        69: "orbitUp",       // "e"        67: "orbitDown",     // "c"                16777234: "orbitLeft",     // "LEFT"        16777236: "orbitRight",    // "RIGHT"        16777235: "orbitForward",  // "UP"        16777237: "orbitBackward", // "DOWN"    }     var CAPTURED_KEYS = [];    for (var key in keyToActionMapping) {        CAPTURED_KEYS.push(key);    }     function getActionForKeyEvent(event) {        if (!event.isControl) {            var action = keyToActionMapping[event.key];            if (action !== undefined) {                if (event.isShifted) {                    if (action === "orbitForward") {                        action = "orbitUp";                    } else if (action === "orbitBackward") {                        action = "orbitDown";                    }                }                return action;            }        }        return null;    }     that.zoomDistance = INITIAL_ZOOM_DISTANCE;    that.targetZoomDistance = INITIAL_ZOOM_DISTANCE;     that.yaw = 0;    that.pitch = 0;    that.targetYaw = 0;    that.targetPitch = 0;     that.focalPoint = Vec3.ZERO;    that.targetFocalPoint = Vec3.ZERO;     easing = false;    easingTime = 0;    startOrientation = Quat.IDENTITY;     that.previousCameraMode = null;     that.lastMousePosition = {        x: 0,        y: 0    };     that.enable = function() {        if (Camera.mode === "independent" || that.enabled || HMD.active) {            return;        }         for (var i = 0; i < CAPTURED_KEYS.length; i++) {            Controller.captureKeyEvents({                text: CAPTURED_KEYS[i]            });        }         for (var action in actions) {            actions[action] = 0;        }         that.enabled = true;        that.mode = MODE_INACTIVE;        Messages.sendLocalMessage(EDIT_CAMERA_MANAGER_CHANNEL, JSON.stringify({ enabled: true }));         // Pick a point INITIAL_ZOOM_DISTANCE in front of the camera to use as a focal point        that.zoomDistance = INITIAL_ZOOM_DISTANCE;        that.targetZoomDistance = that.zoomDistance + 3.0;        var focalPoint = Vec3.sum(Camera.getPosition(),            Vec3.multiply(that.zoomDistance, Quat.getForward(Camera.getOrientation())));         // Determine the correct yaw and pitch to keep the camera in the same location        var dPos = Vec3.subtract(focalPoint, Camera.getPosition());        var xzDist = Math.sqrt(dPos.x * dPos.x + dPos.z * dPos.z);         that.targetPitch = -Math.atan2(dPos.y, xzDist) * 180 / Math.PI;        that.targetPitch += (90 - that.targetPitch) / 3.0; // Swing camera "up" to look down at the focal point        that.targetYaw = Math.atan2(dPos.x, dPos.z) * 180 / Math.PI;        that.pitch = that.targetPitch;        that.yaw = that.targetYaw;         that.focalPoint = focalPoint;        that.setFocalPoint(focalPoint);        that.previousCameraMode = Camera.mode;        Camera.mode = "independent";         that.updateCamera();         cameraTool.setVisible(true);    }     that.disable = function(ignoreCamera) {        if (!that.enabled) {            return;        }         for (var i = 0; i < CAPTURED_KEYS.length; i++) {            Controller.releaseKeyEvents({                text: CAPTURED_KEYS[i]            });        }         that.enabled = false;        that.mode = MODE_INACTIVE;        Messages.sendLocalMessage(EDIT_CAMERA_MANAGER_CHANNEL, JSON.stringify({ enabled: false }));         if (!ignoreCamera) {            Camera.mode = that.previousCameraMode;        }        cameraTool.setVisible(false);    }     that.focus = function(position, dimensions, easeOrientation) {        if (dimensions) {            var size = Math.max(dimensions.x, Math.max(dimensions.y, dimensions.z));            that.targetZoomDistance = Math.max(size * FOCUS_ZOOM_SCALE, FOCUS_MIN_ZOOM);        } else {            that.targetZoomDistance = Vec3.length(Vec3.subtract(Camera.getPosition(), position));        }         if (easeOrientation) {            // Do eased turning towards target            that.focalPoint = that.targetFocalPoint = position;             that.zoomDistance = that.targetZoomDistance = Vec3.length(Vec3.subtract(Camera.getPosition(), position));             var dPos = Vec3.subtract(that.focalPoint, Camera.getPosition());            var xzDist = Math.sqrt(dPos.x * dPos.x + dPos.z * dPos.z);             that.targetPitch = -Math.atan2(dPos.y, xzDist) * 180 / Math.PI;            that.targetYaw = Math.atan2(dPos.x, dPos.z) * 180 / Math.PI;            that.pitch = that.targetPitch;            that.yaw = that.targetYaw;             startOrientation = Camera.getOrientation();             easing = true;            easingTime = 0;        } else {            that.setFocalPoint(position);        }         that.updateCamera();    }     that.setTargetPitchYaw = function(pitch, yaw) {        that.targetPitch = pitch;        that.targetYaw = yaw;    }     that.moveFocalPoint = function(dPos) {        that.setFocalPoint(Vec3.sum(that.focalPoint, dPos));    }     that.setFocalPoint = function(pos) {        that.targetFocalPoint = pos;        that.updateCamera();    }     that.addYaw = function(yaw) {        that.targetYaw += yaw;        that.updateCamera();    }     that.addPitch = function(pitch) {        that.targetPitch += pitch;        that.updateCamera();    }     that.addZoom = function(zoom) {        zoom *= that.targetZoomDistance * ZOOM_SCALING;        that.targetZoomDistance = Math.min(Math.max(that.targetZoomDistance + zoom, MIN_ZOOM_DISTANCE), MAX_ZOOM_DISTANCE);        that.updateCamera();    }     that.pan = function(offset) {        var up = Quat.getUp(Camera.getOrientation());        var right = Quat.getRight(Camera.getOrientation());         up = Vec3.multiply(up, offset.y * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance);        right = Vec3.multiply(right, offset.x * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance);         var dPosition = Vec3.sum(up, right);         that.moveFocalPoint(dPosition);    }     that.mouseMoveEvent = function(event) {        if (that.enabled && that.mode !== MODE_INACTIVE) {            var x = Reticle.getPosition().x;            var y = Reticle.getPosition().y;            if (!hasDragged) {                that.lastMousePosition.x = x;                that.lastMousePosition.y = y;                hasDragged = true;            }            if (that.mode === MODE_ORBIT) {                var diffX = x - that.lastMousePosition.x;                var diffY = y - that.lastMousePosition.y;                that.targetYaw -= MOUSE_SENSITIVITY * (diffX / 5.0);                that.targetPitch += MOUSE_SENSITIVITY * (diffY / 10.0);                 while (that.targetYaw > 180.0) that.targetYaw -= 360;                while (that.targetYaw < -180.0) that.targetYaw += 360;                 if (that.targetPitch > 90) that.targetPitch = 90;                if (that.targetPitch < -90) that.targetPitch = -90;                 that.updateCamera();            } else if (that.mode === MODE_PAN) {                var diffX = x - that.lastMousePosition.x;                var diffY = y - that.lastMousePosition.y;                 var up = Quat.getUp(Camera.getOrientation());                var right = Quat.getRight(Camera.getOrientation());                 up = Vec3.multiply(up, diffY * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance);                right = Vec3.multiply(right, -diffX * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance);                 var dPosition = Vec3.sum(up, right);                 that.moveFocalPoint(dPosition);            }             var newX = x;            var newY = y;            var updatePosition = false;             if (x <= 0) {                newX = Window.innerWidth;                updatePosition = true;            } else if (x >= Window.innerWidth) {                newX = 0;                updatePosition = true;            }             if (y <= 0) {                newY = Window.innerHeight;                updatePosition = true;            } else if (y >= Window.innerHeight) {                newY = 0;                updatePosition = true;            }             if (updatePosition) {                Reticle.setPosition({ x: newX, y: newY});            }             that.lastMousePosition.x = newX;            that.lastMousePosition.y = newY;             return true;        }        return false;    }     var hasDragged = false;    that.mousePressEvent = function(event) {         if (cameraTool.mousePressEvent(event)) {            return true;        }         if (!that.enabled) {            return;        }         if (event.isRightButton || (event.isLeftButton && event.isAlt && !event.isShifted)) {            that.mode = MODE_ORBIT;        } else if (event.isMiddleButton || (event.isLeftButton && event.isControl && event.isShifted)) {            that.mode = MODE_PAN;        }         if (that.mode !== MODE_INACTIVE) {            hasDragged = false;            return true;        }         return false;    }     that.mouseReleaseEvent = function(event) {         if (!that.enabled) {            return;        }         that.mode = MODE_INACTIVE;        Reticle.setVisible(true);     }     that.keyPressEvent = function(event) {        var action = getActionForKeyEvent(event);        if (action) {            actions[action] = 1;        }    };     that.keyReleaseEvent = function(event) {        var action = getActionForKeyEvent(event);        if (action) {            actions[action] = 0;        }    };     that.wheelEvent = function(event) {        if (!that.enabled) {            return;        }         var dZoom = -event.delta * SCROLL_SENSITIVITY;         // Scale based on current zoom level        dZoom *= that.targetZoomDistance * ZOOM_SCALING;         that.targetZoomDistance = Math.min(Math.max(that.targetZoomDistance + dZoom, MIN_ZOOM_DISTANCE), MAX_ZOOM_DISTANCE);         that.updateCamera();    }     that.updateCamera = function() {        if (!that.enabled || Camera.mode !== "independent") {            cameraTool.update();            return;        }         var yRot = Quat.angleAxis(that.yaw, {            x: 0,            y: 1,            z: 0        });        var xRot = Quat.angleAxis(that.pitch, {            x: 1,            y: 0,            z: 0        });        var q = Quat.multiply(yRot, xRot);         var pos = Vec3.multiply(Quat.getForward(q), that.zoomDistance);        Camera.setPosition(Vec3.sum(that.focalPoint, pos));         yRot = Quat.angleAxis(that.yaw - 180, {            x: 0,            y: 1,            z: 0        });        xRot = Quat.angleAxis(-that.pitch, {            x: 1,            y: 0,            z: 0        });        q = Quat.multiply(yRot, xRot);         if (easing) {            var t = easeOutCubic(easingTime / EASE_TIME);            q = Quat.slerp(startOrientation, q, t);        }         Camera.setOrientation(q);         cameraTool.update();    }     function normalizeDegrees(degrees) {        while (degrees > 180) {            degrees -= 360;        }        while (degrees < -180) {            degrees += 360;        }        return degrees;    }     // Ease the position and orbit of the camera    that.update = function(dt) {        if (Camera.mode !== "independent") {            that.updateCamera();            return;        }         // Update based on current actions        that.targetYaw += (actions.orbitRight - actions.orbitLeft) * dt * KEY_ORBIT_SENSITIVITY;        that.targetPitch += (actions.orbitUp - actions.orbitDown) * dt * KEY_ORBIT_SENSITIVITY;        that.targetPitch = clamp(that.targetPitch, -90, 90);         var dZoom = actions.orbitBackward - actions.orbitForward;        if (dZoom) {            dZoom *= that.targetZoomDistance * dt * KEY_ZOOM_SENSITIVITY;            that.targetZoomDistance += dZoom;            that.targetZoomDistance = clamp(that.targetZoomDistance, MIN_ZOOM_DISTANCE, MAX_ZOOM_DISTANCE);        }         if (easing) {            easingTime = Math.min(EASE_TIME, easingTime + dt);        }         var scale = Math.min(dt * EASING_MULTIPLIER, 1.0);         var dYaw = normalizeDegrees(that.targetYaw - that.yaw);        var dPitch = normalizeDegrees(that.targetPitch - that.pitch);         that.yaw += scale * dYaw;        that.pitch += scale * dPitch;         // Normalize between [-180, 180]

Lines 1–500 of 661.