"use strict"; // // focus.js // Sam Cake Lab // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // (function() { function Cam() { this._enabled = false; this._aspectRatio = 2.0; this._height = 1024; this._fov = 10.0; this._exposure = 0.0; this._position = Camera.position; this._orientation = Camera.orientation; this._name = "focus"; this._shotNum = 0; }; Cam.prototype = { init: function() { this.updateImageSize(); this.updateTransform(); this.updateFov(); this.updateExposure(); this.updateTone(); }, // Image Size aspectRatio: function() { return this._aspectRatio; }, height: function() { return this._height; }, width: function() { return this._height * this._aspectRatio; }, updateImageSize: function() { this.config().resetSizeSpectatorCamera(this.width(), this.height()); }, setAspectRatio: function(ar) { ar = Math.min(10, Math.max(ar, 0.1)) this.updateImageSize(); }, setHeight: function(h) { this._height = Math.max(1, h) this.updateImageSize(); }, setWidth: function(w) { this._height = Math.max(1, w) / this.aspectRatio(); this.updateImageSize(); }, setWidthHeight: function(w, h) { this._height = Math.max(1, h); this._aspectRatio = Math.max(1, w) / this._height; this.updateImageSize(); }, // Activation config: function() { return Render.getConfig("SecondaryCamera"); }, enable: function() { this._enabled = true; this.config().enableSecondaryCameraRenderConfigs(true); }, disable: function() { this._enabled = false; this.config().enableSecondaryCameraRenderConfigs(false); }, // Transform position: function() { return this._position; }, orientation: function() { return this._orientation; }, updateTransform: function() { this.config().position = this._position; this.config().orientation = this._orientation; }, setTransform: function(pos, ori) { this._position = pos; this._orientation = ori; this.updateTransform(); }, setPosition: function(pos) { this._position = pos; this.updateTransform(); }, setOrientation: function(ori) { this._orientation = ori; this.updateTransform(); }, // Focus fov: function() { return this._fov; }, updateFov: function() { this.config().vFoV = this._fov; }, setFov: function(fov) { this._fov = Math.min(180, Math.max(0.1, fov)); this.updateFov(); }, // Exposure exposure: function() { return this._exposure; }, updateExposure: function() { Render.getConfig("SecondaryCameraJob.ToneMapping").exposure = this._exposure; }, setExposure: function(exposure) { this._exposure = Math.min(5, Math.max(-5, exposure)); this.updateExposure(); }, // Tone updateTone: function() { Render.getConfig("SecondaryCameraJob.ToneMapping").curve = 0; }, // Shot! shoot: function() { var d = new Date(); var name = this._name + '-' + d.getMonth() + '.' + + d.getDate() + '-' + d.getHours() + '.' + d.getMinutes() + '-' + this._shotNum; Window.takeSecondaryCameraSnapshot(false, name); this._shotNum++; this.updateTone(); }, }; var cam = new Cam(); cam.init(); var overlays = []; var overlayFrames = {}; var viewFinderOverlay = false; var DIM = {x: 1.0, y: -1.0, z: 0.0}; var avatarHeadJoint = MyAvatar.getJointIndex("Head"); function getCamePos(i) { return Vec3.sum(Camera.position, Vec3.multiplyQbyV(Camera.orientation, { x: -0.5 + 0.2 * i, y: -0.3, z: -1 })) } function updateFrames() { for (var i = 0; i < overlays.length; i++) { overlayFrames[overlays[i]] = { position: getCamePos(i), rotation: Camera.orientation } } Overlays.editOverlays(overlayFrames) } function updateOverlay() { // The only way I found to update the viewFinderOverlay without turning the spectator camera on and off is to delete and recreate the // overlay, which is inefficient but resizing the window shouldn't be performed often if (viewFinderOverlay) { Overlays.deleteOverlay(viewFinderOverlay); } viewFinderOverlay = Overlays.addOverlay("image3d", { url: "resource://spectatorCameraFrame", emissive: true, parentID: MyAvatar.SELF_ID, parentJointIndex: -7, alpha: 1, localRotation: { w: 1, x: 0, y: 0, z: 0 }, localPosition: { x: 0, y: 0.0, z: -1.0 }, dimensions: DIM, }); } var accumulated = 0; function theUpdate(deltaTime) { accumulated += deltaTime; if (accumulated > 1) updateFrames() cam.setTransform(Camera.position, Camera.orientation); } function takeShot() { cam.shoot(); } function turnOn() { // createAppWindow(); cam.enable(); cam.setAspectRatio(2.0); cam.setTransform(Camera.position, Camera.orientation); updateOverlay() Script.update.connect(theUpdate); initUIFromCam(); } function turnOff() { // killAppWindow(); Script.update.disconnect(theUpdate); cam.disable(); for (var i = 0; i < overlays.length; i++) { Overlays.deleteOverlay(overlays[i]); } Overlays.deleteOverlay(viewFinderOverlay); } var AppUi = Script.require('appUi'); //***************************************************** // THE APP TABLET ICON ON OFF + "?" + Date.now() //***************************************************** var TABLET_BUTTON_NAME = "Focus"; var ICON_URL = Script.resolvePath("./focus.svg"); var ACTIVE_ICON_URL = Script.resolvePath("./focus.svg"); function fromAppUI (message) { if (message.focusApp !== undefined) { print("fromAppUI: " + JSON.stringify(message)) switch (message.focusApp) { case "takeShot": takeShot(); break; case "setFov": cam.setFov(message.params[0]); break; case "setExposure": cam.setExposure(message.params[0]); break; } } } var UI_URL = Script.resolvePath("./focus.qml"); var appWindow = null function createAppWindow() { appWindow = Desktop.createWindow(UI_URL, { title: 'Shot!', flags: Desktop.ALWAYS_ON_TOP, presentationMode: Desktop.PresentationMode.NATIVE, size: {x: 300, y: 300} }); appWindow.closed.connect(killAppWindow); appWindow.fromQml.connect(fromAppUI); } function killAppWindow() { if (appWindow !== null) { appWindow.closed.disconnect(killAppWindow); appWindow.fromQml.disconnect(fromAppUI); appWindow.close() appWindow = null } } function sendToUI(message) { if (appWindow) { appWindow.sendToQml(message); } } function initUIFromCam() { sendToUI({ 'initFOV': cam.fov() }); sendToUI({ 'initExposure': cam.exposure() }); } var ui; function startup() { ui = new AppUi({ buttonName: TABLET_BUTTON_NAME, home: UI_URL, onOpened: function () { print("Hello Focus!"); turnOn() }, onClosed: function () { turnOff() print("Bye Focus!"); }, onMessage: fromAppUI, normalButton: ICON_URL, activeButton: ACTIVE_ICON_URL }); } startup(); Script.scriptEnding.connect(function () { turnOff() }); }());