diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf old mode 100755 new mode 100644 diff --git a/scripts/developer/utilities/render/photobooth/html/photobooth.html b/scripts/developer/utilities/render/photobooth/html/photobooth.html index 8964a51f05..f9c79174f3 100644 --- a/scripts/developer/utilities/render/photobooth/html/photobooth.html +++ b/scripts/developer/utilities/render/photobooth/html/photobooth.html @@ -2,166 +2,151 @@ <head> <title>Photo Booth</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <link rel="stylesheet" type="text/css" href="../../../../../system/html/css/edit-style.css"> - <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script> - - <script> - var EventBridge; - var openEventBridge = function (callback) { - var WebChannel = new QWebChannel(qt.webChannelTransport, function (channel) { - EventBridge = WebChannel.objects.eventBridgeWrapper.eventBridge; - callback(); - }); - }; - - var emit = function (eventType, data) { - data = data || {}; - data.type = eventType; - EventBridge.emitWebEvent(JSON.stringify(data)); - }; - - function loaded () { - openEventBridge(function () { - emit("onLoad", {value: "faye"}); - - var elModelURL = document.getElementById("model-url"); - var elReloadModelButton = document.getElementById("reload-model-button"); - var elCamera = document.getElementById("property-camera"); - //var elLightingPreset = document.getElementById("property-lighting-preset"); - var elPictureButton = document.getElementById("picture-button"); - - elReloadModelButton.addEventListener('click', function() { - emit("onClickReloadModelButton", {value: elModelURL.value}); - }); - elCamera.addEventListener('change', function() { - emit("onSelectCamera", {value: this.value}); - }); - // elLightingPreset.addEventListener('change', function() { - // emit("onSelectLightingPreset", {value: "faye"}); - // }); - elPictureButton.addEventListener('click', function() { - emit("onClickPictureButton", {value: "faye"}); - }); - - - }); - - // Drop downs - function setDropdownText(dropdown) { - var lis = dropdown.parentNode.getElementsByTagName("li"); - var text = ""; - for (var i = 0; i < lis.length; i++) { - if (lis[i].getAttribute("value") === dropdown.value) { - text = lis[i].textContent; - } - } - dropdown.firstChild.textContent = text; - } - function toggleDropdown(event) { - var element = event.target; - if (element.nodeName !== "DT") { - element = element.parentNode; - } - element = element.parentNode; - var isDropped = element.getAttribute("dropped"); - element.setAttribute("dropped", isDropped !== "true" ? "true" : "false"); - } - function setDropdownValue(event) { - var dt = event.target.parentNode.parentNode.previousSibling; - dt.value = event.target.getAttribute("value"); - dt.firstChild.textContent = event.target.textContent; - - dt.parentNode.setAttribute("dropped", "false"); - - var evt = document.createEvent("HTMLEvents"); - evt.initEvent("change", true, true); - dt.dispatchEvent(evt); - } - - var elDropdowns = document.getElementsByTagName("select"); - for (var i = 0; i < elDropdowns.length; i++) { - var options = elDropdowns[i].getElementsByTagName("option"); - var selectedOption = 0; - for (var j = 0; j < options.length; j++) { - if (options[j].getAttribute("selected") === "selected") { - selectedOption = j; - } - } - var div = elDropdowns[i].parentNode; - - var dl = document.createElement("dl"); - div.appendChild(dl); - - var dt = document.createElement("dt"); - dt.name = elDropdowns[i].name; - dt.id = elDropdowns[i].id; - dt.addEventListener("click", toggleDropdown, true); - dl.appendChild(dt); - - var span = document.createElement("span"); - span.setAttribute("value", options[selectedOption].value); - span.textContent = options[selectedOption].firstChild.textContent; - dt.appendChild(span); - - var span = document.createElement("span"); - span.textContent = "5"; // caratDn - dt.appendChild(span); - - var dd = document.createElement("dd"); - dl.appendChild(dd); - - var ul = document.createElement("ul"); - dd.appendChild(ul); - - for (var j = 0; j < options.length; j++) { - var li = document.createElement("li"); - li.setAttribute("value", options[j].value); - li.textContent = options[j].firstChild.textContent; - li.addEventListener("click", setDropdownValue); - ul.appendChild(li); - } - } - elDropdowns = document.getElementsByTagName("select"); - while (elDropdowns.length > 0) { - var el = elDropdowns[0]; - el.parentNode.removeChild(el); - elDropdowns = document.getElementsByTagName("select"); - } - } - </script> - + <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/9.7.2/css/bootstrap-slider.min.css"> <style> - </style> + body { + margin: 0; + padding: 0; + width: 100%; + color: white; + } + + .top-bar { + height: 90px; + background: linear-gradient(#2b2b2b, #1e1e1e); + font-family: Raleway-Bold; + padding-left: 30px; + padding-right: 30px; + display: flex; + align-items: center; + position: fixed; + width: 480px; + top: 0; + z-index: 1; + font-size: 16px; + } + + .content { + margin-top: 90px; + padding: 30px; + } + + .slider { + margin-left: 70px; + } + + #camera-toggle { + font-family: Raleway-Bold; + font-size: 13px; + text-transform: uppercase; + vertical-align: top; + height: 28px; + min-width: 120px; + padding: 0px 18px; + margin-right: 0px; + border-radius: 5px; + border: none; + color: #121212; + background-color: #afafaf; + background: linear-gradient(#fff 20%, #afafaf 100%); + cursor: pointer; + } + + .dropdown li { + background-color: #ffffff; + } + + + + </style> </head> - <body onload="loaded()"> - <div id="properties-list"> - <div class="property url refresh"> - <label>Model URL</label> - <input type="text" id="model-url"></input> - <input type="button" id="reload-model-button" class="glyph" value="F"> - </div> - <!-- - <div class="property dropdown"> - <label>Lighting Preset</label> - <select id="property-lighting-preset"> - <option>Default Lighting</option> - <option>Sam's Cool Light</option> - <option>Alan's Light Magic</option> - </select> - </div> - --> - <div class="property dropdown"> - <label>Camera</label> - <select id="property-camera"> - <option>First Person Camera</option> - <option>Main Camera</option> - <option>Left Camera</option> - <option>Right Camera</option> - </select> - </div> - <div class="property"> - <input id="picture-button" type="button" class="blue" value="Take Picture"> + <body> + <div class="top-bar"> + <div>Photobooth</div> + </div> + <div class="content"> + <div id="properties-list"> + <div class="property url refresh"> + <label>Model URL</label> + <input type="text" id="model-url"></input> + <input type="button" id="reload-model-button" class="glyph" value="F"> + </div> + <div class="property"> + <label>Rotate Model</label> + <input + id="rotate-slider" + type="text" + data-provide="slider" + data-slider-ticks="[-180, 0, 180]" + data-slider-ticks-labels='["clockwise", "centre", "anti-clockwise"]' + data-slider-min="-180" + data-slider-max="180" + data-slider-step="1" + data-slider-value="0" + data-slider-tooltip="show" + > + </div> + <div class="property"> + <label>Camera</label> + <div class="dropdown"> + <button id="camera-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + First Person Camera + <span class="glyphicon glyphicon-menu-down"></span> + </button> + <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> + <li>First Person Camera</li> + <li>Main Camera</li> + <li>Left Camera</li> + <li>Right Camera</li> + </ul> + </div> + </div> + <div class="property"> + <input id="picture-button" type="button" class="blue" value="Take Picture"> + </div> </div> </div> + <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> + <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/9.7.2/bootstrap-slider.min.js"></script> + <script> + + // Helper function to emit web events to photoboothApp.js + function emit(eventType, eventData) { + var eventObject = { + "app": "photobooth", + "type": eventType, + "data": eventData + }; + EventBridge.emitWebEvent(JSON.stringify(eventObject)); + } + + $(document).ready(function() { + // Send a ready event to hifi + emit("ready", null); + // Send an event when camera selection changes + $(".dropdown-menu li").click(function() { + console.log("clicked " + this.textContent); + $("#camera-toggle").text(this.textContent + " "); + $("#camera-toggle").append("<span class='glyphicon glyphicon-menu-down'></span>"); + emit("onSelectCamera", {value: this.textContent}); + }); + // Send an event to hifi to trigger snapshot + $("#picture-button").click(function() { + emit("onClickPictureButton", null); + }); + // Send an event to hifi for loading the given model URL + $("#reload-model-button").click(function() { + emit("onClickReloadModelButton", {value: $("#model-url").val()}); + }); + $("#rotate-slider").slider().on("slide", function(e){ + console.log("slided " + e.value); + emit("onRotateSlider", {value: e.value}); + }); + }); + </script> </body> </html> diff --git a/scripts/developer/utilities/render/photobooth/photobooth.js b/scripts/developer/utilities/render/photobooth/photobooth.js deleted file mode 100644 index b78986be1a..0000000000 --- a/scripts/developer/utilities/render/photobooth/photobooth.js +++ /dev/null @@ -1,178 +0,0 @@ -(function () { - var SNAPSHOT_DELAY = 500; // 500ms - var PHOTOBOOTH_WINDOW_HTML_URL = Script.resolvePath("./html/photobooth.html"); - var PHOTOBOOTH_SETUP_JSON_URL = Script.resolvePath("./photoboothSetup.json"); - var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); - var MODEL_BOUNDING_BOX_DIMENSIONS = {x: 1.0174,y: 1.1925,z: 1.0165}; - - var PhotoBooth = {}; - PhotoBooth.init = function () { - var success = Clipboard.importEntities(PHOTOBOOTH_SETUP_JSON_URL); - var forwardFactor = 10; - var forwardUnitVector = Vec3.normalize(Quat.getForward(MyAvatar.orientation)); - var forwardOffset = Vec3.multiply(forwardUnitVector,forwardFactor); - var rightFactor = 3; - // TODO: rightUnitVec is unused and spawnLocation declaration is incorrect - var rightUnitVec = Vec3.normalize(Quat.getRight(MyAvatar.orientation)); - var spawnLocation = Vec3.sum(Vec3.sum(MyAvatar.position,forwardOffset),rightFactor); - if (success) { - this.pastedEntityIDs = Clipboard.pasteEntities(spawnLocation); - this.processPastedEntities(); - - } - }; - - PhotoBooth.processPastedEntities = function () { - var cameraResults = {}; - var modelResult; - var modelPos; - this.pastedEntityIDs.forEach(function(id) { - var props = Entities.getEntityProperties(id); - var parts = props["name"].split(":"); - if (parts[0] === "Photo Booth Camera") { - cameraResults[parts[1]] = id; - } - if (parts[0] === "Photo Booth Model") { - modelResult = id; - modelPos = props.position; - } - }); - print(JSON.stringify(cameraResults)); - print(JSON.stringify(modelResult)); - this.cameraEntities = cameraResults; - this.modelEntityID = modelResult; - this.centrePos = modelPos; - }; - - // replace the model in scene with new model - PhotoBooth.changeModel = function (newModelURL) { - // deletes old model - Entities.deleteEntity(this.modelEntityID); - // create new model at centre of the photobooth - var newProps = { - name: "Photo Booth Model", - type: "Model", - modelURL: newModelURL, - position: this.centrePos - }; - var newModelEntityID = Entities.addEntity(newProps); - - // scale model dimensions to fit in bounding box - var scaleModel = function () { - newProps = Entities.getEntityProperties(newModelEntityID); - var myDimensions = newProps.dimensions; - print("myDimensions: " + JSON.stringify(myDimensions)); - var k; - if (myDimensions.x > MODEL_BOUNDING_BOX_DIMENSIONS.x) { - k = MODEL_BOUNDING_BOX_DIMENSIONS.x / myDimensions.x; - myDimensions = Vec3.multiply(k, myDimensions); - } - if (myDimensions.y > MODEL_BOUNDING_BOX_DIMENSIONS.y) { - k = MODEL_BOUNDING_BOX_DIMENSIONS.y / myDimensions.y; - myDimensions = Vec3.multiply(k, myDimensions); - } - if (myDimensions.z > MODEL_BOUNDING_BOX_DIMENSIONS.z) { - k = MODEL_BOUNDING_BOX_DIMENSIONS.z / myDimensions.z; - myDimensions = Vec3.multiply(k, myDimensions); - } - // position the new model on the table - var y_offset = (MODEL_BOUNDING_BOX_DIMENSIONS.y - myDimensions.y) / 2; - var myPosition = Vec3.sum(newProps.position, {x:0, y:-y_offset, z:0}); - Entities.editEntity(newModelEntityID,{position: myPosition, dimensions: myDimensions}); - }; - - // add a delay before scaling to make sure the entity server have gotten the right model dimensions - Script.setTimeout(function () { - scaleModel(); - }, 400); - - this.modelEntityID = newModelEntityID; - }; - - PhotoBooth.destroy = function () { - this.pastedEntityIDs.forEach(function(id) { - Entities.deleteEntity(id); - }); - Entities.deleteEntity(this.modelEntityID); - }; - - var main = function () { - PhotoBooth.init(); - - var photoboothWindowListener = {}; - photoboothWindowListener.onLoad = function (event) { - print("loaded" + event.value); - if (!event.hasOwnProperty("value")){ - return; - } - }; - - photoboothWindowListener.onSelectCamera = function (event) { - print("selected camera " + event.value); - if (!event.hasOwnProperty("value")){ - return; - } - if (event.value === "First Person Camera") { - Camera.mode = "first person"; - } else { - Camera.mode = "entity"; - var cameraID = PhotoBooth.cameraEntities[event.value]; - Camera.setCameraEntity(cameraID); - } - }; - - photoboothWindowListener.onSelectLightingPreset = function (event) { - print("selected lighting preset" + event.value); - if (!event.hasOwnProperty("value")){ - return; - } - }; - - photoboothWindowListener.onClickPictureButton = function (event) { - print("clicked picture button"); - // hide HUD tool bar - toolbar.writeProperty("visible", false); - // hide Overlays (such as Running Scripts or other Dialog UI) - Menu.setIsOptionChecked("Overlays", false); - // hide mouse cursor - Reticle.visible = false; - // giving a delay here before snapshotting so that there is time to hide toolbar and other UIs - // void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) - Script.setTimeout(function () { - Window.takeSnapshot(false, false, 1.91); - // show hidden items after snapshot is taken - toolbar.writeProperty("visible", true); - Menu.setIsOptionChecked("Overlays", true); - // unknown issue: somehow we don't need to reset cursor to visible in script and the mouse still returns after snapshot - // Reticle.visible = true; - }, SNAPSHOT_DELAY); - }; - - photoboothWindowListener.onClickReloadModelButton = function (event) { - print("clicked reload model button " + event.value); - PhotoBooth.changeModel(event.value); - }; - - var photoboothWindow = new OverlayWebWindow({ - title: 'Photo Booth', - source: PHOTOBOOTH_WINDOW_HTML_URL, - width: 450, - height: 450, - visible: true - }); - - photoboothWindow.webEventReceived.connect(function (data) { - var event = JSON.parse(data); - if (photoboothWindowListener[event.type]) { - photoboothWindowListener[event.type](event); - } - }); - }; - main(); - - function cleanup() { - Camera.mode = "first person"; - PhotoBooth.destroy(); - } - Script.scriptEnding.connect(cleanup); -}()); \ No newline at end of file diff --git a/scripts/developer/utilities/render/photobooth/photoboothApp.js b/scripts/developer/utilities/render/photobooth/photoboothApp.js new file mode 100644 index 0000000000..154028f091 --- /dev/null +++ b/scripts/developer/utilities/render/photobooth/photoboothApp.js @@ -0,0 +1,199 @@ +// +// photobooth.js +// scripts/developer/utilities/render/photobooth +// +// Created by Faye Li on 2 Nov 2016 +// Copyright 2016 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 +// +/* globals Tablet, Toolbars, Script, HMD, Controller, Menu */ +(function () { + var SNAPSHOT_DELAY = 500; // 500ms + var PHOTOBOOTH_WINDOW_HTML_URL = Script.resolvePath("./html/photobooth.html"); + var PHOTOBOOTH_SETUP_JSON_URL = Script.resolvePath("./photoboothSetup.json"); + var MODEL_BOUNDING_BOX_DIMENSIONS = {x: 1.0174,y: 1.1925,z: 1.0165}; + var PhotoBooth = {}; + var photoboothCreated = false; + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var button = tablet.addButton({ + icon: "icons/tablet-icons/snap-i.svg", + text: "PHOTOBOOTH" + }); + + function onClicked() { + if (photoboothCreated) { + tablet.gotoHomeScreen(); + PhotoBooth.destroy(); + } else { + tablet.gotoWebScreen(PHOTOBOOTH_WINDOW_HTML_URL); + PhotoBooth.init(); + } + } + + function onScreenChanged() { + if (photoboothCreated) { + tablet.gotoHomeScreen(); + PhotoBooth.destroy(); + button.editProperties({isActive: false}); + } else { + button.editProperties({isActive: true}); + } + } + tablet.screenChanged.connect(onScreenChanged); + button.clicked.connect(onClicked); + tablet.webEventReceived.connect(onWebEventReceived); + + + function onWebEventReceived(event) { + print("photobooth.js received a web event:" + event); + // Converts the event to a JavasScript Object + if (typeof event === "string") { + event = JSON.parse(event); + } + if (event.app === "photobooth") { + if (event.type === "onClickPictureButton") { + print("clicked picture button"); + // // hide HUD tool bar + // toolbar.writeProperty("visible", false); + // hide Overlays (such as Running Scripts or other Dialog UI) + Menu.setIsOptionChecked("Overlays", false); + // hide mouse cursor + Reticle.visible = false; + // hide tablet + HMD.closeTablet(); + // // giving a delay here before snapshotting so that there is time to hide other UIs + // void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) + Script.setTimeout(function () { + Window.takeSnapshot(false, false, 1.91); + // show hidden items after snapshot is taken + // issue: currently there's no way to show tablet via a script command. user will have to manually open tablet again + // issue: somehow we don't need to reset cursor to visible in script and the mouse still returns after snapshot + // Reticle.visible = true; + // toolbar.writeProperty("visible", true); + Menu.setIsOptionChecked("Overlays", true); + }, SNAPSHOT_DELAY); + } else if (event.type === "onClickReloadModelButton") { + print("clicked reload model button " + event.data.value); + PhotoBooth.changeModel(event.data.value); + } else if (event.type === "onSelectCamera") { + print("selected camera " + event.data.value); + if (!event.data.hasOwnProperty("value")){ + return; + } + if (event.data.value === "First Person Camera") { + Camera.mode = "first person"; + } else { + Camera.mode = "entity"; + var cameraID = PhotoBooth.cameraEntities[event.data.value]; + Camera.setCameraEntity(cameraID); + } + } else if (event.type === "onRotateSlider") { + var props = {}; + props.rotation = Quat.fromPitchYawRollDegrees(0, event.data.value, 0); + Entities.editEntity(PhotoBooth.modelEntityID, props); + } + } + } + + PhotoBooth.init = function () { + photoboothCreated = true; + var success = Clipboard.importEntities(PHOTOBOOTH_SETUP_JSON_URL); + var frontFactor = 10; + // getForward is preffered as getFront function is deprecated + var frontUnitVec = Vec3.normalize(Quat.getFront(MyAvatar.orientation)); + var frontOffset = Vec3.multiply(frontUnitVec,frontFactor); + var upFactor = 3; + var upUnitVec = Vec3.normalize(Quat.getUp(MyAvatar.orientation)); + var upOffset = Vec3.multiply(upUnitVec, upFactor); + var spawnLocation = Vec3.sum(MyAvatar.position,frontOffset); + spawnLocation = Vec3.sum(spawnLocation, upOffset); + if (success) { + this.pastedEntityIDs = Clipboard.pasteEntities(spawnLocation); + this.processPastedEntities(); + } + }; + + PhotoBooth.processPastedEntities = function () { + var cameraResults = {}; + var modelResult; + var modelPos; + this.pastedEntityIDs.forEach(function(id) { + var props = Entities.getEntityProperties(id); + var parts = props["name"].split(":"); + if (parts[0] === "Photo Booth Camera") { + cameraResults[parts[1]] = id; + } + if (parts[0] === "Photo Booth Model") { + modelResult = id; + modelPos = props.position; + } + }); + print(JSON.stringify(cameraResults)); + print(JSON.stringify(modelResult)); + this.cameraEntities = cameraResults; + this.modelEntityID = modelResult; + this.centrePos = modelPos; + }; + + // replace the model in scene with new model + PhotoBooth.changeModel = function (newModelURL) { + // deletes old model + Entities.deleteEntity(this.modelEntityID); + // create new model at centre of the photobooth + var newProps = { + name: "Photo Booth Model", + type: "Model", + modelURL: newModelURL, + position: this.centrePos + }; + var newModelEntityID = Entities.addEntity(newProps); + + // scale model dimensions to fit in bounding box + var scaleModel = function () { + newProps = Entities.getEntityProperties(newModelEntityID); + var myDimensions = newProps.dimensions; + print("myDimensions: " + JSON.stringify(myDimensions)); + var k; + if (myDimensions.x > MODEL_BOUNDING_BOX_DIMENSIONS.x) { + k = MODEL_BOUNDING_BOX_DIMENSIONS.x / myDimensions.x; + myDimensions = Vec3.multiply(k, myDimensions); + } + if (myDimensions.y > MODEL_BOUNDING_BOX_DIMENSIONS.y) { + k = MODEL_BOUNDING_BOX_DIMENSIONS.y / myDimensions.y; + myDimensions = Vec3.multiply(k, myDimensions); + } + if (myDimensions.z > MODEL_BOUNDING_BOX_DIMENSIONS.z) { + k = MODEL_BOUNDING_BOX_DIMENSIONS.z / myDimensions.z; + myDimensions = Vec3.multiply(k, myDimensions); + } + // position the new model on the table + var y_offset = (MODEL_BOUNDING_BOX_DIMENSIONS.y - myDimensions.y) / 2; + var myPosition = Vec3.sum(newProps.position, {x:0, y:-y_offset, z:0}); + Entities.editEntity(newModelEntityID,{position: myPosition, dimensions: myDimensions}); + }; + + // add a delay before scaling to make sure the entity server have gotten the right model dimensions + Script.setTimeout(function () { + scaleModel(); + }, 400); + + this.modelEntityID = newModelEntityID; + }; + + PhotoBooth.destroy = function () { + this.pastedEntityIDs.forEach(function(id) { + Entities.deleteEntity(id); + }); + Entities.deleteEntity(this.modelEntityID); + photoboothCreated = false; + Camera.mode = "first person"; + }; + + function cleanup() { + tablet.removeButton(button); + PhotoBooth.destroy(); + } + Script.scriptEnding.connect(cleanup); +}()); \ No newline at end of file