Karol SuprynowiczFix Uuid.NULL behaviord38ebbd2 years agoHistory
1162 lines · 43.4 KB · JavaScript
RawDownload
////  miniTablet.js////  Created by David Rowe on August 9th, 2018.//  Copyright 2018 High Fidelity, Inc.//  Copyright 2023 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// /* global getTabletWidthFromSettings, handsAreTracked, TRIGGER_OFF_VALUE, Controller, Script, Camera, Tablet, MyAvatar,   Quat, SoundCache, HMD, Vec3, Uuid, Messages */ (function () {     "use strict";     Script.include("./libraries/utils.js");    Script.include("./libraries/controllerDispatcherUtils.js");     var controllerStandard = Controller.Standard;     var UI,        ui = null,        State,        miniState = null,        miniTabletEnabled = true,         // Hands.        NO_HAND = -1,        LEFT_HAND = 0,        RIGHT_HAND = 1,        HAND_NAMES = ["LeftHand", "RightHand"],         // Track controller grabbing state.        HIFI_OBJECT_MANIPULATION_CHANNEL = "Hifi-Object-Manipulation",        grabbingHand = NO_HAND,        grabbedItem = null,         // Miscellaneous.        tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"),        DEBUG = false;      function debug(message) {        if (!DEBUG) {            return;        }        print("DEBUG: " + message);    }     function error(message) {        print("ERROR: " + message);    }     function handJointName(hand) {        var jointName;        if (hand === LEFT_HAND) {            if (Camera.mode === "first person" || Camera.mode === "first person look at") {                jointName = "_CONTROLLER_LEFTHAND";            } else if (Camera.mode === "third person") {                jointName = "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND";            } else {                jointName = "LeftHand";            }        } else {            if (Camera.mode === "first person" || Camera.mode === "first person look at") {                jointName = "_CONTROLLER_RIGHTHAND";            } else if (Camera.mode === "third person") {                jointName = "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND";            } else {                jointName = "RightHand";            }        }        return jointName;    }     function handJointIndex(hand) {        return MyAvatar.getJointIndex(handJointName(hand));    }     function otherHand(hand) {        return hand === LEFT_HAND ? RIGHT_HAND : LEFT_HAND;    }     UI = function () {         if (!(this instanceof UI)) {            return new UI();        }         var // Base overlay            miniOverlay = null,            MINI_MODEL = Script.resolvePath("./assets/models/miniTabletBlank.fbx"),            MINI_DIMENSIONS = { x: 0.0637, y: 0.0965, z: 0.0045 }, // Proportional to tablet proper.            MINI_POSITIONS = [                {                    x: -0.01, // Distance across hand.                    y: 0.08, // Distance from joint.                    z: 0.06 // Distance above palm.                },                {                    x: 0.01, // Distance across hand.                    y: 0.08, // Distance from joint.                    z: 0.06 // Distance above palm.                }            ],            DEGREES_180 = 180,            MINI_PICTH = 40,            MINI_ROTATIONS = [                Quat.fromVec3Degrees({ x: 0, y: DEGREES_180 - MINI_PICTH, z: 90 }),                Quat.fromVec3Degrees({ x: 0, y: DEGREES_180 + MINI_PICTH, z: -90 })            ],             // UI overlay.            uiHand = LEFT_HAND,            miniUIOverlay = null,            MINI_UI_HTML = Script.resolvePath("./html/miniTablet.html"),            MINI_HAND_UI_HTML = Script.resolvePath("./html/miniHandsTablet.html"),            MINI_UI_DIMENSIONS = { x: 0.059, y: 0.0865, z: 0.001 },            MINI_UI_WIDTH_PIXELS = 150,            METERS_TO_INCHES = 39.3701,            MINI_UI_DPI = MINI_UI_WIDTH_PIXELS / (MINI_UI_DIMENSIONS.x * METERS_TO_INCHES),            MINI_UI_OFFSET = 0.001, // Above model surface.            MINI_UI_LOCAL_POSITION = { x: 0.0002, y: 0.0024, z: -(MINI_DIMENSIONS.z / 2 + MINI_UI_OFFSET) },            MINI_UI_LOCAL_ROTATION = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }),            miniUIOverlayEnabled = false,            MINI_UI_OVERLAY_ENABLED_DELAY = 500,            miniOverlayObject = null,             // Button icons.            MUTE_ON_ICON = Script.resourcesPath() + "icons/tablet-icons/mic-mute-a.svg",            MUTE_OFF_ICON = Script.resourcesPath() + "icons/tablet-icons/mic-unmute-i.svg",            GOTO_ICON = Script.resourcesPath() + "icons/tablet-icons/goto-i.svg",             // Expansion to tablet.            MINI_EXPAND_HANDLES = [ // Normalized coordinates in range [-0.5, 0.5] about center of mini tablet.                { x: 0.5, y: -0.65, z: 0 },                { x: -0.5, y: -0.65, z: 0 }            ],            MINI_EXPAND_DELTA_ROTATION = Quat.fromVec3Degrees({ x: -5, y: 0, z: 0 }),            MINI_EXPAND_HANDLES_OTHER = [ // Different handles when expanding after being grabbed by other hand,                { x: 0.5, y: -0.4, z: 0 },                { x: -0.5, y: -0.4, z: 0 }            ],            MINI_EXPAND_DELTA_ROTATION_OTHER = Quat.IDENTITY,            miniExpandHand,            miniExpandHandles = MINI_EXPAND_HANDLES,            miniExpandDeltaRotation = MINI_EXPAND_HANDLES_OTHER,            miniExpandLocalPosition,            miniExpandLocalRotation = Quat.IDENTITY,            miniInitialWidth,            miniTargetWidth,            miniTargetLocalRotation,             // Laser pointing at.            isBodyHovered = false,             // EventBridge.            READY_MESSAGE = "ready", // Engine <== Dialog            HOVER_MESSAGE = "hover", // Engine <== Dialog            UNHOVER_MESSAGE = "unhover", // Engine <== Dialog            MUTE_MESSAGE = "mute", // Engine <=> Dialog            GOTO_MESSAGE = "goto", // Engine <=> Dialog            EXPAND_MESSAGE = "expand", // Engine <== Dialog             // Sounds.            HOVER_SOUND = "./assets/sounds/button-hover.wav",            HOVER_VOLUME = 0.5,            CLICK_SOUND = "./assets/sounds/button-click.wav",            CLICK_VOLUME = 0.8,            hoverSound = SoundCache.getSound(Script.resolvePath(HOVER_SOUND)),            clickSound = SoundCache.getSound(Script.resolvePath(CLICK_SOUND));          function updateMutedStatus() {            if (miniOverlayObject) {                var isMuted = Audio.muted;                miniOverlayObject.emitScriptEvent(JSON.stringify({                    type: MUTE_MESSAGE,                    on: isMuted,                    icon: isMuted ? MUTE_ON_ICON : MUTE_OFF_ICON                }));            }        }         function setGotoIcon() {            if (miniOverlayObject) {                miniOverlayObject.emitScriptEvent(JSON.stringify({                    type: GOTO_MESSAGE,                    icon: GOTO_ICON                }));            }        }         function onWebEventReceived(data) {            var message;             try {                message = JSON.parse(data);            } catch (e) {                console.error("EventBridge message error");                return;            }             switch (message.type) {                case READY_MESSAGE:                    // Send initial button statuses.                    updateMutedStatus();                    setGotoIcon();                    break;                case HOVER_MESSAGE:                    if (message.target === "body") {                        // Laser status.                        isBodyHovered = true;                    } else if (message.target === "button") {                        // Audio feedback.                        playSound(hoverSound, HOVER_VOLUME);                    }                    break;                case UNHOVER_MESSAGE:                    if (message.target === "body") {                        // Laser status.                        isBodyHovered = false;                    }                    break;                case MUTE_MESSAGE:                    // Toggle mute.                    playSound(clickSound, CLICK_VOLUME);                    Audio.muted = !Audio.muted;                    break;                case GOTO_MESSAGE:                    // Goto.                    playSound(clickSound, CLICK_VOLUME);                    miniState.setState(miniState.MINI_EXPANDING, { hand: uiHand, goto: true });                    break;                case EXPAND_MESSAGE:                    // Expand tablet;                    playSound(clickSound, CLICK_VOLUME);                    miniState.setState(miniState.MINI_EXPANDING, { hand: uiHand, goto: false });                    break;            }        }          function updateMiniTabletID() {            HMD.miniTabletID = miniOverlay;            HMD.miniTabletScreenID = miniUIOverlay;            HMD.miniTabletHand = miniOverlay ? uiHand : -1;        }         function playSound(sound, volume) {            Audio.playSound(sound, {                position: uiHand === LEFT_HAND ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(),                volume: volume,                localOnly: true            });        }          function getUIPosition(hand) {            return MINI_POSITIONS[hand];        }         function getMiniTabletID() {            return miniOverlay;        }         function getMiniTabletProperties() {            var properties = Entities.getEntityProperties(miniOverlay, ["position", "orientation"]);            return {                position: properties.position,                orientation: properties.orientation            };        }         function isLaserPointingAt() {            return isBodyHovered;        }          function show(hand) {            var initialScale = 0.01; // Start very small.             uiHand = hand;             Entities.editEntity(miniOverlay, {                "parentID": MyAvatar.SELF_ID,                "parentJointIndex": handJointIndex(hand),                "localPosition": Vec3.multiply(MyAvatar.sensorToWorldScale, MINI_POSITIONS[hand]),                "localRotation": MINI_ROTATIONS[hand],                "dimensions": Vec3.multiply(initialScale, MINI_DIMENSIONS),                "grab": {                    "grabbable": true                },                "visible": true            });            Entities.editEntity(miniUIOverlay, {                "sourceUrl": handsAreTracked() ? MINI_HAND_UI_HTML : MINI_UI_HTML,                "localPosition": Vec3.multiply(MyAvatar.sensorToWorldScale, MINI_UI_LOCAL_POSITION),                "localRotation": MINI_UI_LOCAL_ROTATION,                "dimensions": Vec3.multiply(initialScale, MINI_UI_DIMENSIONS),                "dpi": MINI_UI_DPI / initialScale,                "visible": true            });             updateMiniTabletID();             if (!miniUIOverlayEnabled) {                // Overlay content is created the first time it is visible to the user. The initial creation displays artefacts.                // Delay showing UI overlay until after giving it time for its content to be created.                Script.setTimeout(function () {                    Entities.editEntity(miniUIOverlay, { "alpha": 1.0 });                }, MINI_UI_OVERLAY_ENABLED_DELAY);            }        }         function size(scaleFactor) {            // Scale UI in place.            Entities.editEntity(miniOverlay, {                "dimensions": Vec3.multiply(scaleFactor, MINI_DIMENSIONS)            });            Entities.editEntity(miniUIOverlay, {                "localPosition": Vec3.multiply(scaleFactor, MINI_UI_LOCAL_POSITION),                "dimensions": Vec3.multiply(scaleFactor, MINI_UI_DIMENSIONS),                "dpi": MINI_UI_DPI / scaleFactor            });            updateRotation();        }         function startExpandingTablet(hand) {            // Expansion details.            if (hand === uiHand) {                miniExpandHandles = MINI_EXPAND_HANDLES;                miniExpandDeltaRotation = MINI_EXPAND_DELTA_ROTATION;            } else {                miniExpandHandles = MINI_EXPAND_HANDLES_OTHER;                miniExpandDeltaRotation = MINI_EXPAND_DELTA_ROTATION_OTHER;            }             // Grab details.            var properties = Entities.getEntityProperties(miniOverlay, ["localPosition", "localRotation"]);            miniExpandHand = hand;            miniExpandLocalRotation = properties.localRotation;            miniExpandLocalPosition = Vec3.sum(properties.localPosition,                Vec3.multiplyQbyV(miniExpandLocalRotation,                    Vec3.multiplyVbyV(miniExpandHandles[miniExpandHand], MINI_DIMENSIONS)));             // Start expanding.            miniInitialWidth = MINI_DIMENSIONS.x;            miniTargetWidth = getTabletWidthFromSettings();            miniTargetLocalRotation = Quat.multiply(miniExpandLocalRotation, miniExpandDeltaRotation);        }         function sizeAboutHandles(scaleFactor) {            // Scale UI and move per handles.            var tabletScaleFactor,                dimensions,                localRotation,                localPosition;             tabletScaleFactor = MyAvatar.sensorToWorldScale *                (1 + scaleFactor * (miniTargetWidth - miniInitialWidth) / miniInitialWidth);            dimensions = Vec3.multiply(tabletScaleFactor, MINI_DIMENSIONS);            localRotation = Quat.mix(miniExpandLocalRotation, miniTargetLocalRotation, scaleFactor);            localPosition =                Vec3.sum(miniExpandLocalPosition,                    Vec3.multiplyQbyV(miniExpandLocalRotation,                        Vec3.multiply(-tabletScaleFactor,                            Vec3.multiplyVbyV(miniExpandHandles[miniExpandHand], MINI_DIMENSIONS)))                );            localPosition = Vec3.sum(localPosition,                Vec3.multiplyQbyV(miniExpandLocalRotation, { x: 0, y: 0.5 * -dimensions.y, z: 0 }));            localPosition = Vec3.sum(localPosition,                Vec3.multiplyQbyV(localRotation, { x: 0, y: 0.5 * dimensions.y, z: 0 }));            Entities.editEntity(miniOverlay, {                "localPosition": localPosition,                "localRotation": localRotation,                "dimensions": dimensions            });            // FIXME: Temporary code change to try not displaying UI when mini tablet is expanding to become the tablet proper.            Entities.editEntity(miniUIOverlay, {                /*                "localPosition": Vec3.multiply(tabletScaleFactor, MINI_UI_LOCAL_POSITION),                "dimensions": Vec3.multiply(tabletScaleFactor, MINI_UI_DIMENSIONS),                "dpi": MINI_UI_DPI / tabletScaleFactor                */                "visible": false            });        }         function updateRotation() {            // Update the rotation of the tablet about its face normal so that its base is horizontal.            var COS_5_DEGREES = 0.996,                RADIANS_TO_DEGREES = DEGREES_180 / Math.PI,                defaultLocalRotation,                handOrientation,                defaultOrientation,                faceNormal,                desiredOrientation,                defaultYAxis,                desiredYAxis,                cross,                dot,                deltaAngle,                deltaRotation,                localRotation;             if (Entities.getEntityProperties(miniOverlay, ["parentJointIndex"]).parentJointIndex !== handJointIndex(uiHand)) {                // Overlay has been grabbed by other hand but this script hasn't received notification yet.                return;            }             defaultLocalRotation = MINI_ROTATIONS[uiHand];            handOrientation =                Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(handJointIndex(uiHand)));            defaultOrientation = Quat.multiply(handOrientation, defaultLocalRotation);            faceNormal = Vec3.multiplyQbyV(defaultOrientation, Vec3.UNIT_Z);             if (Math.abs(Vec3.dot(faceNormal, Vec3.UNIT_Y)) > COS_5_DEGREES) {                // Don't rotate mini tablet if almost flat in the x-z plane.                return;            } else {                // Rotate the tablet so that its base is parallel with the x-z plane.                desiredOrientation = Quat.lookAt(Vec3.ZERO, Vec3.multiplyQbyV(defaultOrientation, Vec3.UNIT_Z), Vec3.UNIT_Y);                defaultYAxis = Vec3.multiplyQbyV(defaultOrientation, Vec3.UNIT_Y);                desiredYAxis = Vec3.multiplyQbyV(desiredOrientation, Vec3.UNIT_Y);                cross = Vec3.cross(defaultYAxis, desiredYAxis);                dot = Vec3.dot(defaultYAxis, desiredYAxis);                deltaAngle = Math.atan2(Vec3.length(cross), dot) * RADIANS_TO_DEGREES;                if (Vec3.dot(cross, Vec3.multiplyQbyV(desiredOrientation, Vec3.UNIT_Z)) > 0) {                    deltaAngle = -deltaAngle;                }                deltaRotation = Quat.angleAxis(deltaAngle, Vec3.multiplyQbyV(defaultLocalRotation, Vec3.UNIT_Z));                localRotation = Quat.multiply(deltaRotation, defaultLocalRotation);            }            Entities.editEntity(miniOverlay, {                "localRotation": localRotation            });        }         function release() {            Entities.editEntity(miniOverlay, {                "parentID": Uuid.NONE, // Release hold so that hand can grab tablet proper.                "grab": {                    "grabbable": false                }            });        }         function hide() {            Entities.editEntity(miniOverlay, {                "visible": false            });            Entities.editEntity(miniUIOverlay, {                "visible": false            });        }         function checkEventBridge() {            // The miniUIOverlay overlay's overlay object is not available immediately the overlay is created so we have to             // provide a means to check for and connect it when it does become available.            if (miniOverlayObject) {                return;            }             miniOverlayObject = Entities.getEntityObject(miniUIOverlay);            if (miniOverlayObject) {                miniOverlayObject.webEventReceived.connect(onWebEventReceived);            }        }         function create() {            miniOverlay = Entities.addEntity({                "type": "Model",                "modelURL": MINI_MODEL,                "dimensions": Vec3.multiply(MyAvatar.sensorToWorldScale, MINI_DIMENSIONS),                "primitiveMode": "solid",                "grab": {                    "grabbable": true                },                "renderLayer": "world",                "visible": false            }, "local");            miniUIOverlay = Entities.addEntity({                "type": "Web",                "sourceUrl": handsAreTracked() ? MINI_HAND_UI_HTML : MINI_UI_HTML,                "parentID": miniOverlay,                "localPosition": Vec3.multiply(MyAvatar.sensorToWorldScale, MINI_UI_LOCAL_POSITION),                "localRotation": MINI_UI_LOCAL_ROTATION,                "dimensions": Vec3.multiply(MyAvatar.sensorToWorldScale, MINI_UI_DIMENSIONS),                "dpi": MINI_UI_DPI / MyAvatar.sensorToWorldScale,                "alpha": 0, // Hide overlay while its content is being created.                "grab": {                    "grabbable": false                },                "showKeyboardFocusHighlight": false,                "renderLayer": "world",                "visible": false

Lines 1–500 of 1162.