// // lobby.js // examples // // Created by Stephen Birarda on October 17, 2014 // 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 // var panelWall = false; var orbShell = false; var descriptionText = false; var showText = false; // used for formating the description text, in meters var textWidth = 4; var textHeight = .5; var numberOfLines = 2; var textMargin = 0.0625; var lineHeight = (textHeight - (2 * textMargin)) / numberOfLines; var avatarStickPosition = {}; var orbNaturalExtentsMin = { x: -1.230354, y: -1.22077, z: -1.210487 }; var orbNaturalExtentsMax = { x: 1.230353, y: 1.229819, z: 1.210487 }; var panelsNaturalExtentsMin = { x: -1.223182, y: -0.348487, z: 0.0451369 }; var panelsNaturalExtentsMax = { x: 1.223039, y: 0.602978, z: 1.224298 }; var orbNaturalDimensions = Vec3.subtract(orbNaturalExtentsMax, orbNaturalExtentsMin); var panelsNaturalDimensions = Vec3.subtract(panelsNaturalExtentsMax, panelsNaturalExtentsMin); var SCALING_FACTOR = 10; var orbDimensions = Vec3.multiply(orbNaturalDimensions, SCALING_FACTOR); var panelsDimensions = Vec3.multiply(panelsNaturalDimensions, SCALING_FACTOR); var orbNaturalCenter = Vec3.sum(orbNaturalExtentsMin, Vec3.multiply(orbNaturalDimensions, 0.5)); var panelsNaturalCenter = Vec3.sum(panelsNaturalExtentsMin, Vec3.multiply(panelsNaturalDimensions, 0.5)); var orbCenter = Vec3.multiply(orbNaturalCenter, SCALING_FACTOR); var panelsCenter = Vec3.multiply(panelsNaturalCenter, SCALING_FACTOR); var panelsCenterShift = Vec3.subtract(panelsCenter, orbCenter); var ORB_SHIFT = { x: 0, y: -1.4, z: -0.8}; var LOBBY_PANEL_WALL_URL = Script.getExternalPath(Script.ExternalPaths.Assets, "models/sets/Lobby/PanelWallForInterface.fbx"); var LOBBY_BLANK_PANEL_TEXTURE_URL = Script.getExternalPath(Script.ExternalPaths.Assets, "models/sets/Lobby/Texture.jpg"); var LOBBY_SHELL_URL = Script.getExternalPath(Script.ExternalPaths.Assets, "models/sets/Lobby/LobbyShellForInterface.fbx"); var droneSound = SoundCache.getSound(Script.getExternalPath(Script.ExternalPaths.Assets, "sounds/Lobby/drone.stereo.raw")); var currentDrone = null; var latinSound = SoundCache.getSound(Script.getExternalPath(Script.ExternalPaths.Assets, "sounds/Lobby/latin.stereo.raw")); var latinInjector = null; var elevatorSound = SoundCache.getSound(Script.getExternalPath(Script.ExternalPaths.Assets, "sounds/Lobby/elevator.stereo.raw")); var elevatorInjector = null; var currentMuzakInjector = null; var currentSound = null; function textOverlayPosition() { var TEXT_DISTANCE_OUT = 6; var TEXT_DISTANCE_DOWN = -2; return Vec3.sum(Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), TEXT_DISTANCE_OUT)), Vec3.multiply(Quat.getUp(Camera.orientation), TEXT_DISTANCE_DOWN)); } var panelPlaceOrder = [ 7, 8, 9, 10, 11, 12, 13, 0, 1, 2, 3, 4, 5, 6, 14, 15, 16, 17, 18, 19, 20 ]; // place index is 0-based function placeIndexToPanelIndex(placeIndex) { return panelPlaceOrder.indexOf(placeIndex) + 1; } // Panel index is 1-based function panelIndexToPlaceIndex(panelIndex) { return panelPlaceOrder[panelIndex - 1]; } var MAX_NUM_PANELS = 21; var DRONE_VOLUME = 0.3; function drawLobby() { if (!panelWall) { print("Adding overlays for the lobby panel wall and orb shell."); var cameraEuler = Quat.safeEulerAngles(Camera.orientation); var towardsMe = Quat.angleAxis(cameraEuler.y + 180, { x: 0, y: 1, z: 0}); var orbPosition = Vec3.sum(Camera.position, Vec3.multiplyQbyV(towardsMe, ORB_SHIFT)); var panelWallProps = { url: LOBBY_PANEL_WALL_URL, position: Vec3.sum(orbPosition, Vec3.multiplyQbyV(towardsMe, panelsCenterShift)), rotation: towardsMe, dimensions: panelsDimensions }; var orbShellProps = { url: LOBBY_SHELL_URL, position: orbPosition, rotation: towardsMe, dimensions: orbDimensions, ignoreRayIntersection: true }; var windowDimensions = Controller.getViewportDimensions(); var descriptionTextProps = { position: textOverlayPosition(), dimensions: { x: textWidth, y: textHeight }, backgroundColor: { red: 0, green: 0, blue: 0}, color: { red: 255, green: 255, blue: 255}, topMargin: textMargin, leftMargin: textMargin, bottomMargin: textMargin, rightMargin: textMargin, text: "", lineHeight: lineHeight, alpha: 0.9, backgroundAlpha: 0.9, ignoreRayIntersection: true, visible: false, isFacingAvatar: true }; avatarStickPosition = MyAvatar.position; panelWall = Overlays.addOverlay("model", panelWallProps); orbShell = Overlays.addOverlay("model", orbShellProps); descriptionText = Overlays.addOverlay("text3d", descriptionTextProps); if (droneSound.downloaded) { // start the drone sound if (!currentDrone) { currentDrone = Audio.playSound(droneSound, { stereo: true, loop: true, localOnly: true, volume: DRONE_VOLUME }); } else { currentDrone.restart(); } } // start one of our muzak sounds playRandomMuzak(); } } var places = {}; function changeLobbyTextures() { var req = new XMLHttpRequest(); req.open("GET", "https://metaverse.highfidelity.com/api/v1/places?limit=21", false); req.send(); places = JSON.parse(req.responseText).data.places; var NUM_PANELS = places.length; var textureProp = { textures: {} }; for (var j = 0; j < NUM_PANELS; j++) { var panelIndex = placeIndexToPanelIndex(j); textureProp["textures"]["file" + panelIndex] = places[j].previews.lobby; }; Overlays.editOverlay(panelWall, textureProp); } var MUZAK_VOLUME = 0.1; function playCurrentSound(secondOffset) { if (currentSound == latinSound) { if (!latinInjector) { latinInjector = Audio.playSound(latinSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME }); } else { latinInjector.restart(); } currentMuzakInjector = latinInjector; } else if (currentSound == elevatorSound) { if (!elevatorInjector) { elevatorInjector = Audio.playSound(elevatorSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME }); } else { elevatorInjector.restart(); } currentMuzakInjector = elevatorInjector; } } function playNextMuzak() { if (panelWall) { if (currentSound == latinSound) { if (elevatorSound.downloaded) { currentSound = elevatorSound; } } else if (currentSound == elevatorSound) { if (latinSound.downloaded) { currentSound = latinSound; } } playCurrentSound(0); } } function playRandomMuzak() { currentSound = null; if (latinSound.downloaded && elevatorSound.downloaded) { currentSound = Math.random() < 0.5 ? latinSound : elevatorSound; } else if (latinSound.downloaded) { currentSound = latinSound; } else if (elevatorSound.downloaded) { currentSound = elevatorSound; } if (currentSound) { // pick a random number of seconds from 0-10 to offset the muzak var secondOffset = Math.random() * 10; playCurrentSound(secondOffset); } else { currentMuzakInjector = null; } } function cleanupLobby() { toggleEnvironmentRendering(true); // for each of the 21 placeholder textures, set them back to default so the cached model doesn't have changed textures var panelTexturesReset = {}; panelTexturesReset["textures"] = {}; for (var j = 0; j < MAX_NUM_PANELS; j++) { panelTexturesReset["textures"]["file" + (j + 1)] = LOBBY_BLANK_PANEL_TEXTURE_URL; }; Overlays.editOverlay(panelWall, panelTexturesReset); Overlays.deleteOverlay(panelWall); Overlays.deleteOverlay(orbShell); Overlays.deleteOverlay(descriptionText); panelWall = false; orbShell = false; if (currentDrone) { currentDrone.stop(); currentDrone = null } if (currentMuzakInjector) { currentMuzakInjector.stop(); currentMuzakInjector = null; } places = {}; } function actionStartEvent(event) { if (panelWall) { // we've got an action event and our panel wall is up // check if we hit a panel and if we should jump there var result = Overlays.findRayIntersection(event.actionRay); if (result.intersects && result.overlayID == panelWall) { var panelName = result.extraInfo.subMeshName + ''; var panelStringIndex = panelName.indexOf("Panel"); if (panelStringIndex != -1) { var panelIndex = parseInt(panelName.slice(5)); var placeIndex = panelIndexToPlaceIndex(panelIndex); if (placeIndex < places.length) { var actionPlace = places[placeIndex]; print("Jumping to " + actionPlace.name + " at " + actionPlace.address + " after click on panel " + panelIndex + " with place index " + placeIndex); Window.location = actionPlace.address; maybeCleanupLobby(); } } } } } function backStartEvent() { if (!panelWall) { toggleEnvironmentRendering(false); drawLobby(); changeLobbyTextures(); } else { cleanupLobby(); } } var CLEANUP_EPSILON_DISTANCE = 0.05; function maybeCleanupLobby() { if (panelWall && Vec3.length(Vec3.subtract(avatarStickPosition, MyAvatar.position)) > CLEANUP_EPSILON_DISTANCE) { cleanupLobby(); } } function toggleEnvironmentRendering(shouldRender) { Scene.shouldRenderAvatars = shouldRender; Scene.shouldRenderEntities = shouldRender; } function handleLookAt(pickRay) { if (panelWall && descriptionText) { // we've got an action event and our panel wall is up // check if we hit a panel and if we should jump there var result = Overlays.findRayIntersection(pickRay); if (result.intersects && result.overlayID == panelWall) { var panelName = result.extraInfo.subMeshName + ''; var panelStringIndex = panelName.indexOf("Panel"); if (panelStringIndex != -1) { var panelIndex = parseInt(panelName.slice(5)); var placeIndex = panelIndexToPlaceIndex(panelIndex); if (placeIndex < places.length) { var actionPlace = places[placeIndex]; if (actionPlace.description == "") { Overlays.editOverlay(descriptionText, { text: actionPlace.name, visible: showText }); } else { // handle line wrapping var allWords = actionPlace.description.split(" "); var currentGoodLine = ""; var currentTestLine = ""; var formatedDescription = ""; var wordsFormated = 0; var currentTestWord = 0; var wordsOnLine = 0; while (wordsFormated < allWords.length) { // first add the "next word" to the line and test it. currentTestLine = currentGoodLine; if (wordsOnLine > 0) { currentTestLine += " " + allWords[currentTestWord]; } else { currentTestLine = allWords[currentTestWord]; } var lineLength = Overlays.textSize(descriptionText, currentTestLine).width; if (lineLength < textWidth || wordsOnLine == 0) { wordsFormated++; currentTestWord++; wordsOnLine++; currentGoodLine = currentTestLine; } else { formatedDescription += currentGoodLine + "\n"; wordsOnLine = 0; currentGoodLine = ""; currentTestLine = ""; } } formatedDescription += currentGoodLine; Overlays.editOverlay(descriptionText, { text: formatedDescription, visible: showText }); } } else { Overlays.editOverlay(descriptionText, { text: "", visible: false }); } } } } } function update(deltaTime) { maybeCleanupLobby(); if (panelWall) { Overlays.editOverlay(descriptionText, { position: textOverlayPosition() }); // if the reticle is up then we may need to play the next muzak if (currentMuzakInjector && !currentMuzakInjector.playing) { playNextMuzak(); } } } function mouseMoveEvent(event) { if (panelWall) { var pickRay = Camera.computePickRay(event.x, event.y); handleLookAt(pickRay); } } Controller.actionStartEvent.connect(actionStartEvent); Controller.backStartEvent.connect(backStartEvent); Script.update.connect(update); Script.scriptEnding.connect(maybeCleanupLobby); Controller.mouseMoveEvent.connect(mouseMoveEvent);