mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-29 19:02:55 +02:00
Merge pull request #10002 from fayeli/tablet-photobooth
Photobooth Improvements: Using Tablet & Ability to Rotate Model
This commit is contained in:
commit
d1827cf12c
4 changed files with 340 additions and 334 deletions
0
interface/resources/fonts/hifi-glyphs.ttf
Executable file → Normal file
0
interface/resources/fonts/hifi-glyphs.ttf
Executable file → Normal file
|
@ -2,166 +2,151 @@
|
||||||
<head>
|
<head>
|
||||||
<title>Photo Booth</title>
|
<title>Photo Booth</title>
|
||||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
<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">
|
<link rel="stylesheet" type="text/css" href="../../../../../system/html/css/edit-style.css">
|
||||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/9.7.2/css/bootstrap-slider.min.css">
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<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>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body onload="loaded()">
|
<body>
|
||||||
<div id="properties-list">
|
<div class="top-bar">
|
||||||
<div class="property url refresh">
|
<div>Photobooth</div>
|
||||||
<label>Model URL</label>
|
</div>
|
||||||
<input type="text" id="model-url"></input>
|
<div class="content">
|
||||||
<input type="button" id="reload-model-button" class="glyph" value="F">
|
<div id="properties-list">
|
||||||
</div>
|
<div class="property url refresh">
|
||||||
<!--
|
<label>Model URL</label>
|
||||||
<div class="property dropdown">
|
<input type="text" id="model-url"></input>
|
||||||
<label>Lighting Preset</label>
|
<input type="button" id="reload-model-button" class="glyph" value="F">
|
||||||
<select id="property-lighting-preset">
|
</div>
|
||||||
<option>Default Lighting</option>
|
<div class="property">
|
||||||
<option>Sam's Cool Light</option>
|
<label>Rotate Model</label>
|
||||||
<option>Alan's Light Magic</option>
|
<input
|
||||||
</select>
|
id="rotate-slider"
|
||||||
</div>
|
type="text"
|
||||||
-->
|
data-provide="slider"
|
||||||
<div class="property dropdown">
|
data-slider-ticks="[-180, 0, 180]"
|
||||||
<label>Camera</label>
|
data-slider-ticks-labels='["clockwise", "centre", "anti-clockwise"]'
|
||||||
<select id="property-camera">
|
data-slider-min="-180"
|
||||||
<option>First Person Camera</option>
|
data-slider-max="180"
|
||||||
<option>Main Camera</option>
|
data-slider-step="1"
|
||||||
<option>Left Camera</option>
|
data-slider-value="0"
|
||||||
<option>Right Camera</option>
|
data-slider-tooltip="show"
|
||||||
</select>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="property">
|
<div class="property">
|
||||||
<input id="picture-button" type="button" class="blue" value="Take Picture">
|
<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>
|
||||||
</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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -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);
|
|
||||||
}());
|
|
199
scripts/developer/utilities/render/photobooth/photoboothApp.js
Normal file
199
scripts/developer/utilities/render/photobooth/photoboothApp.js
Normal file
|
@ -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);
|
||||||
|
}());
|
Loading…
Reference in a new issue