diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ebf5ea5e4..9731582b59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,7 @@ if (WIN32) elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) #SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic") #SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fno-strict-aliasing") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fno-strict-aliasing -ggdb") endif(WIN32) if (NOT MSVC12) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 08a510f32c..4755d9137a 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -208,7 +208,7 @@ void Agent::run() { _scriptEngine.init(); // must be done before we set up the viewers - _scriptEngine.registerGlobalObject("SoundCache", &SoundCache::getInstance()); + _scriptEngine.registerGlobalObject("SoundCache", DependencyManager::get().data()); _scriptEngine.registerGlobalObject("EntityViewer", &_entityViewer); _entityViewer.setJurisdictionListener(_scriptEngine.getEntityScriptingInterface()->getJurisdictionListener()); diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 9eb2fad739..80f3cbab5e 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -136,7 +136,7 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : // Create Singleton objects on main thread NetworkAccessManager::getInstance(); - SoundCache::getInstance(); + auto soundCache = DependencyManager::get(); } void AssignmentClient::sendAssignmentRequest() { diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index d882ea19ac..85d0a7414c 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -147,3 +147,14 @@ void EntityServer::pruneDeletedEntities() { } } +void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectionObject) { + bool wantEditLogging = false; + readOptionBool(QString("wantEditLogging"), settingsSectionObject, wantEditLogging); + qDebug("wantEditLogging=%s", debug::valueOf(wantEditLogging)); + + + EntityTree* tree = static_cast(_tree); + tree->setWantEditLogging(wantEditLogging); +} + + diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index d072d18cdf..d8c2e39f3b 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -41,6 +41,7 @@ public: virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent); virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode); + virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject); public slots: void pruneDeletedEntities(); diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 1ff82f58eb..fdaede8c44 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -409,6 +409,13 @@ "default": "", "advanced": true }, + { + "name": "wantEditLogging", + "type": "checkbox", + "help": "Logging of all edits to entities", + "default": true, + "advanced": true + }, { "name": "verboseDebug", "type": "checkbox", diff --git a/examples/controllers/oculus/goTo.js b/examples/controllers/oculus/goTo.js new file mode 100644 index 0000000000..91ae8e6141 --- /dev/null +++ b/examples/controllers/oculus/goTo.js @@ -0,0 +1,248 @@ +// +// goTo.js +// examples +// +// Created by Thijs Wenker on 12/28/14. +// Copyright 2014 High Fidelity, Inc. +// +// Control a virtual keyboard using your favorite HMD. +// Usage: Enable VR-mode and go to First person mode, +// look at the key that you would like to press, and press the spacebar on your "REAL" keyboard. +// +// Enter a location URL using your HMD. Press Enter to pop-up the virtual keyboard and location input. +// Press Space on the keyboard or the X button on your gamepad to press a key that you have selected. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../../libraries/globals.js"); +Script.include("../../libraries/virtualKeyboard.js"); + +const MAX_SHOW_INSTRUCTION_TIMES = 2; +const INSTRUCTIONS_SETTING = "GoToInstructionsShowCounter" + +var windowDimensions = Controller.getViewportDimensions(); + +function Instructions(imageURL, originalWidth, originalHeight) { + var tthis = this; + this.originalSize = {w: originalWidth, h: originalHeight}; + this.visible = false; + this.overlay = Overlays.addOverlay("image", { + imageURL: imageURL, + x: 0, + y: 0, + width: originalWidth, + height: originalHeight, + alpha: 1, + visible: this.visible + }); + + this.show = function() { + var timesShown = Settings.getValue(INSTRUCTIONS_SETTING); + timesShown = timesShown === "" ? 0 : parseInt(timesShown); + print(timesShown); + if (timesShown < MAX_SHOW_INSTRUCTION_TIMES) { + Settings.setValue(INSTRUCTIONS_SETTING, timesShown + 1); + tthis.visible = true; + tthis.rescale(); + Overlays.editOverlay(tthis.overlay, {visible: tthis.visible}); + return; + } + tthis.remove(); + } + + this.remove = function() { + Overlays.deleteOverlay(tthis.overlay); + tthis.visible = false; + }; + + this.rescale = function() { + var scale = Math.min(windowDimensions.x / tthis.originalSize.w, windowDimensions.y / tthis.originalSize.h); + var newWidth = tthis.originalSize.w * scale; + var newHeight = tthis.originalSize.h * scale; + Overlays.editOverlay(tthis.overlay, { + x: (windowDimensions.x / 2) - (newWidth / 2), + y: (windowDimensions.y / 2) - (newHeight / 2), + width: newWidth, + height: newHeight + }); + }; + this.rescale(); +}; + +var theInstruction = new Instructions(HIFI_PUBLIC_BUCKET + "images/tutorial-goTo.svg", 457, 284.1); + +var firstControllerPlugged = false; + + +var cursor = new Cursor({visible: false}); +var keyboard = new Keyboard({visible: false}); +var textFontSize = 9; +var text = null; +var locationURL = ""; + +function appendChar(char) { + locationURL += char; + updateTextOverlay(); + Overlays.editOverlay(text, {text: locationURL}); +} + +function deleteChar() { + if (locationURL.length > 0) { + locationURL = locationURL.substring(0, locationURL.length - 1); + updateTextOverlay(); + } +} + +function updateTextOverlay() { + var maxLineWidth = Overlays.textSize(text, locationURL).width; + var suggestedFontSize = (windowDimensions.x / maxLineWidth) * textFontSize * 0.90; + var maxFontSize = 140; + textFontSize = (suggestedFontSize > maxFontSize) ? maxFontSize : suggestedFontSize; + var topMargin = (250 - textFontSize) / 4; + Overlays.editOverlay(text, {text: locationURL, font: {size: textFontSize}, topMargin: topMargin, visible: keyboard.visible}); + maxLineWidth = Overlays.textSize(text, locationURL).width; + Overlays.editOverlay(text, {leftMargin: (windowDimensions.x - maxLineWidth) / 2}); +} + +keyboard.onKeyPress = function(event) { + if (event.event == 'keypress') { + appendChar(event.char); + } +}; + +keyboard.onKeyRelease = function(event) { + // you can cancel a key by releasing its focusing before releasing it + if (event.focus) { + if (event.event == 'delete') { + deleteChar(); + } else if (event.event == 'submit' || event.event == 'enter') { + print("going to hifi://" + locationURL); + location = "hifi://" + locationURL; + locationURL = ""; + keyboard.hide(); + cursor.hide(); + updateTextOverlay(); + } + } +}; + +keyboard.onFullyLoaded = function() { + print("Virtual-keyboard fully loaded."); + var dimensions = Controller.getViewportDimensions(); + text = Overlays.addOverlay("text", { + x: 0, + y: dimensions.y - keyboard.height() - 260, + width: dimensions.x, + height: 250, + backgroundColor: { red: 255, green: 255, blue: 255}, + color: { red: 0, green: 0, blue: 0}, + topMargin: 5, + leftMargin: 0, + font: {size: textFontSize}, + text: "", + alpha: 0.8, + visible: keyboard.visible + }); + updateTextOverlay(); + // the cursor is being loaded after the keyboard, else it will be on the background of the keyboard + cursor.initialize(); + cursor.updateVisibility(keyboard.visible); + cursor.onUpdate = function(position) { + keyboard.setFocusPosition(position.x, position.y); + }; +}; + +function keyPressEvent(event) { + if (theInstruction.visible) { + return; + } + if (event.key === SPACEBAR_CHARCODE) { + keyboard.pressFocussedKey(); + } else if (event.key === ENTER_CHARCODE || event.key === RETURN_CHARCODE) { + keyboard.toggle(); + if (cursor !== undefined) { + cursor.updateVisibility(keyboard.visible); + } + Overlays.editOverlay(text, {visible: keyboard.visible}); + } +} + +function keyReleaseEvent(event) { + if (theInstruction.visible) { + return; + } + if (event.key === SPACEBAR_CHARCODE) { + keyboard.releaseKeys(); + } +} + +function scriptEnding() { + keyboard.remove(); + cursor.remove(); + Overlays.deleteOverlay(text); + Overlays.deleteOverlay(textSizeMeasureOverlay); + Controller.releaseKeyEvents({key: SPACEBAR_CHARCODE}); + Controller.releaseKeyEvents({key: RETURN_CHARCODE}); + Controller.releaseKeyEvents({key: ENTER_CHARCODE}); + theInstruction.remove(); +} + +function reportButtonValue(button, newValue, oldValue) { + if (theInstruction.visible) { + if (button == Joysticks.BUTTON_FACE_BOTTOM && newValue) { + theInstruction.remove(); + } + } else if (button == Joysticks.BUTTON_FACE_BOTTOM) { + if (newValue) { + keyboard.pressFocussedKey(); + } else { + keyboard.releaseKeys(); + } + } else if (button == Joysticks.BUTTON_FACE_RIGHT && newValue) { + deleteChar(); + } else if (button == Joysticks.BUTTON_FACE_LEFT && newValue) { + keyboard.hide(); + if (cursor !== undefined) { + cursor.hide(); + } + Overlays.editOverlay(text, {visible: false}); + } else if (button == Joysticks.BUTTON_RIGHT_SHOULDER && newValue) { + if (keyboard.visible) { + print("going to hifi://" + locationURL); + location = "hifi://" + locationURL; + locationURL = ""; + keyboard.hide(); + cursor.hide(); + updateTextOverlay(); + return; + } + keyboard.show(); + if (cursor !== undefined) { + cursor.show(); + } + Overlays.editOverlay(text, {visible: true}); + } +} + +function addJoystick(gamepad) { + gamepad.buttonStateChanged.connect(reportButtonValue); + if (!firstControllerPlugged) { + firstControllerPlugged = true; + theInstruction.show(); + } +} + +var allJoysticks = Joysticks.getAllJoysticks(); +for (var i = 0; i < allJoysticks.length; i++) { + addJoystick(allJoysticks[i]); +} + +Joysticks.joystickAdded.connect(addJoystick); +Controller.captureKeyEvents({key: RETURN_CHARCODE}); +Controller.captureKeyEvents({key: ENTER_CHARCODE}); +Controller.captureKeyEvents({key: SPACEBAR_CHARCODE}); +Controller.keyPressEvent.connect(keyPressEvent); +Controller.keyReleaseEvent.connect(keyReleaseEvent); +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/controllers/oculus/virtualKeyboardTextEntityExample.js b/examples/controllers/oculus/virtualKeyboardTextEntityExample.js new file mode 100644 index 0000000000..c3cb3c6316 --- /dev/null +++ b/examples/controllers/oculus/virtualKeyboardTextEntityExample.js @@ -0,0 +1,183 @@ +// +// virtualKeyboardTextEntityExample.js +// examples +// +// Created by Thijs Wenker on 12/28/14. +// Copyright 2014 High Fidelity, Inc. +// +// Control a virtual keyboard using your favorite HMD. +// Usage: Enable VR-mode and go to First person mode, +// look at the key that you would like to press, and press the spacebar on your "REAL" keyboard. +// +// leased some code from newEditEntities.js for Text Entity example +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../../libraries/globals.js"); +Script.include("../../libraries/virtualKeyboard.js"); + +const SPAWN_DISTANCE = 1; +const DEFAULT_TEXT_DIMENSION_Z = 0.02; + +const TEXT_MARGIN_TOP = 0.15; +const TEXT_MARGIN_LEFT = 0.15; +const TEXT_MARGIN_RIGHT = 0.17; +const TEXT_MARGIN_BOTTOM = 0.17; + +var windowDimensions = Controller.getViewportDimensions(); +var cursor = new Cursor(); +var keyboard = new Keyboard(); +var textFontSize = 9; +var text = null; +var textText = ""; +var textSizeMeasureOverlay = Overlays.addOverlay("text3d", {visible: false}); + +function appendChar(char) { + textText += char; + updateTextOverlay(); + Overlays.editOverlay(text, {text: textText}); +} + +function deleteChar() { + if (textText.length > 0) { + textText = textText.substring(0, textText.length - 1); + updateTextOverlay(); + } +} + +function updateTextOverlay() { + var textLines = textText.split("\n"); + var suggestedFontSize = (windowDimensions.x / Overlays.textSize(text, textText).width) * textFontSize * 0.90; + var maxFontSize = 170 / textLines.length; + textFontSize = (suggestedFontSize > maxFontSize) ? maxFontSize : suggestedFontSize; + var topMargin = (250 - (textFontSize * textLines.length)) / 4; + Overlays.editOverlay(text, {text: textText, font: {size: textFontSize}, topMargin: topMargin}); + Overlays.editOverlay(text, {leftMargin: (windowDimensions.x - Overlays.textSize(text, textLines).width) / 2}); +} + +keyboard.onKeyPress = function(event) { + if (event.event == 'keypress') { + appendChar(event.char); + } else if (event.event == 'enter') { + appendChar("\n"); + } +}; + +keyboard.onKeyRelease = function(event) { + print("Key release event test"); + // you can cancel a key by releasing its focusing before releasing it + if (event.focus) { + if (event.event == 'delete') { + deleteChar(); + } else if (event.event == 'submit') { + print(textText); + + var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); + + var textLines = textText.split("\n"); + var maxLineWidth = Overlays.textSize(textSizeMeasureOverlay, textText).width; + var usernameLine = "--" + GlobalServices.myUsername; + var usernameWidth = Overlays.textSize(textSizeMeasureOverlay, usernameLine).width; + if (maxLineWidth < usernameWidth) { + maxLineWidth = usernameWidth; + } else { + var spaceableWidth = maxLineWidth - usernameWidth; + //TODO: WORKAROUND WARNING BELOW Fix this when spaces are not trimmed out of the textsize calculation anymore + var spaceWidth = Overlays.textSize(textSizeMeasureOverlay, "| |").width + - Overlays.textSize(textSizeMeasureOverlay, "||").width; + var numberOfSpaces = Math.floor(spaceableWidth / spaceWidth); + for (var i = 0; i < numberOfSpaces; i++) { + usernameLine = " " + usernameLine; + } + } + var dimension_x = maxLineWidth + TEXT_MARGIN_RIGHT + TEXT_MARGIN_LEFT; + if (position.x > 0 && position.y > 0 && position.z > 0) { + Entities.addEntity({ + type: "Text", + rotation: MyAvatar.orientation, + position: position, + dimensions: { x: dimension_x, y: (textLines.length + 1) * 0.14 + TEXT_MARGIN_TOP + TEXT_MARGIN_BOTTOM, z: DEFAULT_TEXT_DIMENSION_Z }, + backgroundColor: { red: 0, green: 0, blue: 0 }, + textColor: { red: 255, green: 255, blue: 255 }, + text: textText + "\n" + usernameLine, + lineHeight: 0.1 + }); + } + textText = ""; + updateTextOverlay(); + } + } +}; + +keyboard.onFullyLoaded = function() { + print("Virtual-keyboard fully loaded."); + var dimensions = Controller.getViewportDimensions(); + text = Overlays.addOverlay("text", { + x: 0, + y: dimensions.y - keyboard.height() - 260, + width: dimensions.x, + height: 250, + backgroundColor: { red: 255, green: 255, blue: 255}, + color: { red: 0, green: 0, blue: 0}, + topMargin: 5, + leftMargin: 0, + font: {size: textFontSize}, + text: "", + alpha: 0.8 + }); + updateTextOverlay(); + // the cursor is being loaded after the keyboard, else it will be on the background of the keyboard + cursor.initialize(); + cursor.onUpdate = function(position) { + keyboard.setFocusPosition(position.x, position.y); + }; +}; + +function keyPressEvent(event) { + if (event.key === SPACEBAR_CHARCODE) { + keyboard.pressFocussedKey(); + } +} + +function keyReleaseEvent(event) { + if (event.key === SPACEBAR_CHARCODE) { + keyboard.releaseKeys(); + } +} + +function scriptEnding() { + keyboard.remove(); + cursor.remove(); + Overlays.deleteOverlay(text); + Overlays.deleteOverlay(textSizeMeasureOverlay); + Controller.releaseKeyEvents({key: SPACEBAR_CHARCODE}); +} + +function reportButtonValue(button, newValue, oldValue) { + if (button == Joysticks.BUTTON_FACE_BOTTOM) { + if (newValue) { + keyboard.pressFocussedKey(); + } else { + keyboard.releaseKeys(); + } + } else if (button == Joysticks.BUTTON_FACE_RIGHT && newValue) { + deleteChar(); + } +} + +function addJoystick(gamepad) { + gamepad.buttonStateChanged.connect(reportButtonValue); +} + +var allJoysticks = Joysticks.getAllJoysticks(); +for (var i = 0; i < allJoysticks.length; i++) { + addJoystick(allJoysticks[i]); +} + +Joysticks.joystickAdded.connect(addJoystick); +Controller.captureKeyEvents({key: SPACEBAR_CHARCODE}); +Controller.keyPressEvent.connect(keyPressEvent); +Controller.keyReleaseEvent.connect(keyReleaseEvent); +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/editEntities.js b/examples/editEntities.js index f25a873992..61100b4556 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -127,7 +127,7 @@ var toolBar = (function () { height: toolHeight, alpha: 0.9, visible: true - }, true, false); + }); browseModelsButton = toolBar.addTool({ imageURL: toolIconUrl + "list-icon.svg", @@ -190,6 +190,7 @@ var toolBar = (function () { cameraManager.enable(); entityListTool.setVisible(true); gridTool.setVisible(true); + grid.setEnabled(true); propertiesTool.setVisible(true); Window.setFocus(); } @@ -261,6 +262,8 @@ var toolBar = (function () { toolBar.move(toolsX, toolsY); }; + var newModelButtonDown = false; + var browseModelsButtonDown = false; that.mousePressEvent = function (event) { var clickedOverlay, url, @@ -273,19 +276,14 @@ var toolBar = (function () { return true; } + // Handle these two buttons in the mouseRelease event handler so that we don't suppress a mouseRelease event from + // occurring when showing a modal dialog. if (newModelButton === toolBar.clicked(clickedOverlay)) { - url = Window.prompt("Model URL", modelURLs[Math.floor(Math.random() * modelURLs.length)]); - if (url !== null && url !== "") { - addModel(url); - } + newModelButtonDown = true; return true; } - if (browseModelsButton === toolBar.clicked(clickedOverlay)) { - url = Window.s3Browse(".*(fbx|FBX)"); - if (url !== null && url !== "") { - addModel(url); - } + browseModelsButtonDown = true; return true; } @@ -347,6 +345,7 @@ var toolBar = (function () { return true; } + if (newTextButton === toolBar.clicked(clickedOverlay)) { var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); @@ -366,10 +365,37 @@ var toolBar = (function () { return true; } - return false; }; + that.mouseReleaseEvent = function(event) { + var handled = false; + if (newModelButtonDown) { + var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + if (newModelButton === toolBar.clicked(clickedOverlay)) { + url = Window.prompt("Model URL", modelURLs[Math.floor(Math.random() * modelURLs.length)]); + if (url !== null && url !== "") { + addModel(url); + } + handled = true; + } + } else if (browseModelsButtonDown) { + var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + if (browseModelsButton === toolBar.clicked(clickedOverlay)) { + url = Window.s3Browse(".*(fbx|FBX)"); + if (url !== null && url !== "") { + addModel(url); + } + handled = true; + } + } + + newModelButtonDown = false; + browseModelsButtonDown = false; + + return handled; + } + that.cleanup = function () { toolBar.cleanup(); }; @@ -532,8 +558,13 @@ function highlightEntityUnderCursor(position, accurateRay) { function mouseReleaseEvent(event) { + if (toolBar.mouseReleaseEvent(event)) { + return true; + } if (placingEntityID) { - selectionManager.setSelections([placingEntityID]); + if (isActive) { + selectionManager.setSelections([placingEntityID]); + } placingEntityID = null; } if (isActive && selectionManager.hasSelection()) { @@ -956,9 +987,18 @@ PropertiesTool = function(opts) { selectionManager.saveProperties(); for (var i = 0; i < selectionManager.selections.length; i++) { var properties = selectionManager.savedProperties[selectionManager.selections[i].id]; - Entities.editEntity(selectionManager.selections[i], { - dimensions: properties.naturalDimensions, - }); + var naturalDimensions = properties.naturalDimensions; + + // If any of the natural dimensions are not 0, resize + if (properties.type == "Model" && naturalDimensions.x == 0 + && naturalDimensions.y == 0 && naturalDimensions.z == 0) { + Window.alert("Cannot reset entity to its natural dimensions: Model URL" + + " is invalid or the model has not yet been loaded."); + } else { + Entities.editEntity(selectionManager.selections[i], { + dimensions: properties.naturalDimensions, + }); + } } pushCommandForSelections(); selectionManager._update(); diff --git a/examples/example/entities/butterflies.js b/examples/example/entities/butterflies.js index 336e128d83..9cbe8f966c 100644 --- a/examples/example/entities/butterflies.js +++ b/examples/example/entities/butterflies.js @@ -13,6 +13,9 @@ // +print("BUTTERFLIES START"); + + var numButterflies = 25; @@ -109,7 +112,7 @@ function updateButterflies(deltaTime) { var properties = Entities.getEntityProperties(butterflies[i]); if (Vec3.length(Vec3.subtract(properties.position, flockPosition)) > range) { Entities.editEntity(butterflies[i], { position: flockPosition } ); - } else if (properties.velocity.y < 0.0) { + } else if (properties.velocity.y <= 0.0) { // If falling, Create a new direction and impulse var HORIZ_SCALE = 0.50; var VERT_SCALE = 0.50; @@ -139,3 +142,5 @@ Script.scriptEnding.connect(function() { Entities.deleteEntity(butterflies[i]); } }); + +print("BUTTERFLIES END"); diff --git a/examples/html/gridControls.html b/examples/html/gridControls.html index 7e354c5bec..941a4b5c2a 100644 --- a/examples/html/gridControls.html +++ b/examples/html/gridControls.html @@ -30,8 +30,8 @@ elPosY.value = origin.y.toFixed(2); } - if (data.minorGridSpacing !== undefined) { - elMinorSpacing.value = data.minorGridSpacing; + if (data.minorGridWidth !== undefined) { + elMinorSpacing.value = data.minorGridWidth; } if (data.majorGridEvery !== undefined) { @@ -57,7 +57,7 @@ origin: { y: elPosY.value, }, - minorGridSpacing: elMinorSpacing.value, + minorGridWidth: elMinorSpacing.value, majorGridEvery: elMajorSpacing.value, gridColor: gridColor, colorIndex: gridColorIndex, diff --git a/examples/controllers/oculus/virtualKeyboard.js b/examples/libraries/virtualKeyboard.js similarity index 61% rename from examples/controllers/oculus/virtualKeyboard.js rename to examples/libraries/virtualKeyboard.js index d17b36ae4f..a1f952a5eb 100644 --- a/examples/controllers/oculus/virtualKeyboard.js +++ b/examples/libraries/virtualKeyboard.js @@ -9,179 +9,50 @@ // Usage: Enable VR-mode and go to First person mode, // look at the key that you would like to press, and press the spacebar on your "REAL" keyboard. // -// leased some code from newEditEntities.js for Text Entity example -// // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +// experimental 3dmode +THREE_D_MODE = false; -const KBD_UPPERCASE_DEFAULT = 0; -const KBD_LOWERCASE_DEFAULT = 1; -const KBD_UPPERCASE_HOVER = 2; -const KBD_LOWERCASE_HOVER = 3; -const KBD_BACKGROUND = 4; +KBD_UPPERCASE_DEFAULT = 0; +KBD_LOWERCASE_DEFAULT = 1; +KBD_UPPERCASE_HOVER = 2; +KBD_LOWERCASE_HOVER = 3; +KBD_BACKGROUND = 4; -const KEYBOARD_URL = HIFI_PUBLIC_BUCKET + "images/keyboard.svg"; -const CURSOR_URL = HIFI_PUBLIC_BUCKET + "images/cursor.svg"; +KEYBOARD_URL = HIFI_PUBLIC_BUCKET + "images/keyboard.svg"; +CURSOR_URL = HIFI_PUBLIC_BUCKET + "images/cursor.svg"; -const SPACEBAR_CHARCODE = 32; +RETURN_CHARCODE = 0x01000004; +ENTER_CHARCODE = 0x01000005; +SPACEBAR_CHARCODE = 32; -const KEYBOARD_WIDTH = 1174.7; -const KEYBOARD_HEIGHT = 434.1; +KEYBOARD_WIDTH = 1174.7; +KEYBOARD_HEIGHT = 434.1; -const CURSOR_WIDTH = 33.9; -const CURSOR_HEIGHT = 33.9; +CURSOR_WIDTH = 33.9; +CURSOR_HEIGHT = 33.9; + +KEYBOARD_SCALE_MULTIPLIER = 0.50; // VIEW_ANGLE can be adjusted to your likings, the smaller the faster movement. // Try setting it to 60 if it goes too fast for you. const VIEW_ANGLE = 40.0; const VIEW_ANGLE_BY_TWO = VIEW_ANGLE / 2; -const SPAWN_DISTANCE = 1; -const DEFAULT_TEXT_DIMENSION_Z = 0.02; - const BOUND_X = 0; const BOUND_Y = 1; const BOUND_W = 2; const BOUND_H = 3; -const KEY_STATE_LOWER = 0; -const KEY_STATE_UPPER = 1; - -const TEXT_MARGIN_TOP = 0.15; -const TEXT_MARGIN_LEFT = 0.15; -const TEXT_MARGIN_RIGHT = 0.17; -const TEXT_MARGIN_BOTTOM = 0.17; +KEY_STATE_LOWER = 0; +KEY_STATE_UPPER = 1; var windowDimensions = Controller.getViewportDimensions(); -var cursor = null; -var keyboard = new Keyboard(); -var textFontSize = 9; -var text = null; -var textText = ""; -var textSizeMeasureOverlay = Overlays.addOverlay("text3d", {visible: false}); -function appendChar(char) { - textText += char; - updateTextOverlay(); - Overlays.editOverlay(text, {text: textText}); -} - -function deleteChar() { - if (textText.length > 0) { - textText = textText.substring(0, textText.length - 1); - updateTextOverlay(); - } -} - -function updateTextOverlay() { - var textLines = textText.split("\n"); - var maxLineWidth = 0; - for (textLine in textLines) { - var lineWidth = Overlays.textSize(text, textLines[textLine]).width; - if (lineWidth > maxLineWidth) { - maxLineWidth = lineWidth; - } - } - var suggestedFontSize = (windowDimensions.x / maxLineWidth) * textFontSize * 0.90; - var maxFontSize = 190 / textLines.length; - textFontSize = (suggestedFontSize > maxFontSize) ? maxFontSize : suggestedFontSize; - var topMargin = (250 - (textFontSize * textLines.length)) / 4; - Overlays.editOverlay(text, {text: textText, font: {size: textFontSize}, topMargin: topMargin}); - var maxLineWidth = 0; - for (textLine in textLines) { - var lineWidth = Overlays.textSize(text, textLines[textLine]).width; - if (lineWidth > maxLineWidth) { - maxLineWidth = lineWidth; - } - } - Overlays.editOverlay(text, {leftMargin: (windowDimensions.x - maxLineWidth) / 2}); -} - -keyboard.onKeyPress = function(event) { - if (event.event == 'keypress') { - appendChar(event.char); - } else if (event.event == 'enter') { - appendChar("\n"); - } -}; - -keyboard.onKeyRelease = function(event) { - print("Key release event test"); - // you can cancel a key by releasing its focusing before releasing it - if (event.focus) { - if (event.event == 'delete') { - deleteChar(); - } else if (event.event == 'submit') { - print(textText); - - var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); - - var textLines = textText.split("\n"); - var maxLineWidth = 0; - for (textLine in textLines) { - var lineWidth = Overlays.textSize(textSizeMeasureOverlay, textLines[textLine]).width; - if (lineWidth > maxLineWidth) { - maxLineWidth = lineWidth; - } - } - var usernameLine = "--" + GlobalServices.myUsername; - var usernameWidth = Overlays.textSize(textSizeMeasureOverlay, usernameLine).width; - if (maxLineWidth < usernameWidth) { - maxLineWidth = usernameWidth; - } else { - var spaceableWidth = maxLineWidth - usernameWidth; - var spaceWidth = Overlays.textSize(textSizeMeasureOverlay, " ").width; - var numberOfSpaces = Math.floor(spaceableWidth / spaceWidth); - for (var i = 0; i < numberOfSpaces; i++) { - usernameLine = " " + usernameLine; - } - } - var dimension_x = maxLineWidth + TEXT_MARGIN_RIGHT + TEXT_MARGIN_LEFT; - if (position.x > 0 && position.y > 0 && position.z > 0) { - Entities.addEntity({ - type: "Text", - rotation: MyAvatar.orientation, - position: position, - dimensions: { x: dimension_x, y: (textLines.length + 1) * 0.14 + TEXT_MARGIN_TOP + TEXT_MARGIN_BOTTOM, z: DEFAULT_TEXT_DIMENSION_Z }, - backgroundColor: { red: 0, green: 0, blue: 0 }, - textColor: { red: 255, green: 255, blue: 255 }, - text: textText + "\n" + usernameLine - }); - } - textText = ""; - updateTextOverlay(); - } - } -}; - -keyboard.onFullyLoaded = function() { - print("Virtual-keyboard fully loaded."); - var dimensions = Controller.getViewportDimensions(); - text = Overlays.addOverlay("text", { - x: 0, - y: dimensions.y - keyboard.height() - 260, - width: dimensions.x, - height: 250, - backgroundColor: { red: 255, green: 255, blue: 255}, - color: { red: 0, green: 0, blue: 0}, - topMargin: 5, - leftMargin: 0, - font: {size: textFontSize}, - text: "", - alpha: 0.8 - }); - updateTextOverlay(); - // the cursor is being loaded after the keyboard, else it will be on the background of the keyboard - cursor = new Cursor(); - cursor.onUpdate = function(position) { - keyboard.setFocusPosition(position.x, position.y); - }; -}; - -function KeyboardKey(keyboard, keyProperties) { +KeyboardKey = (function(keyboard, keyProperties) { var tthis = this; this._focus = false; this._beingPressed = false; @@ -247,6 +118,11 @@ function KeyboardKey(keyboard, keyProperties) { }); } }; + this.updateVisibility = function() { + for (var i = 0; i < tthis.bounds.length; i++) { + Overlays.editOverlay(tthis.overlays[i], {visible: tthis.keyboard.visible}); + } + }; this.rescale = function() { for (var i = 0; i < tthis.bounds.length; i++) { Overlays.editOverlay(tthis.overlays[i], { @@ -271,24 +147,48 @@ function KeyboardKey(keyboard, keyProperties) { return true; }; for (var i = 0; i < this.bounds.length; i++) { - var newOverlay = Overlays.cloneOverlay(this.keyboard.background); - Overlays.editOverlay(newOverlay, { - x: this.keyboard.getX() + this.bounds[i][BOUND_X] * keyboard.scale, - y: this.keyboard.getY() + this.bounds[i][BOUND_Y] * keyboard.scale, - width: this.bounds[i][BOUND_W] * keyboard.scale, - height: this.bounds[i][BOUND_H] * keyboard.scale, - subImage: {width: this.bounds[i][BOUND_W], height: this.bounds[i][BOUND_H], x: this.bounds[i][BOUND_X], y: (KEYBOARD_HEIGHT * this.keyState) + this.bounds[i][BOUND_Y]}, - alpha: 1 - }); - this.overlays.push(newOverlay); + if (THREE_D_MODE) { + this.overlays.push(Overlays.addOverlay("billboard", { + scale: 1, + rotation: MyAvatar.rotation, + isFacingAvatar: false, + url: KEYBOARD_URL, + alpha: 1, + position: { + x: MyAvatar.position.x,// + this.bounds[i][BOUND_X] * 0.01,// /*+ this.keyboard.getX()*/ + this.bounds[i][BOUND_X] * keyboard.scale, + y: MyAvatar.position.y,// - this.bounds[i][BOUND_Y] * 0.01,// /*+ this.keyboard.getY()*/ + this.bounds[i][BOUND_Y] * keyboard.scale, + z: MyAvatar.position.z + }, + width: this.bounds[i][BOUND_W] * keyboard.scale, + height: this.bounds[i][BOUND_H] * keyboard.scale, + subImage: {width: this.bounds[i][BOUND_W], height: this.bounds[i][BOUND_H], x: this.bounds[i][BOUND_X], y: (KEYBOARD_HEIGHT * this.keyState) + this.bounds[i][BOUND_Y]}, + alpha: 1, + visible: tthis.keyboard.visible + })); + } else { + this.overlays.push(Overlays.addOverlay("image", { + imageURL: KEYBOARD_URL, + x: this.keyboard.getX() + this.bounds[i][BOUND_X] * keyboard.scale, + y: this.keyboard.getY() + this.bounds[i][BOUND_Y] * keyboard.scale, + width: this.bounds[i][BOUND_W] * keyboard.scale, + height: this.bounds[i][BOUND_H] * keyboard.scale, + subImage: {width: this.bounds[i][BOUND_W], height: this.bounds[i][BOUND_H], x: this.bounds[i][BOUND_X], y: (KEYBOARD_HEIGHT * this.keyState) + this.bounds[i][BOUND_Y]}, + alpha: 1, + visible: tthis.keyboard.visible + })); + } } -} +}); -function Keyboard() { +Keyboard = (function(params) { + if (params === undefined) { + params = {}; + } var tthis = this; this.focussed_key = -1; - this.scale = windowDimensions.x / KEYBOARD_WIDTH; + this.scale = (windowDimensions.x / KEYBOARD_WIDTH) * KEYBOARD_SCALE_MULTIPLIER; this.shift = false; + this.visible = params.visible != undefined ? params.visible : true; this.width = function() { return KEYBOARD_WIDTH * tthis.scale; }; @@ -301,17 +201,33 @@ function Keyboard() { this.getY = function() { return windowDimensions.y - this.height(); }; - this.background = Overlays.addOverlay("image", { - x: this.getX(), - y: this.getY(), - width: this.width(), - height: this.height(), - subImage: {width: KEYBOARD_WIDTH, height: KEYBOARD_HEIGHT, y: KEYBOARD_HEIGHT * KBD_BACKGROUND}, - imageURL: KEYBOARD_URL, - alpha: 1 - }); + if (THREE_D_MODE) { + this.background = Overlays.addOverlay("billboard", { + scale: 1, + position: MyAvatar.position, + rotation: MyAvatar.rotation, + width: this.width(), + height: this.height(), + subImage: {width: KEYBOARD_WIDTH, height: KEYBOARD_HEIGHT, y: KEYBOARD_HEIGHT * KBD_BACKGROUND}, + isFacingAvatar: false, + url: KEYBOARD_URL, + alpha: 1, + visible: this.visible + }); + } else { + this.background = Overlays.addOverlay("image", { + x: this.getX(), + y: this.getY(), + width: this.width(), + height: this.height(), + subImage: {width: KEYBOARD_WIDTH, height: KEYBOARD_HEIGHT, y: KEYBOARD_HEIGHT * KBD_BACKGROUND}, + imageURL: KEYBOARD_URL, + alpha: 1, + visible: this.visible + }); + } this.rescale = function() { - this.scale = windowDimensions.x / KEYBOARD_WIDTH; + this.scale = (windowDimensions.x / KEYBOARD_WIDTH) * KEYBOARD_SCALE_MULTIPLIER; Overlays.editOverlay(tthis.background, { x: this.getX(), y: this.getY(), @@ -349,6 +265,9 @@ function Keyboard() { }; this.pressFocussedKey = function() { + if (!tthis.visible) { + return tthis; + } if (tthis.focussed_key != -1) { if (tthis.keys[tthis.focussed_key].event == 'shift') { tthis.toggleShift(); @@ -402,6 +321,32 @@ function Keyboard() { for (var i = 0; i < this.keys.length; i++) { this.keys[i].remove(); } + // resets the cursor and magnifier + this.updateVisibility(false); + }; + + this.show = function() { + tthis.updateVisibility(true); + }; + + this.hide = function() { + tthis.updateVisibility(false); + }; + + this.toggle = function() { + tthis.updateVisibility(!tthis.visible); + }; + + this.updateVisibility = function(visible) { + tthis.visible = visible; + if (HMD.magnifier == visible) { + HMD.toggleMagnifier(); + } + Window.cursorVisible = !visible; + Overlays.editOverlay(tthis.background, { visible: tthis.visible }); + for (var i = 0; i < this.keys.length; i++) { + this.keys[i].updateVisibility(); + } }; this.onKeyPress = null; @@ -502,97 +447,94 @@ function Keyboard() { {bounds: [[899, 355, 263, 67]], event: 'submit'} ]; - + for (var i = 0; i < keyProperties.length; i++) { + this.keys.push(new KeyboardKey(this, keyProperties[i])); + } this.keyboardTextureLoaded = function() { if (Overlays.isLoaded(tthis.background)) { Script.clearInterval(tthis.keyboardTextureLoaded_timer); - for (var i = 0; i < keyProperties.length; i++) { - tthis.keys.push(new KeyboardKey(tthis, keyProperties[i])); - } if (keyboard.onFullyLoaded != null) { tthis.onFullyLoaded(); } } }; this.keyboardTextureLoaded_timer = Script.setInterval(this.keyboardTextureLoaded, 250); -} +}); -function Cursor() { +Cursor = (function(params) { + if (params === undefined) { + params = {}; + } var tthis = this; - this.x = windowDimensions.x / 2; - this.y = windowDimensions.y / 2; - this.overlay = Overlays.addOverlay("image", { - x: this.x, - y: this.y, - width: CURSOR_WIDTH, - height: CURSOR_HEIGHT, - imageURL: CURSOR_URL, - alpha: 1 - }); - this.remove = function() { - Overlays.deleteOverlay(this.overlay); + this.initialize = function() { + this.visible = params.visible != undefined ? params.visible : true; + this.x = windowDimensions.x / 2; + this.y = windowDimensions.y / 2; + this.overlay = Overlays.addOverlay("image", { + x: this.x, + y: this.y, + width: CURSOR_WIDTH, + height: CURSOR_HEIGHT, + imageURL: CURSOR_URL, + alpha: 1, + visible: this.visible + }); + this.remove = function() { + Overlays.deleteOverlay(this.overlay); + }; + this.getPosition = function() { + return {x: tthis.getX(), y: tthis.getY()}; + }; + this.getX = function() { + return tthis.x; + }; + this.getY = function() { + return tthis.y; + }; + this.show = function() { + tthis.updateVisibility(true); + }; + this.hide = function() { + tthis.updateVisibility(false); + }; + this.toggle = function() { + tthis.updateVisibility(!tthis.visible); + }; + this.updateVisibility = function(visible) { + tthis.visible = visible; + Overlays.editOverlay(this.overlay, { visible: tthis.visible }); + }; + this.onUpdate = null; + this.update = function() { + var newWindowDimensions = Controller.getViewportDimensions(); + if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) { + windowDimensions = newWindowDimensions; + keyboard.rescale(); + Overlays.editOverlay(text, { + y: windowDimensions.y - keyboard.height() - 260, + width: windowDimensions.x + }); + } + var editobject = {}; + var hudLookatPosition = HMD.getHUDLookAtPosition2D(); + if (hudLookatPosition === null) { + return; + } + if (tthis.x !== hudLookatPosition.x) { + tthis.x = hudLookatPosition.x; + editobject.x = tthis.x - (CURSOR_WIDTH / 2); + } + if (tthis.y !== hudLookatPosition.y) { + tthis.y = hudLookatPosition.y; + editobject.y = tthis.y - (CURSOR_HEIGHT / 2); + } + if (Object.keys(editobject).length > 0) { + Overlays.editOverlay(tthis.overlay, editobject); + if (tthis.onUpdate != null) { + tthis.onUpdate(tthis.getPosition()); + } + } + }; + Script.update.connect(this.update); }; - this.getPosition = function() { - return {x: tthis.getX(), y: tthis.getY()}; - }; - this.getX = function() { - return tthis.x; - }; - this.getY = function() { - return tthis.y; - }; - this.onUpdate = null; - this.update = function() { - var newWindowDimensions = Controller.getViewportDimensions(); - if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) { - windowDimensions = newWindowDimensions; - keyboard.rescale(); - Overlays.editOverlay(text, { - y: windowDimensions.y - keyboard.height() - 260, - width: windowDimensions.x - }); - } - var editobject = {}; - if (MyAvatar.getHeadFinalYaw() <= VIEW_ANGLE_BY_TWO && MyAvatar.getHeadFinalYaw() >= -1 * VIEW_ANGLE_BY_TWO) { - angle = ((-1 * MyAvatar.getHeadFinalYaw()) + VIEW_ANGLE_BY_TWO) / VIEW_ANGLE; - tthis.x = angle * windowDimensions.x; - editobject.x = tthis.x - (CURSOR_WIDTH / 2); - } - if (MyAvatar.getHeadFinalPitch() <= VIEW_ANGLE_BY_TWO && MyAvatar.getHeadFinalPitch() >= -1 * VIEW_ANGLE_BY_TWO) { - angle = ((-1 * MyAvatar.getHeadFinalPitch()) + VIEW_ANGLE_BY_TWO) / VIEW_ANGLE; - tthis.y = angle * windowDimensions.y; - editobject.y = tthis.y - (CURSOR_HEIGHT / 2); - } - if (Object.keys(editobject).length > 0) { - Overlays.editOverlay(tthis.overlay, editobject); - if (tthis.onUpdate != null) { - tthis.onUpdate(tthis.getPosition()); - } - } - }; - Script.update.connect(this.update); -} - -function keyPressEvent(event) { - if (event.key === SPACEBAR_CHARCODE) { - keyboard.pressFocussedKey(); - } -} - -function keyReleaseEvent(event) { - if (event.key === SPACEBAR_CHARCODE) { - keyboard.releaseKeys(); - } -} - -function scriptEnding() { - keyboard.remove(); - cursor.remove(); - Overlays.deleteOverlay(text); - Overlays.deleteOverlay(textSizeMeasureOverlay); - Controller.releaseKeyEvents({key: SPACEBAR_CHARCODE}); -} -Controller.captureKeyEvents({key: SPACEBAR_CHARCODE}); -Controller.keyPressEvent.connect(keyPressEvent); -Controller.keyReleaseEvent.connect(keyReleaseEvent); -Script.scriptEnding.connect(scriptEnding); \ No newline at end of file +}); diff --git a/gvr-interface/src/Client.cpp b/gvr-interface/src/Client.cpp index e323ab96b5..65238ad784 100644 --- a/gvr-interface/src/Client.cpp +++ b/gvr-interface/src/Client.cpp @@ -9,8 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - #include #include #include diff --git a/gvr-interface/src/Client.h b/gvr-interface/src/Client.h index 6eb3913ea9..6fbe40f165 100644 --- a/gvr-interface/src/Client.h +++ b/gvr-interface/src/Client.h @@ -16,13 +16,14 @@ #include -class QThread; - class Client : public QObject { Q_OBJECT public: Client(QObject* parent = 0); + + virtual void cleanupBeforeQuit() = 0; protected: + void setupNetworking(); virtual void processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket); private slots: diff --git a/gvr-interface/src/GVRInterface.cpp b/gvr-interface/src/GVRInterface.cpp index 7476bfc764..3d58396322 100644 --- a/gvr-interface/src/GVRInterface.cpp +++ b/gvr-interface/src/GVRInterface.cpp @@ -86,6 +86,13 @@ GVRInterface::GVRInterface(int argc, char* argv[]) : QTimer* idleTimer = new QTimer(this); connect(idleTimer, &QTimer::timeout, this, &GVRInterface::idle); idleTimer->start(0); + + // call our quit handler before we go down + connect(this, &QCoreApplication::aboutToQuit, this, &GVRInterface::handleApplicationQuit); +} + +void GVRInterface::handleApplicationQuit() { + _client->cleanupBeforeQuit(); } void GVRInterface::idle() { diff --git a/gvr-interface/src/GVRInterface.h b/gvr-interface/src/GVRInterface.h index 926c44da15..9ffbd52909 100644 --- a/gvr-interface/src/GVRInterface.h +++ b/gvr-interface/src/GVRInterface.h @@ -53,6 +53,7 @@ private slots: void handleApplicationStateChange(Qt::ApplicationState state); void idle(); private: + void handleApplicationQuit(); void enterVRMode(); void leaveVRMode(); diff --git a/gvr-interface/src/RenderingClient.cpp b/gvr-interface/src/RenderingClient.cpp index 9737263eee..e6d2f6b585 100644 --- a/gvr-interface/src/RenderingClient.cpp +++ b/gvr-interface/src/RenderingClient.cpp @@ -37,7 +37,7 @@ RenderingClient::RenderingClient(QObject *parent, const QString& launchURLString DependencyManager::set(); // get our audio client setup on its own thread - QThread* audioThread = new QThread(this); + QThread* audioThread = new QThread(); auto audioClient = DependencyManager::set(); audioClient->setPositionGetter(getPositionForAudio); @@ -45,6 +45,8 @@ RenderingClient::RenderingClient(QObject *parent, const QString& launchURLString audioClient->moveToThread(audioThread); connect(audioThread, &QThread::started, audioClient.data(), &AudioClient::start); + connect(audioClient.data(), &AudioClient::destroyed, audioThread, &QThread::quit); + connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater); audioThread->start(); @@ -68,15 +70,13 @@ void RenderingClient::sendAvatarPacket() { _fakeAvatar.sendIdentityPacket(); } -RenderingClient::~RenderingClient() { - auto audioClient = DependencyManager::get(); - - // stop the audio client - QMetaObject::invokeMethod(audioClient.data(), "stop", Qt::BlockingQueuedConnection); +void RenderingClient::cleanupBeforeQuit() { - // ask the audio thread to quit and wait until it is done - audioClient->thread()->quit(); - audioClient->thread()->wait(); + QMetaObject::invokeMethod(DependencyManager::get().data(), + "stop", Qt::BlockingQueuedConnection); + + // destroy the AudioClient so it and its thread will safely go down + DependencyManager::destroy(); } void RenderingClient::processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket) { diff --git a/gvr-interface/src/RenderingClient.h b/gvr-interface/src/RenderingClient.h index 870bde748f..c4724bc086 100644 --- a/gvr-interface/src/RenderingClient.h +++ b/gvr-interface/src/RenderingClient.h @@ -26,7 +26,6 @@ class RenderingClient : public Client { Q_OBJECT public: RenderingClient(QObject* parent = 0, const QString& launchURLString = QString()); - ~RenderingClient(); const glm::vec3& getPosition() const { return _position; } const glm::quat& getOrientation() const { return _orientation; } @@ -35,6 +34,8 @@ public: static glm::vec3 getPositionForAudio() { return _instance->getPosition(); } static glm::quat getOrientationForAudio() { return _instance->getOrientation(); } + virtual void cleanupBeforeQuit(); + private slots: void goToLocation(const glm::vec3& newPosition, bool hasOrientationChange, const glm::quat& newOrientation, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 474913fefb..ce33b04777 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -65,6 +65,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +111,7 @@ #include "scripting/AccountScriptingInterface.h" #include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" +#include "scripting/HMDScriptingInterface.h" #include "scripting/JoystickScriptingInterface.h" #include "scripting/GlobalServicesScriptingInterface.h" #include "scripting/LocationScriptingInterface.h" @@ -211,6 +213,8 @@ bool setupEssentials(int& argc, char** argv) { auto addressManager = DependencyManager::set(); auto nodeList = DependencyManager::set(NodeType::Agent, listenPort); auto geometryCache = DependencyManager::set(); + auto scriptCache = DependencyManager::set(); + auto soundCache = DependencyManager::set(); auto glowEffect = DependencyManager::set(); auto faceshift = DependencyManager::set(); auto audio = DependencyManager::set(); @@ -328,7 +332,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), &_datagramProcessor, SLOT(processDatagrams())); // put the audio processing on a separate thread - QThread* audioThread = new QThread(this); + QThread* audioThread = new QThread(); audioThread->setObjectName("Audio Thread"); auto audioIO = DependencyManager::get(); @@ -338,7 +342,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : audioIO->moveToThread(audioThread); connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start); - connect(audioIO.data(), SIGNAL(muteToggled()), this, SLOT(audioMuteToggled())); + connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit); + connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater); + connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled); audioThread->start(); @@ -516,53 +522,65 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : void Application::aboutToQuit() { _aboutToQuit = true; + + cleanupBeforeQuit(); } -Application::~Application() { +void Application::cleanupBeforeQuit() { + // make sure we don't call the idle timer any more + delete idleTimer; + + // save state QMetaObject::invokeMethod(&_settingsTimer, "stop", Qt::BlockingQueuedConnection); _settingsThread.quit(); saveSettings(); - - _entities.getTree()->setSimulation(NULL); - qInstallMessageHandler(NULL); - _window->saveGeometry(); - int DELAY_TIME = 1000; - UserActivityLogger::getInstance().close(DELAY_TIME); - - // make sure we don't call the idle timer any more - delete idleTimer; - + // TODO: now that this is in cleanupBeforeQuit do we really need it to stop and force + // an event loop to send the packet? + UserActivityLogger::getInstance().close(); + // let the avatar mixer know we're out MyAvatar::sendKillAvatar(); + + // stop the AudioClient + QMetaObject::invokeMethod(DependencyManager::get().data(), + "stop", Qt::BlockingQueuedConnection); + + // destroy the AudioClient so it and its thread have a chance to go down safely + DependencyManager::destroy(); +} + +Application::~Application() { + EntityTree* tree = _entities.getTree(); + tree->lockForWrite(); + _entities.getTree()->setSimulation(NULL); + tree->unlock(); + + qInstallMessageHandler(NULL); // ask the datagram processing thread to quit and wait until it is done _nodeThread->quit(); _nodeThread->wait(); - auto audioIO = DependencyManager::get(); - - // stop the audio process - QMetaObject::invokeMethod(audioIO.data(), "stop", Qt::BlockingQueuedConnection); - - // ask the audio thread to quit and wait until it is done - audioIO->thread()->quit(); - audioIO->thread()->wait(); - _octreeProcessor.terminate(); _entityEditSender.terminate(); Menu::getInstance()->deleteLater(); _myAvatar = NULL; + + ModelEntityItem::cleanupLoadedAnimations() ; DependencyManager::destroy(); + + qDebug() << "start destroying ResourceCaches Application::~Application() line:" << __LINE__; DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); + qDebug() << "done destroying ResourceCaches Application::~Application() line:" << __LINE__; } void Application::initializeGL() { @@ -2599,6 +2617,9 @@ const GLfloat WORLD_AMBIENT_COLOR[] = { 0.525f, 0.525f, 0.6f }; const GLfloat WORLD_DIFFUSE_COLOR[] = { 0.6f, 0.525f, 0.525f }; const GLfloat WORLD_SPECULAR_COLOR[] = { 0.94f, 0.94f, 0.737f, 1.0f }; +const glm::vec3 GLOBAL_LIGHT_COLOR = { 0.6f, 0.525f, 0.525f }; +const float GLOBAL_LIGHT_INTENSITY = 1.0f; + void Application::setupWorldLight() { // Setup 3D lights (after the camera transform, so that they are positioned in world space) @@ -2613,6 +2634,7 @@ void Application::setupWorldLight() { glLightfv(GL_LIGHT0, GL_SPECULAR, WORLD_SPECULAR_COLOR); glMaterialfv(GL_FRONT, GL_SPECULAR, WORLD_SPECULAR_COLOR); glMateriali(GL_FRONT, GL_SHININESS, 96); + } bool Application::shouldRenderMesh(float largestDimension, float distanceToCamera) { @@ -2819,7 +2841,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs { DependencyManager::get()->setAmbientLightMode(getRenderAmbientLight()); - + DependencyManager::get()->setGlobalLight(-getSunDirection(), GLOBAL_LIGHT_COLOR, GLOBAL_LIGHT_INTENSITY); PROFILE_RANGE("DeferredLighting"); PerformanceTimer perfTimer("lighting"); DependencyManager::get()->render(); @@ -3457,7 +3479,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("SoundCache", &SoundCache::getInstance()); + scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Metavoxels", &_metavoxels); @@ -3471,6 +3493,10 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("UndoStack", &_undoStackScriptingInterface); + QScriptValue hmdInterface = scriptEngine->registerGlobalObject("HMD", &HMDScriptingInterface::getInstance()); + scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition2D", HMDScriptingInterface::getHUDLookAtPosition2D, 0); + scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0); + #ifdef HAVE_RTMIDI scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance()); #endif diff --git a/interface/src/Application.h b/interface/src/Application.h index d015d09035..da29920ef9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -410,6 +410,8 @@ private: void initDisplay(); void init(); + + void cleanupBeforeQuit(); void update(float deltaTime); diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 9c56f8bc8d..02d3c62765 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -129,12 +129,12 @@ void DatagramProcessor::processDatagrams() { if (incomingType == PacketTypeMuteEnvironment) { glm::vec3 position; - float radius, distance; + float radius; int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment); memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3)); memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float)); - distance = glm::distance(DependencyManager::get()->getMyAvatar()->getPosition(), + float distance = glm::distance(DependencyManager::get()->getMyAvatar()->getPosition(), position); mute = mute && (distance < radius); diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 9b1470ad67..14d1939198 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -688,7 +688,7 @@ void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) { roll = 0.0f; #endif } - + glm::vec3 OculusManager::getRelativePosition() { #if (defined(__APPLE__) || defined(_WIN32)) && HAVE_LIBOVR ovrTrackingState trackingState = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp new file mode 100644 index 0000000000..fe274b6878 --- /dev/null +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -0,0 +1,56 @@ +// +// HMDScriptingInterface.cpp +// interface/src/scripting +// +// Created by Thijs Wenker on 1/12/15. +// Copyright 2015 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 +// + +#include "HMDScriptingInterface.h" + +#include + +HMDScriptingInterface& HMDScriptingInterface::getInstance() { + static HMDScriptingInterface sharedInstance; + return sharedInstance; +} + +bool HMDScriptingInterface::getHUDLookAtPosition3D(glm::vec3& result) const { + Camera* camera = Application::getInstance()->getCamera(); + glm::vec3 position = camera->getPosition(); + glm::quat orientation = camera->getOrientation(); + + glm::vec3 direction = orientation * glm::vec3(0.0f, 0.0f, -1.0f); + + ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); + + return applicationOverlay.calculateRayUICollisionPoint(position, direction, result); +} + +QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) { + + glm::vec3 hudIntersection; + + if ((&HMDScriptingInterface::getInstance())->getHUDLookAtPosition3D(hudIntersection)) { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + glm::vec3 sphereCenter = myAvatar->getDefaultEyePosition(); + glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * (hudIntersection - sphereCenter); + glm::quat rotation = ::rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), direction); + glm::vec3 eulers = ::safeEulerAngles(rotation); + return qScriptValueFromValue(engine, Application::getInstance()->getApplicationOverlay() + .sphericalToOverlay(glm::vec2(eulers.y, -eulers.x))); + } + return QScriptValue::NullValue; +} + +QScriptValue HMDScriptingInterface::getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine) { + glm::vec3 result; + HMDScriptingInterface* hmdInterface = &HMDScriptingInterface::getInstance(); + if ((&HMDScriptingInterface::getInstance())->getHUDLookAtPosition3D(result)) { + return qScriptValueFromValue(engine, result); + } + return QScriptValue::NullValue; +} diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h new file mode 100644 index 0000000000..bcd458ca5e --- /dev/null +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -0,0 +1,42 @@ +// +// HMDScriptingInterface.h +// interface/src/scripting +// +// Created by Thijs Wenker on 1/12/15. +// Copyright 2015 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 +// + +#ifndef hifi_HMDScriptingInterface_h +#define hifi_HMDScriptingInterface_h + +#include + +#include "Application.h" +#include "devices/OculusManager.h" + +class HMDScriptingInterface : public QObject { + Q_OBJECT + Q_PROPERTY(bool magnifier READ getMagnifier) + Q_PROPERTY(bool active READ isHMDMode) +public: + static HMDScriptingInterface& getInstance(); + + static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine); + static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine); + +public slots: + void toggleMagnifier() { Application::getInstance()->getApplicationOverlay().toggleMagnifier(); }; + +private: + HMDScriptingInterface() {}; + bool getMagnifier() const { return Application::getInstance()->getApplicationOverlay().hasMagnifier(); }; + bool isHMDMode() const { return Application::getInstance()->isHMDMode(); } + + bool getHUDLookAtPosition3D(glm::vec3& result) const; + +}; + +#endif // hifi_HMDScriptingInterface_h diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 18b9943e29..569ef0cf80 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -139,6 +139,7 @@ ApplicationOverlay::ApplicationOverlay() : _alpha(1.0f), _oculusUIRadius(1.0f), _crosshairTexture(0), + _magnifier(true), _previousBorderWidth(-1), _previousBorderHeight(-1), _previousMagnifierBottomLeft(), @@ -559,7 +560,7 @@ void ApplicationOverlay::renderPointers() { _reticlePosition[MOUSE] = position; _reticleActive[MOUSE] = true; - _magActive[MOUSE] = true; + _magActive[MOUSE] = _magnifier; _reticleActive[LEFT_CONTROLLER] = false; _reticleActive[RIGHT_CONTROLLER] = false; } else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { @@ -718,6 +719,9 @@ void ApplicationOverlay::renderPointersOculus(const glm::vec3& eyePos) { //Renders a small magnification of the currently bound texture at the coordinates void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder) { + if (!_magnifier) { + return; + } auto glCanvas = DependencyManager::get(); const int widgetWidth = glCanvas->width(); @@ -788,13 +792,12 @@ void ApplicationOverlay::renderAudioMeter() { // Audio VU Meter and Mute Icon const int MUTE_ICON_SIZE = 24; - const int AUDIO_METER_INSET = 2; const int MUTE_ICON_PADDING = 10; - const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - AUDIO_METER_INSET - MUTE_ICON_PADDING; - const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 * AUDIO_METER_INSET; + const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - MUTE_ICON_PADDING; + const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 ; const int AUDIO_METER_HEIGHT = 8; const int AUDIO_METER_GAP = 5; - const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_INSET + AUDIO_METER_GAP; + const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_GAP; int audioMeterY; bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !OculusManager::isConnected(); @@ -846,8 +849,8 @@ void ApplicationOverlay::renderAudioMeter() { audioMeterY += AUDIO_METER_HEIGHT; // Draw audio meter background Quad - DependencyManager::get()->renderQuad(AUDIO_METER_X, audioMeterY, AUDIO_METER_WIDTH, AUDIO_METER_HEIGHT, - glm::vec4(0, 0, 0, 1)); + DependencyManager::get()->renderQuad(AUDIO_METER_X, audioMeterY, AUDIO_METER_WIDTH, AUDIO_METER_HEIGHT, + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); if (audioLevel > AUDIO_RED_START) { glm::vec4 quadColor; @@ -857,10 +860,10 @@ void ApplicationOverlay::renderAudioMeter() { quadColor = glm::vec4(1, 1, 1, 1); } // Draw Red Quad - DependencyManager::get()->renderQuad(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, - audioMeterY + AUDIO_METER_INSET, + DependencyManager::get()->renderQuad(AUDIO_METER_X + AUDIO_RED_START, + audioMeterY, audioLevel - AUDIO_RED_START, - AUDIO_METER_HEIGHT - AUDIO_METER_INSET, quadColor, + AUDIO_METER_HEIGHT, quadColor, _audioRedQuad); audioLevel = AUDIO_RED_START; @@ -874,26 +877,28 @@ void ApplicationOverlay::renderAudioMeter() { quadColor = glm::vec4(1, 1, 1, 1); } // Draw Green Quad - DependencyManager::get()->renderQuad(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, - audioMeterY + AUDIO_METER_INSET, + DependencyManager::get()->renderQuad(AUDIO_METER_X + AUDIO_GREEN_START, + audioMeterY, audioLevel - AUDIO_GREEN_START, - AUDIO_METER_HEIGHT - AUDIO_METER_INSET, quadColor, + AUDIO_METER_HEIGHT, quadColor, _audioGreenQuad); audioLevel = AUDIO_GREEN_START; } - // Draw Blue Quad - glm::vec4 quadColor; - if (!isClipping) { - quadColor = AUDIO_METER_BLUE; - } else { - quadColor = glm::vec4(1, 1, 1, 1); + + if (audioLevel >= 0) { + glm::vec4 quadColor; + if (!isClipping) { + quadColor = AUDIO_METER_BLUE; + } else { + quadColor = glm::vec4(1, 1, 1, 1); + } + // Draw Blue (low level) quad + DependencyManager::get()->renderQuad(AUDIO_METER_X, + audioMeterY, + audioLevel, AUDIO_METER_HEIGHT, quadColor, + _audioBlueQuad); } - // Draw Blue (low level) quad - DependencyManager::get()->renderQuad(AUDIO_METER_X + AUDIO_METER_INSET, - audioMeterY + AUDIO_METER_INSET, - audioLevel, AUDIO_METER_HEIGHT - AUDIO_METER_INSET, quadColor, - _audioBlueQuad); } void ApplicationOverlay::renderStatsAndLogs() { diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 68edbda691..64161e4a31 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -37,6 +37,9 @@ public: QPoint getPalmClickLocation(const PalmData *palm) const; bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; + bool hasMagnifier() const { return _magnifier; } + void toggleMagnifier() { _magnifier = !_magnifier; } + float getOculusUIAngularSize() const { return _oculusUIAngularSize; } void setOculusUIAngularSize(float oculusUIAngularSize) { _oculusUIAngularSize = oculusUIAngularSize; } @@ -109,7 +112,8 @@ private: bool _magActive[NUMBER_OF_RETICLES]; float _magSizeMult[NUMBER_OF_RETICLES]; quint64 _lastMouseMove; - + bool _magnifier; + float _alpha; float _oculusUIRadius; float _trailingAudioLoudness; diff --git a/interface/src/ui/CachesSizeDialog.cpp b/interface/src/ui/CachesSizeDialog.cpp index 4d4457c922..d21d1c2db8 100644 --- a/interface/src/ui/CachesSizeDialog.cpp +++ b/interface/src/ui/CachesSizeDialog.cpp @@ -59,8 +59,8 @@ CachesSizeDialog::CachesSizeDialog(QWidget* parent) : void CachesSizeDialog::confirmClicked(bool checked) { DependencyManager::get()->setUnusedResourceCacheSize(_animations->value() * BYTES_PER_MEGABYTES); DependencyManager::get()->setUnusedResourceCacheSize(_geometries->value() * BYTES_PER_MEGABYTES); - ScriptCache::getInstance()->setUnusedResourceCacheSize(_scripts->value() * BYTES_PER_MEGABYTES); - SoundCache::getInstance().setUnusedResourceCacheSize(_sounds->value() * BYTES_PER_MEGABYTES); + DependencyManager::get()->setUnusedResourceCacheSize(_scripts->value() * BYTES_PER_MEGABYTES); + DependencyManager::get()->setUnusedResourceCacheSize(_sounds->value() * BYTES_PER_MEGABYTES); DependencyManager::get()->setUnusedResourceCacheSize(_textures->value() * BYTES_PER_MEGABYTES); QDialog::close(); @@ -69,8 +69,8 @@ void CachesSizeDialog::confirmClicked(bool checked) { void CachesSizeDialog::resetClicked(bool checked) { _animations->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); _geometries->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); - _scripts->setValue(ScriptCache::getInstance()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); - _sounds->setValue(SoundCache::getInstance().getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); + _scripts->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); + _sounds->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); _textures->setValue(DependencyManager::get()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES); } diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 049e5bd1cd..99b60fc232 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -41,6 +41,9 @@ LoginDialog::LoginDialog(QWidget* parent) : this, &LoginDialog::handleLoginClicked); connect(_ui->closeButton, &QPushButton::clicked, this, &LoginDialog::close); + + // Initialize toggle connection + toggleQAction(); }; LoginDialog::~LoginDialog() { diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index d2bfa1955b..6672a88e45 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -17,7 +17,9 @@ #include "Line3DOverlay.h" -Line3DOverlay::Line3DOverlay() { +Line3DOverlay::Line3DOverlay() : + _geometryCacheID(DependencyManager::get()->allocateID()) +{ } Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) : diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 6ee90b11cc..7a88b327d1 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -19,7 +19,9 @@ #include "Rectangle3DOverlay.h" -Rectangle3DOverlay::Rectangle3DOverlay() { +Rectangle3DOverlay::Rectangle3DOverlay() : + _geometryCacheID(DependencyManager::get()->allocateID()) +{ } Rectangle3DOverlay::Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay) : diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 391d6aaf40..b427d5ba42 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -141,6 +141,8 @@ AudioClient::AudioClient() : } AudioClient::~AudioClient() { + stop(); + if (_gverbLocal) { gverb_free(_gverbLocal); } @@ -494,7 +496,6 @@ void AudioClient::start() { } void AudioClient::stop() { - _inputFrameBuffer.finalize(); _inputGain.finalize(); _sourceGain.finalize(); @@ -892,13 +893,14 @@ void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArr void AudioClient::sendMuteEnvironmentPacket() { QByteArray mutePacket = byteArrayWithPopulatedHeader(PacketTypeMuteEnvironment); - QDataStream mutePacketStream(&mutePacket, QIODevice::Append); + int headerSize = mutePacket.size(); const float MUTE_RADIUS = 50; glm::vec3 currentSourcePosition = _positionGetter(); - mutePacketStream.writeBytes(reinterpret_cast(¤tSourcePosition), sizeof(glm::vec3)); - mutePacketStream.writeBytes(reinterpret_cast(&MUTE_RADIUS), sizeof(float)); + mutePacket.resize(mutePacket.size() + sizeof(glm::vec3) + sizeof(float)); + memcpy(mutePacket.data() + headerSize, ¤tSourcePosition, sizeof(glm::vec3)); + memcpy(mutePacket.data() + headerSize + sizeof(glm::vec3), &MUTE_RADIUS, sizeof(float)); // grab our audio mixer from the NodeList, if it exists auto nodelist = DependencyManager::get(); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index dc4e16d81b..0095a885eb 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -186,6 +186,10 @@ protected: AudioClient(); ~AudioClient(); + virtual void customDeleter() { + deleteLater(); + } + private: void outputFormatChanged(); diff --git a/libraries/audio/src/SoundCache.cpp b/libraries/audio/src/SoundCache.cpp index fe05372ce5..2949a6c70a 100644 --- a/libraries/audio/src/SoundCache.cpp +++ b/libraries/audio/src/SoundCache.cpp @@ -15,11 +15,6 @@ static int soundPointerMetaTypeId = qRegisterMetaType(); -SoundCache& SoundCache::getInstance() { - static SoundCache staticInstance; - return staticInstance; -} - SoundCache::SoundCache(QObject* parent) : ResourceCache(parent) { diff --git a/libraries/audio/src/SoundCache.h b/libraries/audio/src/SoundCache.h index f9fbf51c10..7942e16010 100644 --- a/libraries/audio/src/SoundCache.h +++ b/libraries/audio/src/SoundCache.h @@ -17,11 +17,11 @@ #include "Sound.h" /// Scriptable interface for sound loading. -class SoundCache : public ResourceCache { +class SoundCache : public ResourceCache, public Dependency { Q_OBJECT + SINGLETON_DEPENDENCY + public: - static SoundCache& getInstance(); - Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url); protected: diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 4db6cd5d79..51779d3f56 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -194,6 +194,7 @@ public: bool containsBoundsProperties() const { return (_positionChanged || _dimensionsChanged); } bool containsPositionChange() const { return _positionChanged; } bool containsDimensionsChange() const { return _dimensionsChanged; } + bool containsAnimationSettingsChange() const { return _animationSettingsChanged; } float getGlowLevel() const { return _glowLevel; } float getLocalRenderAlpha() const { return _localRenderAlpha; } @@ -256,12 +257,57 @@ inline void EntityItemProperties::setPosition(const glm::vec3& value) inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { - debug << "EntityItemProperties[" << "\n" - << " position:" << properties.getPosition() << "in meters" << "\n" - << " velocity:" << properties.getVelocity() << "in meters" << "\n" - << " last edited:" << properties.getLastEdited() << "\n" - << " edited ago:" << properties.getEditedAgo() << "\n" - << "]"; + debug << "EntityItemProperties[" << "\n"; + + // TODO: figure out why position and animationSettings don't seem to like the macro approach + if (properties.containsPositionChange()) { + debug << " position:" << properties.getPosition() << "in meters" << "\n"; + } + if (properties.containsAnimationSettingsChange()) { + debug << " animationSettings:" << properties.getAnimationSettings() << "\n"; + } + + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Dimensions, dimensions, "in meters"); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Velocity, velocity, "in meters"); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Visible, visible, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Rotation, rotation, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Density, density, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Gravity, gravity, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Damping, damping, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Lifetime, lifetime, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Script, script, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Color, color, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ModelURL, modelURL, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationURL, animationURL, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFPS, animationFPS, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFrameIndex, animationFrameIndex, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationIsPlaying, animationIsPlaying, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, RegistrationPoint, registrationPoint, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AngularVelocity, angularVelocity, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AngularDamping, angularDamping, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, IgnoreForCollisions, ignoreForCollisions, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, CollisionsWillMove, collisionsWillMove, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, IsSpotlight, isSpotlight, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, DiffuseColor, diffuseColor, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AmbientColor, ambientColor, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, SpecularColor, specularColor, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ConstantAttenuation, constantAttenuation, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, LinearAttenuation, linearAttenuation, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, QuadraticAttenuation, quadraticAttenuation, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Exponent, exponent, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Cutoff, cutoff, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Locked, locked, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Textures, textures, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, UserData, userData, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Text, text, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, LineHeight, lineHeight, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, TextColor, textColor, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundColor, backgroundColor, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ShapeType, shapeType, ""); + + debug << " last edited:" << properties.getLastEdited() << "\n"; + debug << " edited ago:" << properties.getEditedAgo() << "\n"; + debug << "]"; return debug; } diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 518035c0d3..592f808e1a 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -321,6 +321,10 @@ T _##n; \ bool _##n##Changed; +#define DEBUG_PROPERTY_IF_CHANGED(D, P, N, n, x) \ + if (P.n##Changed()) { \ + D << " " << #n << ":" << P.get##N() << x << "\n"; \ + } #endif // hifi_EntityItemPropertiesMacros_h diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 95617b4944..8bb759c1b4 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -592,6 +592,10 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char // if the EntityItem exists, then update it if (existingEntity) { + if (wantEditLogging()) { + qDebug() << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID; + qDebug() << " properties:" << properties; + } updateEntity(entityItemID, properties, senderNode->getCanAdjustLocks()); existingEntity->markAsChangedOnServer(); } else { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 3bc0986799..d8b9b9f38f 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -151,6 +151,9 @@ public: void emitEntityScriptChanging(const EntityItemID& entityItemID); void setSimulation(EntitySimulation* simulation); + + bool wantEditLogging() const { return _wantEditLogging; } + void setWantEditLogging(bool value) { _wantEditLogging = value; } signals: void deletingEntity(const EntityItemID& entityID); @@ -180,6 +183,8 @@ private: QHash _entityToElementMap; EntitySimulation* _simulation; + + bool _wantEditLogging = false; }; #endif // hifi_EntityTree_h diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 4d8e741cc6..ed76b8c99f 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -163,16 +163,6 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit QMap ModelEntityItem::_loadedAnimations; // TODO: improve cleanup by leveraging the AnimationPointer(s) -// This class/instance will cleanup the animations once unloaded. -class EntityAnimationsBookkeeper { -public: - ~EntityAnimationsBookkeeper() { - ModelEntityItem::cleanupLoadedAnimations(); - } -}; - -EntityAnimationsBookkeeper modelAnimationsBookkeeperInstance; - void ModelEntityItem::cleanupLoadedAnimations() { foreach(AnimationPointer animation, _loadedAnimations) { animation.clear(); diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 3502dd219f..72d153289a 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1127,7 +1127,7 @@ FBXTexture getTexture(const QString& textureID, texture.transform.setTranslation(p.translation); texture.transform.setRotation(glm::quat(glm::radians(p.rotation))); texture.transform.setScale(p.scaling); - if ((p.UVSet != "map1") || (p.UVSet != "UVSet0")) { + if ((p.UVSet != "map1") && (p.UVSet != "UVSet0")) { texture.texcoordSet = 1; } texture.texcoordSetName = p.UVSet; @@ -1556,6 +1556,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, if (property.name == propertyName) { QString v = property.properties.at(0).toString(); if (property.properties.at(0) == "UVSet") { + std::string uvName = property.properties.at(index).toString().toStdString(); tex.assign(tex.UVSet, property.properties.at(index).toString()); } else if (property.properties.at(0) == "CurrentTextureBlendMode") { tex.assign(tex.currentTextureBlendMode, property.properties.at(index).value()); diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 81c861fff8..43d8c991b6 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -85,8 +85,11 @@ bool Texture::Storage::allocateMip(uint16 level) { bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size size, const Byte* bytes) { // Ok we should be able to do that... allocateMip(level); - _mips[level]->_format = format; - Size allocated = _mips[level]->_sysmem.setData(size, bytes); + auto mip = _mips[level]; + mip->_format = format; + Size allocated = mip->_sysmem.setData(size, bytes); + mip->_isGPULoaded = false; + return allocated == size; } diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 642562bfb5..9c672a415e 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -465,9 +465,9 @@ void Bitstream::writeRawDelta(const QScriptValue& value, const QScriptValue& ref } else if (reference.isArray()) { if (value.isArray()) { *this << false; - int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + int length = value.property(DependencyManager::get()->getLengthString()).toInt32(); *this << length; - int referenceLength = reference.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + int referenceLength = reference.property(DependencyManager::get()->getLengthString()).toInt32(); for (int i = 0; i < length; i++) { if (i < referenceLength) { writeDelta(value.property(i), reference.property(i)); @@ -555,7 +555,7 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) } else { QVariant variant; readRawDelta(variant, reference.toVariant()); - value = ScriptCache::getInstance()->getEngine()->newVariant(variant); + value = DependencyManager::get()->getEngine()->newVariant(variant); } } else if (reference.isQObject()) { bool typeChanged; @@ -566,7 +566,7 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) } else { QObject* object; readRawDelta(object, reference.toQObject()); - value = ScriptCache::getInstance()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership); + value = DependencyManager::get()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership); } } else if (reference.isQMetaObject()) { bool typeChanged; @@ -577,7 +577,7 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) } else { const QMetaObject* metaObject; *this >> metaObject; - value = ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject); + value = DependencyManager::get()->getEngine()->newQMetaObject(metaObject); } } else if (reference.isDate()) { bool typeChanged; @@ -588,7 +588,7 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) } else { QDateTime dateTime; *this >> dateTime; - value = ScriptCache::getInstance()->getEngine()->newDate(dateTime); + value = DependencyManager::get()->getEngine()->newDate(dateTime); } } else if (reference.isRegExp()) { bool typeChanged; @@ -599,7 +599,7 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) } else { QRegExp regExp; *this >> regExp; - value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp); + value = DependencyManager::get()->getEngine()->newRegExp(regExp); } } else if (reference.isArray()) { bool typeChanged; @@ -610,8 +610,8 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) } else { int length; *this >> length; - value = ScriptCache::getInstance()->getEngine()->newArray(length); - int referenceLength = reference.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + value = DependencyManager::get()->getEngine()->newArray(length); + int referenceLength = reference.property(DependencyManager::get()->getLengthString()).toInt32(); for (int i = 0; i < length; i++) { QScriptValue element; if (i < referenceLength) { @@ -630,7 +630,7 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) } else { // start by shallow-copying the reference - value = ScriptCache::getInstance()->getEngine()->newObject(); + value = DependencyManager::get()->getEngine()->newObject(); for (QScriptValueIterator it(reference); it.hasNext(); ) { it.next(); value.setProperty(it.scriptName(), it.value()); @@ -1036,7 +1036,7 @@ Bitstream& Bitstream::operator<<(const QScriptValue& value) { } else if (value.isArray()) { writeScriptValueType(*this, ARRAY_SCRIPT_VALUE); - int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + int length = value.property(DependencyManager::get()->getLengthString()).toInt32(); *this << length; for (int i = 0; i < length; i++) { *this << value.property(i); @@ -1087,37 +1087,37 @@ Bitstream& Bitstream::operator>>(QScriptValue& value) { case VARIANT_SCRIPT_VALUE: { QVariant variantValue; *this >> variantValue; - value = ScriptCache::getInstance()->getEngine()->newVariant(variantValue); + value = DependencyManager::get()->getEngine()->newVariant(variantValue); break; } case QOBJECT_SCRIPT_VALUE: { QObject* object; *this >> object; - ScriptCache::getInstance()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership); + DependencyManager::get()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership); break; } case QMETAOBJECT_SCRIPT_VALUE: { const QMetaObject* metaObject; *this >> metaObject; - ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject); + DependencyManager::get()->getEngine()->newQMetaObject(metaObject); break; } case DATE_SCRIPT_VALUE: { QDateTime dateTime; *this >> dateTime; - value = ScriptCache::getInstance()->getEngine()->newDate(dateTime); + value = DependencyManager::get()->getEngine()->newDate(dateTime); break; } case REGEXP_SCRIPT_VALUE: { QRegExp regExp; *this >> regExp; - value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp); + value = DependencyManager::get()->getEngine()->newRegExp(regExp); break; } case ARRAY_SCRIPT_VALUE: { int length; *this >> length; - value = ScriptCache::getInstance()->getEngine()->newArray(length); + value = DependencyManager::get()->getEngine()->newArray(length); for (int i = 0; i < length; i++) { QScriptValue element; *this >> element; @@ -1126,7 +1126,7 @@ Bitstream& Bitstream::operator>>(QScriptValue& value) { break; } case OBJECT_SCRIPT_VALUE: { - value = ScriptCache::getInstance()->getEngine()->newObject(); + value = DependencyManager::get()->getEngine()->newObject(); forever { QScriptString name; *this >> name; @@ -1477,7 +1477,7 @@ Bitstream& Bitstream::operator>(QScriptString& string) { QString rawString; *this >> rawString; string = (rawString == INVALID_STRING) ? QScriptString() : - ScriptCache::getInstance()->getEngine()->toStringHandle(rawString); + DependencyManager::get()->getEngine()->toStringHandle(rawString); return *this; } @@ -1828,7 +1828,7 @@ QJsonValue JSONWriter::getData(const QScriptValue& value) { } else if (value.isArray()) { object.insert("type", QString("ARRAY")); QJsonArray array; - int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + int length = value.property(DependencyManager::get()->getLengthString()).toInt32(); for (int i = 0; i < length; i++) { array.append(getData(value.property(i))); } @@ -2209,31 +2209,31 @@ void JSONReader::putData(const QJsonValue& data, QScriptValue& value) { } else if (type == "VARIANT") { QVariant variant; putData(object.value("value"), variant); - value = ScriptCache::getInstance()->getEngine()->newVariant(variant); + value = DependencyManager::get()->getEngine()->newVariant(variant); } else if (type == "QOBJECT") { QObject* qObject; putData(object.value("value"), qObject); - value = ScriptCache::getInstance()->getEngine()->newQObject(qObject, QScriptEngine::ScriptOwnership); + value = DependencyManager::get()->getEngine()->newQObject(qObject, QScriptEngine::ScriptOwnership); } else if (type == "QMETAOBJECT") { const QMetaObject* metaObject; putData(object.value("value"), metaObject); - value = ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject); + value = DependencyManager::get()->getEngine()->newQMetaObject(metaObject); } else if (type == "DATE") { QDateTime dateTime; putData(object.value("value"), dateTime); - value = ScriptCache::getInstance()->getEngine()->newDate(dateTime); + value = DependencyManager::get()->getEngine()->newDate(dateTime); } else if (type == "REGEXP") { QRegExp regExp; putData(object.value("value"), regExp); - value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp); + value = DependencyManager::get()->getEngine()->newRegExp(regExp); } else if (type == "ARRAY") { QJsonArray array = object.value("value").toArray(); - value = ScriptCache::getInstance()->getEngine()->newArray(array.size()); + value = DependencyManager::get()->getEngine()->newArray(array.size()); for (int i = 0; i < array.size(); i++) { QScriptValue element; putData(array.at(i), element); @@ -2241,7 +2241,7 @@ void JSONReader::putData(const QJsonValue& data, QScriptValue& value) { } } else if (type == "OBJECT") { QJsonObject jsonObject = object.value("value").toObject(); - value = ScriptCache::getInstance()->getEngine()->newObject(); + value = DependencyManager::get()->getEngine()->newObject(); for (QJsonObject::const_iterator it = jsonObject.constBegin(); it != jsonObject.constEnd(); it++) { QScriptValue element; putData(it.value(), element); diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 73900fdcbc..4e86e1c636 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -663,7 +663,7 @@ void ParameterizedURLEditor::updateURL() { QByteArray valuePropertyName = widget->property("valuePropertyName").toByteArray(); const QMetaObject* widgetMetaObject = widget->metaObject(); QMetaProperty widgetProperty = widgetMetaObject->property(widgetMetaObject->indexOfProperty(valuePropertyName)); - parameters.insert(ScriptCache::getInstance()->getEngine()->toStringHandle( + parameters.insert(DependencyManager::get()->getEngine()->toStringHandle( widget->property("parameterName").toString()), widgetProperty.read(widget)); } } @@ -677,7 +677,7 @@ void ParameterizedURLEditor::updateParameters() { if (_program) { _program->disconnect(this); } - _program = ScriptCache::getInstance()->getProgram(_url.getURL()); + _program = DependencyManager::get()->getProgram(_url.getURL()); if (_program->isLoaded()) { continueUpdatingParameters(); } else { @@ -698,7 +698,7 @@ void ParameterizedURLEditor::continueUpdatingParameters() { } delete form; } - QSharedPointer value = ScriptCache::getInstance()->getValue(_url.getURL()); + QSharedPointer value = DependencyManager::get()->getValue(_url.getURL()); const QList& parameters = static_cast(value.data())->getParameterInfo(); if (parameters.isEmpty()) { return; diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index 7e8dbc4bae..b7e81b5381 100644 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -57,8 +57,8 @@ bool operator==(const QScriptValue& first, const QScriptValue& second) { if (!second.isArray()) { return false; } - int length = first.property(ScriptCache::getInstance()->getLengthString()).toInt32(); - if (second.property(ScriptCache::getInstance()->getLengthString()).toInt32() != length) { + int length = first.property(DependencyManager::get()->getLengthString()).toInt32(); + if (second.property(DependencyManager::get()->getLengthString()).toInt32() != length) { return false; } for (int i = 0; i < length; i++) { @@ -103,11 +103,6 @@ bool operator<(const QScriptValue& first, const QScriptValue& second) { return first.lessThan(second); } -ScriptCache* ScriptCache::getInstance() { - static ScriptCache cache; - return &cache; -} - ScriptCache::ScriptCache() : _engine(NULL) { diff --git a/libraries/metavoxels/src/ScriptCache.h b/libraries/metavoxels/src/ScriptCache.h index 5d29157b3d..b9294a7504 100644 --- a/libraries/metavoxels/src/ScriptCache.h +++ b/libraries/metavoxels/src/ScriptCache.h @@ -25,13 +25,11 @@ class NetworkProgram; class NetworkValue; /// Maintains a cache of loaded scripts. -class ScriptCache : public ResourceCache { +class ScriptCache : public ResourceCache, public Dependency { Q_OBJECT + SINGLETON_DEPENDENCY public: - - static ScriptCache* getInstance(); - ScriptCache(); void setEngine(QScriptEngine* engine); diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index 0beaa20a83..fbd485e6a0 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -23,6 +23,7 @@ typedef gpu::BufferView::Index Index; typedef gpu::BufferView BufferView; typedef AABox Box; typedef std::vector< Box > Boxes; +typedef glm::vec3 Vec3; class Mesh { public: @@ -35,7 +36,7 @@ public: typedef gpu::Stream::Format VertexFormat; typedef std::map< Slot, BufferView > BufferViewMap; - typedef glm::vec3 Vec3; + typedef model::Vec3 Vec3; Mesh(); Mesh(const Mesh& mesh); diff --git a/libraries/model/src/model/Light.cpp b/libraries/model/src/model/Light.cpp new file mode 100755 index 0000000000..9616ed2106 --- /dev/null +++ b/libraries/model/src/model/Light.cpp @@ -0,0 +1,97 @@ +// +// Light.cpp +// libraries/model/src/model +// +// Created by Sam Gateau on 1/26/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 +// +#include "Light.h" + +using namespace model; + +Light::Light() : + _flags(0), + _schemaBuffer(), + _transform() { + // only if created from nothing shall we create the Buffer to store the properties + Schema schema; + _schemaBuffer = gpu::BufferView(new gpu::Buffer(sizeof(Schema), (const gpu::Buffer::Byte*) &schema)); +} + +Light::Light(const Light& light) : + _flags(light._flags), + _schemaBuffer(light._schemaBuffer), + _transform(light._transform) { +} + +Light& Light::operator= (const Light& light) { + _flags = (light._flags); + _schemaBuffer = (light._schemaBuffer); + _transform = (light._transform); + + return (*this); +} + +Light::~Light() { +} + +void Light::setPosition(const Vec3& position) { + _transform.setTranslation(position); + editSchema()._position = Vec4(position, 1.f); +} + +void Light::setOrientation(const glm::quat& orientation) { + _transform.setRotation(orientation); +} + +void Light::setDirection(const Vec3& direction) { + editSchema()._direction = glm::normalize(direction); +} + +const Vec3& Light::getDirection() const { + return getSchema()._direction; +} + +void Light::setColor(const Color& color) { + editSchema()._color = color; +} + +void Light::setIntensity(float intensity) { + editSchema()._intensity = intensity; +} + +void Light::setMaximumRadius(float radius) { + if (radius <= 0.f) { + radius = 1.0f; + } + float CutOffIntensityRatio = 0.05f; + float surfaceRadius = radius / (sqrt(1.0f / CutOffIntensityRatio) - 1.f); + editSchema()._attenuation = Vec4(surfaceRadius, 1.0f/surfaceRadius, CutOffIntensityRatio, radius); +} + +void Light::setSpotAngle(float angle) { + if (angle <= 0.f) { + angle = 0.0f; + } + float cosAngle = cos(angle); + editSchema()._spot.x = cos(angle); + editSchema()._spot.y = sin(angle); + editSchema()._spot.z = angle; +} + +void Light::setSpotExponent(float exponent) { + if (exponent <= 0.f) { + exponent = 1.0f; + } + editSchema()._spot.w = exponent; +} + +void Light::setShowContour(float show) { + if (show <= 0.f) { + show = 0.0f; + } + editSchema()._control.w = show; +} diff --git a/libraries/model/src/model/Light.h b/libraries/model/src/model/Light.h new file mode 100755 index 0000000000..2ef2bf3036 --- /dev/null +++ b/libraries/model/src/model/Light.h @@ -0,0 +1,294 @@ +// +// Light.h +// libraries/model/src/model +// +// Created by Sam Gateau on 12/10/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 +// +#ifndef hifi_model_Light_h +#define hifi_model_Light_h + +#include +#include + +#include +#include "Transform.h" +#include "gpu/Resource.h" +#include "gpu/Texture.h" + +namespace model { +typedef gpu::BufferView UniformBufferView; +typedef gpu::TextureView TextureView; +typedef glm::vec3 Vec3; +typedef glm::vec4 Vec4; +typedef glm::quat Quat; + +class SphericalHarmonics { +public: + glm::vec3 L00 ; float spare0; + glm::vec3 L1m1 ; float spare1; + glm::vec3 L10 ; float spare2; + glm::vec3 L11 ; float spare3; + glm::vec3 L2m2 ; float spare4; + glm::vec3 L2m1 ; float spare5; + glm::vec3 L20 ; float spare6; + glm::vec3 L21 ; float spare7; + glm::vec3 L22 ; float spare8; + + static const int NUM_COEFFICIENTS = 9; + + enum Preset { + OLD_TOWN_SQUARE = 0, + GRACE_CATHEDRAL, + EUCALYPTUS_GROVE, + ST_PETERS_BASILICA, + UFFIZI_GALLERY, + GALILEOS_TOMB, + VINE_STREET_KITCHEN, + BREEZEWAY, + CAMPUS_SUNSET, + FUNSTON_BEACH_SUNSET, + + NUM_PRESET, + }; + + void assignPreset(int p) { + switch (p) { + case OLD_TOWN_SQUARE: { + L00 = glm::vec3( 0.871297f, 0.875222f, 0.864470f); + L1m1 = glm::vec3( 0.175058f, 0.245335f, 0.312891f); + L10 = glm::vec3( 0.034675f, 0.036107f, 0.037362f); + L11 = glm::vec3(-0.004629f,-0.029448f,-0.048028f); + L2m2 = glm::vec3(-0.120535f,-0.121160f,-0.117507f); + L2m1 = glm::vec3( 0.003242f, 0.003624f, 0.007511f); + L20 = glm::vec3(-0.028667f,-0.024926f,-0.020998f); + L21 = glm::vec3(-0.077539f,-0.086325f,-0.091591f); + L22 = glm::vec3(-0.161784f,-0.191783f,-0.219152f); + } + break; + case GRACE_CATHEDRAL: { + L00 = glm::vec3( 0.79f, 0.44f, 0.54f); + L1m1 = glm::vec3( 0.39f, 0.35f, 0.60f); + L10 = glm::vec3(-0.34f, -0.18f, -0.27f); + L11 = glm::vec3(-0.29f, -0.06f, 0.01f); + L2m2 = glm::vec3(-0.11f, -0.05f, -0.12f); + L2m1 = glm::vec3(-0.26f, -0.22f, -0.47f); + L20 = glm::vec3(-0.16f, -0.09f, -0.15f); + L21 = glm::vec3( 0.56f, 0.21f, 0.14f); + L22 = glm::vec3( 0.21f, -0.05f, -0.30f); + } + break; + case EUCALYPTUS_GROVE: { + L00 = glm::vec3( 0.38f, 0.43f, 0.45f); + L1m1 = glm::vec3( 0.29f, 0.36f, 0.41f); + L10 = glm::vec3( 0.04f, 0.03f, 0.01f); + L11 = glm::vec3(-0.10f, -0.10f, -0.09f); + L2m2 = glm::vec3(-0.06f, -0.06f, -0.04f); + L2m1 = glm::vec3( 0.01f, -0.01f, -0.05f); + L20 = glm::vec3(-0.09f, -0.13f, -0.15f); + L21 = glm::vec3(-0.06f, -0.05f, -0.04f); + L22 = glm::vec3( 0.02f, 0.00f, -0.05f); + } + break; + case ST_PETERS_BASILICA: { + L00 = glm::vec3( 0.36f, 0.26f, 0.23f); + L1m1 = glm::vec3( 0.18f, 0.14f, 0.13f); + L10 = glm::vec3(-0.02f, -0.01f, 0.00f); + L11 = glm::vec3( 0.03f, 0.02f, -0.00f); + L2m2 = glm::vec3( 0.02f, 0.01f, -0.00f); + L2m1 = glm::vec3(-0.05f, -0.03f, -0.01f); + L20 = glm::vec3(-0.09f, -0.08f, -0.07f); + L21 = glm::vec3( 0.01f, 0.00f, 0.00f); + L22 = glm::vec3(-0.08f, -0.03f, -0.00f); + } + break; + case UFFIZI_GALLERY: { + L00 = glm::vec3( 0.32f, 0.31f, 0.35f); + L1m1 = glm::vec3( 0.37f, 0.37f, 0.43f); + L10 = glm::vec3( 0.00f, 0.00f, 0.00f); + L11 = glm::vec3(-0.01f, -0.01f, -0.01f); + L2m2 = glm::vec3(-0.02f, -0.02f, -0.03f); + L2m1 = glm::vec3(-0.01f, -0.01f, -0.01f); + L20 = glm::vec3(-0.28f, -0.28f, -0.32f); + L21 = glm::vec3( 0.00f, 0.00f, 0.00f); + L22 = glm::vec3(-0.24f, -0.24f, -0.28f); + } + break; + case GALILEOS_TOMB: { + L00 = glm::vec3( 1.04f, 0.76f, 0.71f); + L1m1 = glm::vec3( 0.44f, 0.34f, 0.34f); + L10 = glm::vec3(-0.22f, -0.18f, -0.17f); + L11 = glm::vec3( 0.71f, 0.54f, 0.56f); + L2m2 = glm::vec3( 0.64f, 0.50f, 0.52f); + L2m1 = glm::vec3(-0.12f, -0.09f, -0.08f); + L20 = glm::vec3(-0.37f, -0.28f, -0.32f); + L21 = glm::vec3(-0.17f, -0.13f, -0.13f); + L22 = glm::vec3( 0.55f, 0.42f, 0.42f); + } + break; + case VINE_STREET_KITCHEN: { + L00 = glm::vec3( 0.64f, 0.67f, 0.73f); + L1m1 = glm::vec3( 0.28f, 0.32f, 0.33f); + L10 = glm::vec3( 0.42f, 0.60f, 0.77f); + L11 = glm::vec3(-0.05f, -0.04f, -0.02f); + L2m2 = glm::vec3(-0.10f, -0.08f, -0.05f); + L2m1 = glm::vec3( 0.25f, 0.39f, 0.53f); + L20 = glm::vec3( 0.38f, 0.54f, 0.71f); + L21 = glm::vec3( 0.06f, 0.01f, -0.02f); + L22 = glm::vec3(-0.03f, -0.02f, -0.03f); + } + break; + case BREEZEWAY: { + L00 = glm::vec3( 0.32f, 0.36f, 0.38f); + L1m1 = glm::vec3( 0.37f, 0.41f, 0.45f); + L10 = glm::vec3(-0.01f, -0.01f, -0.01f); + L11 = glm::vec3(-0.10f, -0.12f, -0.12f); + L2m2 = glm::vec3(-0.13f, -0.15f, -0.17f); + L2m1 = glm::vec3(-0.01f, -0.02f, 0.02f); + L20 = glm::vec3(-0.07f, -0.08f, -0.09f); + L21 = glm::vec3( 0.02f, 0.03f, 0.03f); + L22 = glm::vec3(-0.29f, -0.32f, -0.36f); + } + break; + case CAMPUS_SUNSET: { + L00 = glm::vec3( 0.79f, 0.94f, 0.98f); + L1m1 = glm::vec3( 0.44f, 0.56f, 0.70f); + L10 = glm::vec3(-0.10f, -0.18f, -0.27f); + L11 = glm::vec3( 0.45f, 0.38f, 0.20f); + L2m2 = glm::vec3( 0.18f, 0.14f, 0.05f); + L2m1 = glm::vec3(-0.14f, -0.22f, -0.31f); + L20 = glm::vec3(-0.39f, -0.40f, -0.36f); + L21 = glm::vec3( 0.09f, 0.07f, 0.04f); + L22 = glm::vec3( 0.67f, 0.67f, 0.52f); + } + break; + case FUNSTON_BEACH_SUNSET: { + L00 = glm::vec3( 0.68f, 0.69f, 0.70f); + L1m1 = glm::vec3( 0.32f, 0.37f, 0.44f); + L10 = glm::vec3(-0.17f, -0.17f, -0.17f); + L11 = glm::vec3(-0.45f, -0.42f, -0.34f); + L2m2 = glm::vec3(-0.17f, -0.17f, -0.15f); + L2m1 = glm::vec3(-0.08f, -0.09f, -0.10f); + L20 = glm::vec3(-0.03f, -0.02f, -0.01f); + L21 = glm::vec3( 0.16f, 0.14f, 0.10f); + L22 = glm::vec3( 0.37f, 0.31f, 0.20f); + } + break; + } + } +}; + +class Light { +public: + enum Type { + SUN = 0, + POINT, + SPOT, + + NUM_TYPES, + }; + + typedef Vec3 Color; + + enum FlagBit { + COLOR_BIT = 0, + INTENSITY_BIT, + RANGE_BIT, + SPOT_BIT, + TRANSFORM_BIT, + + NUM_FLAGS, + }; + typedef std::bitset Flags; + + Light(); + Light(const Light& light); + Light& operator= (const Light& light); + virtual ~Light(); + + void setType(Type type) { editSchema()._control.x = float(type); } + Type getType() const { return Type((int) getSchema()._control.x); } + + void setPosition(const Vec3& position); + const Vec3& getPosition() const { return _transform.getTranslation(); } + + void setDirection(const Vec3& direction); + const Vec3& getDirection() const; + + void setOrientation(const Quat& orientation); + const glm::quat& getOrientation() const { return _transform.getRotation(); } + + const Color& getColor() const { return getSchema()._color; } + void setColor(const Color& color); + + float getIntensity() const { return getSchema()._intensity; } + void setIntensity(float intensity); + + bool isRanged() const { return (getType() == POINT) || (getType() == SPOT ); } + + void setMaximumRadius(float radius); + float getMaximumRadius() const { return getSchema()._attenuation.w; } + + // Spot properties + bool isSpot() const { return getType() == SPOT; } + void setSpotAngle(float angle); + float getSpotAngle() const { return getSchema()._spot.z; } + void setSpotExponent(float exponent); + float getSpotExponent() const { return getSchema()._spot.w; } + + // For editing purpose, show the light volume contour. + // Set to non 0 to show it, the value is used as the intensity of the contour color + void setShowContour(float show); + float getShowContour() const { return getSchema()._control.w; } + + // Spherical Harmonics storing the Ambien lighting approximation used for the Sun typed light + void setAmbientSphere(const SphericalHarmonics& sphere) { _ambientSphere = sphere; } + const SphericalHarmonics& getAmbientSphere() const { return _ambientSphere; } + void setAmbientSpherePreset(SphericalHarmonics::Preset preset) { _ambientSphere.assignPreset(preset); } + + // Schema to access the attribute values of the light + class Schema { + public: + Vec4 _position; + Vec3 _direction; + float _spare0; + Color _color; + float _intensity; + Vec4 _attenuation; + Vec4 _spot; + Vec4 _shadow; + + Vec4 _control; + + Schema() : + _position(0.0f, 0.0f, 0.0f, 1.0f), + _direction(0.0f, 0.0f, -1.0f), + _spare0(0.f), + _color(1.0f), + _intensity(1.0f), + _attenuation(1.0f, 1.0f, 1.0f, 1.0f), + _spot(0.0f, 0.0f, 0.0f, 3.0f), + _control(0.0f) + {} + }; + + const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; } + +protected: + + Flags _flags; + UniformBufferView _schemaBuffer; + Transform _transform; + SphericalHarmonics _ambientSphere; + + const Schema& getSchema() const { return _schemaBuffer.get(); } + Schema& editSchema() { return _schemaBuffer.edit(); } +}; +typedef QSharedPointer< Light > LightPointer; + +}; + +#endif diff --git a/libraries/model/src/model/Material.h b/libraries/model/src/model/Material.h index efd2f35968..57e0f68a9c 100755 --- a/libraries/model/src/model/Material.h +++ b/libraries/model/src/model/Material.h @@ -100,7 +100,6 @@ public: void setTextureView(MapChannel channel, const TextureView& texture); const TextureMap& getTextureMap() const { return _textureMap; } - const Schema* getSchema() const { return &_schemaBuffer.get(); } protected: Flags _flags; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 13bb2b1ad8..43f4dda565 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -39,6 +39,7 @@ const char SOLO_NODE_TYPES[2] = { const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://metaverse.highfidelity.io"); LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort) : + linkedDataCreateCallback(NULL), _sessionUUID(), _nodeHash(), _nodeMutex(QReadWriteLock::Recursive), diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 4fc6905e3a..f2646369c1 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -86,17 +86,9 @@ void UserActivityLogger::launch(QString applicationVersion) { logAction(ACTION_NAME, actionDetails); } -void UserActivityLogger::close(int delayTime) { +void UserActivityLogger::close() { const QString ACTION_NAME = "close"; - - // In order to get the end of the session, we need to give the account manager enough time to send the packet. - QEventLoop loop; - QTimer timer; - connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); - // Now we can log it logAction(ACTION_NAME, QJsonObject()); - timer.start(delayTime); - loop.exec(); } void UserActivityLogger::changedDisplayName(QString displayName) { diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h index 9b100461ee..295ad5ee8d 100644 --- a/libraries/networking/src/UserActivityLogger.h +++ b/libraries/networking/src/UserActivityLogger.h @@ -30,7 +30,7 @@ public slots: void logAction(QString action, QJsonObject details = QJsonObject(), JSONCallbackParameters params = JSONCallbackParameters()); void launch(QString applicationVersion); - void close(int delayTime); + void close(); void changedDisplayName(QString displayName); void changedModel(QString typeOfModel, QString modelURL); void changedDomain(QString domainURL); diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index f0612cc9b1..2a563dc50a 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -49,14 +49,32 @@ void OctreePersistThread::parseSettings(const QJsonObject& settings) { qDebug() << "BACKUP RULES:"; foreach (const QJsonValue& value, backupRules) { + QJsonObject obj = value.toObject(); + + int interval = 0; + int count = 0; + + QJsonValue intervalVal = obj["backupInterval"]; + if (intervalVal.isString()) { + interval = intervalVal.toString().toInt(); + } else { + interval = intervalVal.toInt(); + } + + QJsonValue countVal = obj["maxBackupVersions"]; + if (countVal.isString()) { + count = countVal.toString().toInt(); + } else { + count = countVal.toInt(); + } + qDebug() << " Name:" << obj["Name"].toString(); qDebug() << " format:" << obj["format"].toString(); - qDebug() << " interval:" << obj["backupInterval"].toInt(); - qDebug() << " count:" << obj["maxBackupVersions"].toInt(); + qDebug() << " interval:" << interval; + qDebug() << " count:" << count; - BackupRule newRule = { obj["Name"].toString(), obj["backupInterval"].toInt(), - obj["format"].toString(), obj["maxBackupVersions"].toInt(), 0}; + BackupRule newRule = { obj["Name"].toString(), interval, obj["format"].toString(), count, 0}; newRule.lastBackup = getMostRecentBackupTimeInUsecs(obj["format"].toString()); @@ -213,7 +231,9 @@ void OctreePersistThread::persist() { } _tree->unlock(); + qDebug() << "persist operation calling backup..."; backup(); // handle backup if requested + qDebug() << "persist operation DONE with backup..."; // create our "lock" file to indicate we're saving. @@ -319,34 +339,41 @@ bool OctreePersistThread::getMostRecentBackup(const QString& format, void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) { if (rule.extensionFormat.contains("%N")) { - qDebug() << "Rolling old backup versions for rule" << rule.name << "..."; - for(int n = rule.maxBackupVersions - 1; n > 0; n--) { - QString backupExtensionN = rule.extensionFormat; - QString backupExtensionNplusOne = rule.extensionFormat; - backupExtensionN.replace(QString("%N"), QString::number(n)); - backupExtensionNplusOne.replace(QString("%N"), QString::number(n+1)); + if (rule.maxBackupVersions > 0) { + qDebug() << "Rolling old backup versions for rule" << rule.name << "..."; + for(int n = rule.maxBackupVersions - 1; n > 0; n--) { + QString backupExtensionN = rule.extensionFormat; + QString backupExtensionNplusOne = rule.extensionFormat; + backupExtensionN.replace(QString("%N"), QString::number(n)); + backupExtensionNplusOne.replace(QString("%N"), QString::number(n+1)); - QString backupFilenameN = _filename + backupExtensionN; - QString backupFilenameNplusOne = _filename + backupExtensionNplusOne; + QString backupFilenameN = _filename + backupExtensionN; + QString backupFilenameNplusOne = _filename + backupExtensionNplusOne; - QFile backupFileN(backupFilenameN); + QFile backupFileN(backupFilenameN); - if (backupFileN.exists()) { - qDebug() << "rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "..."; - int result = rename(qPrintable(backupFilenameN), qPrintable(backupFilenameNplusOne)); - if (result == 0) { - qDebug() << "DONE rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "..."; - } else { - qDebug() << "ERROR in rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "..."; + if (backupFileN.exists()) { + qDebug() << "rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "..."; + int result = rename(qPrintable(backupFilenameN), qPrintable(backupFilenameNplusOne)); + if (result == 0) { + qDebug() << "DONE rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "..."; + } else { + qDebug() << "ERROR in rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "..."; + } } } + qDebug() << "Done rolling old backup versions..."; + } else { + qDebug() << "Rolling backups for rule" << rule.name << "." + << " Max Rolled Backup Versions less than 1 [" << rule.maxBackupVersions << "]." + << " No need to roll backups..."; } - qDebug() << "Done rolling old backup versions..."; } } void OctreePersistThread::backup() { + qDebug() << "backup operation wantBackup:" << _wantBackup; if (_wantBackup) { quint64 now = usecTimestampNow(); @@ -354,10 +381,12 @@ void OctreePersistThread::backup() { BackupRule& rule = _backupRules[i]; quint64 sinceLastBackup = now - rule.lastBackup; - quint64 SECS_TO_USECS = 1000 * 1000; quint64 intervalToBackup = rule.interval * SECS_TO_USECS; - + + qDebug() << "Checking [" << rule.name << "] - Time since last backup [" << sinceLastBackup << "] " << + "compared to backup interval [" << intervalToBackup << "]..."; + if (sinceLastBackup > intervalToBackup) { qDebug() << "Time since last backup [" << sinceLastBackup << "] for rule [" << rule.name << "] exceeds backup interval [" << intervalToBackup << "] doing backup now..."; @@ -379,15 +408,28 @@ void OctreePersistThread::backup() { } - qDebug() << "backing up persist file " << _filename << "to" << backupFileName << "..."; - bool result = QFile::copy(_filename, backupFileName); - if (result) { - qDebug() << "DONE backing up persist file..."; + if (rule.maxBackupVersions > 0) { + QFile persistFile(_filename); + if (persistFile.exists()) { + qDebug() << "backing up persist file " << _filename << "to" << backupFileName << "..."; + bool result = QFile::copy(_filename, backupFileName); + if (result) { + qDebug() << "DONE backing up persist file..."; + rule.lastBackup = now; // only record successful backup in this case. + } else { + qDebug() << "ERROR in backing up persist file..."; + } + } else { + qDebug() << "persist file " << _filename << " does not exist. " << + "nothing to backup for this rule ["<< rule.name << "]..."; + } } else { - qDebug() << "ERROR in backing up persist file..."; + qDebug() << "This backup rule" << rule.name + << " has Max Rolled Backup Versions less than 1 [" << rule.maxBackupVersions << "]." + << " There are no backups to be done..."; } - - rule.lastBackup = now; + } else { + qDebug() << "Backup not needed for this rule ["<< rule.name << "]..."; } } } diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh new file mode 100755 index 0000000000..6868b96c24 --- /dev/null +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -0,0 +1,117 @@ + +<@if not DEFERRED_GLOBAL_LIGHT_SLH@> +<@def DEFERRED_GLOBAL_LIGHT_SLH@> + +<@include DeferredLighting.slh@> + +struct SphericalHarmonics { + vec4 L00; + vec4 L1m1; + vec4 L10; + vec4 L11; + vec4 L2m2; + vec4 L2m1; + vec4 L20; + vec4 L21; + vec4 L22; +}; + +vec4 evalSphericalLight(SphericalHarmonics sh, vec3 direction ) { + + const float C1 = 0.429043; + const float C2 = 0.511664; + const float C3 = 0.743125; + const float C4 = 0.886227; + const float C5 = 0.247708; + + vec4 value = C1 * sh.L22 * (direction.x * direction.x - direction.y * direction.y) + + C3 * sh.L20 * direction.z * direction.z + + C4 * sh.L00 - C5 * sh.L20 + + 2.0 * C1 * ( sh.L2m2 * direction.x * direction.y + + sh.L21 * direction.x * direction.z + + sh.L2m1 * direction.y * direction.z ) + + 2.0 * C2 * ( sh.L11 * direction.x + + sh.L1m1 * direction.y + + sh.L10 * direction.z ) ; + return value; +} + +// Need one SH +uniform SphericalHarmonics ambientSphere; + +// Everything about light +<@include Light.slh@> + +// The view Matrix +uniform mat4 invViewMat; + +vec3 evalAmbienGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 diffuse, vec3 specular, float gloss) { + + // Need the light now + Light light = getLight(); + + vec3 fragNormal = vec3(invViewMat * vec4(normal, 0.0)); + vec4 fragEyeVector = invViewMat * vec4(-position, 0.0); + vec3 fragEyeDir = normalize(fragEyeVector.xyz); + + vec3 color = diffuse.rgb * getLightColor(light) * 0.5; + + vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss); + + color += vec3(diffuse + shading.rgb) * shading.w * shadowAttenuation * getLightColor(light) * getLightIntensity(light); + + return color; +} +vec3 evalAmbienSphereGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 diffuse, vec3 specular, float gloss) { + // Need the light now + Light light = getLight(); + + vec3 fragNormal = vec3(invViewMat * vec4(normal, 0.0)); + vec4 fragEyeVector = invViewMat * vec4(-position, 0.0); + vec3 fragEyeDir = normalize(fragEyeVector.xyz); + + // TODO: The world space normal doesn;t seem to work properly with the current SH definitions + // FoOr now, we use the normal in view space + vec3 ambientNormal = normal; + + vec3 color = diffuse.rgb * 0.5 * evalSphericalLight(ambientSphere, ambientNormal).xyz; + + vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss); + + color += vec3(diffuse + shading.rgb) * shading.w * shadowAttenuation * getLightColor(light) * getLightIntensity(light); + + return color; +} + +vec3 evalLightmappedColor(float shadowAttenuation, vec3 normal, vec3 diffuse, vec3 lightmap) { + + Light light = getLight(); + float diffuseDot = dot(normal, getLightDirection(light)); + + // need to catch normals perpendicular to the projection plane hence the magic number for the threshold + // it should be just 0, but we have innacurracy so we need to overshoot + const float PERPENDICULAR_THRESHOLD = -0.005; + float facingLight = step(PERPENDICULAR_THRESHOLD, diffuseDot); + + // evaluate the shadow test but only relevant for light facing fragments + float lightAttenuation = (1 - facingLight) + facingLight * shadowAttenuation; + + // diffuse light is the lightmap dimmed by shadow + vec3 diffuseLight = lightAttenuation * lightmap; + + // ambient is a tiny percentage of the lightmap and only when in the shadow + vec3 ambientLight = (1 - lightAttenuation) * 0.5 * lightmap; + + return diffuse * (ambientLight + diffuseLight); +} + +<@endif@> diff --git a/libraries/render-utils/src/DeferredLighting.slh b/libraries/render-utils/src/DeferredLighting.slh index cd65fd1053..bb37a9e3e8 100755 --- a/libraries/render-utils/src/DeferredLighting.slh +++ b/libraries/render-utils/src/DeferredLighting.slh @@ -11,86 +11,51 @@ <@if not DEFERRED_LIGHTING_SLH@> <@def DEFERRED_LIGHTING_SLH@> -struct SphericalHarmonics { - vec4 L00; - vec4 L1m1; - vec4 L10; - vec4 L11; - vec4 L2m2; - vec4 L2m1; - vec4 L20; - vec4 L21; - vec4 L22; -}; - -vec4 evalSphericalLight(SphericalHarmonics sh, vec3 direction ) { - - const float C1 = 0.429043; - const float C2 = 0.511664; - const float C3 = 0.743125; - const float C4 = 0.886227; - const float C5 = 0.247708; - - vec4 value = C1 * sh.L22 * (direction.x * direction.x - direction.y * direction.y) + - C3 * sh.L20 * direction.z * direction.z + - C4 * sh.L00 - C5 * sh.L20 + - 2.0 * C1 * ( sh.L2m2 * direction.x * direction.y + - sh.L21 * direction.x * direction.z + - sh.L2m1 * direction.y * direction.z ) + - 2.0 * C2 * ( sh.L11 * direction.x + - sh.L1m1 * direction.y + - sh.L10 * direction.z ) ; - return value; -} - -uniform SphericalHarmonics ambientSphere; - -vec3 evalAmbientColor(vec3 normal, vec3 diffuse, vec3 specular, float gloss) { - return diffuse.rgb * gl_FrontLightProduct[0].ambient.rgb; -} - -vec3 evalAmbientSphereColor(vec3 normal, vec3 diffuse, vec3 specular, float gloss) { - vec3 ambientLight = 0.5 * evalSphericalLight(ambientSphere, normal).xyz; - - return diffuse.rgb * ambientLight; -} - -vec3 evalDirectionalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 diffuse, vec3 specular, float gloss) { +// Frag Shading returns the diffuse amount as W and the specular rgb as xyz +vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, vec3 specular, float gloss) { // Diffuse Lighting - float diffuseDot = dot(normal, gl_LightSource[0].position.xyz); - float facingLight = step(0.0, diffuseDot) * shadowAttenuation; - vec3 diffuseColor = diffuse * (gl_FrontLightProduct[0].diffuse.rgb * (diffuseDot * facingLight)); - - // compute the specular multiplier (sans exponent) - float specularPower = facingLight * max(0.0, - dot(normalize(gl_LightSource[0].position.xyz - normalize(position)), normal)); - vec3 specularColor = pow(specularPower, gloss * 128.0) * specular; + float diffuseDot = dot(fragNormal, fragLightDir); + float facingLight = step(0.0, diffuseDot); + float diffuse = diffuseDot * facingLight; + + // Specular Lighting depends on the half vector and the gloss + vec3 halfDir = normalize(fragEyeDir + fragLightDir); - // add specular contribution - return vec3(diffuseColor + specularColor); + // float specularPower = pow(facingLight * max(0.0, dot(halfDir, fragNormal)), gloss * 128.0); + float specularPower = pow(max(0.0, dot(halfDir, fragNormal)), gloss * 128.0); + specularPower *= (gloss * 128.0 * 0.125 + 0.25); + + float shlickPower = (1.0 - dot(fragLightDir,halfDir)); + float shlickPower2 = shlickPower * shlickPower; + float shlickPower5 = shlickPower2 * shlickPower2 * shlickPower; + vec3 schlick = specular * (1.0 - shlickPower5) + vec3(shlickPower5); + vec3 reflect = specularPower * schlick; + + return vec4(reflect, diffuse); } +vec4 evalBlinnShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, vec3 specular, float gloss) { + // Diffuse Lighting + float diffuseDot = dot(fragNormal, fragLightDir); + float facingLight = step(0.0, diffuseDot); + float diffuse = diffuseDot * facingLight; + + // Specular Lighting depends on the half vector and the gloss + vec3 halfDir = normalize(fragEyeDir + fragLightDir); -vec3 evalLightmappedColor(float shadowAttenuation, vec3 normal, vec3 diffuse, vec3 lightmap) { - - float diffuseDot = dot(normal, gl_LightSource[0].position.xyz); - - // need to catch normals perpendicular to the projection plane hence the magic number for the threshold - // it should be just 0, but we have innacurracy so we need to overshoot - const float PERPENDICULAR_THRESHOLD = -0.005; - float facingLight = step(PERPENDICULAR_THRESHOLD, diffuseDot); - - // evaluate the shadow test but only relevant for light facing fragments - float lightAttenuation = (1 - facingLight) + facingLight * shadowAttenuation; - - // diffuse light is the lightmap dimmed by shadow - vec3 diffuseLight = lightAttenuation * lightmap; + float specularPower = pow(facingLight * max(0.0, dot(halfDir, fragNormal)), gloss * 128.0); + vec3 reflect = specularPower * specular; - // ambient is a tiny percentage of the lightmap and only when in the shadow - vec3 ambientLight = (1 - lightAttenuation) * 0.5 * lightmap; - - return diffuse * (ambientLight + diffuseLight); + return vec4(reflect, diffuse); } +vec4 evalFragShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, vec3 specular, float gloss) { + + /*if (gl_FragCoord.x > 1000) { + return evalBlinnShading(fragNormal, fragLightDir, fragEyeDir, specular, gloss); + } else {*/ + return evalPBRShading(fragNormal, fragLightDir, fragEyeDir, specular, gloss); + //} +} <@endif@> diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index b84b01b16a..1f5d0ce4c3 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -25,6 +25,9 @@ #include "RenderUtil.h" #include "TextureCache.h" +#include "gpu/Batch.h" +#include "gpu/GLBackend.h" + #include "simple_vert.h" #include "simple_frag.h" @@ -42,146 +45,6 @@ #include "point_light_frag.h" #include "spot_light_frag.h" -class SphericalHarmonics { -public: - glm::vec3 L00 ; float spare0; - glm::vec3 L1m1 ; float spare1; - glm::vec3 L10 ; float spare2; - glm::vec3 L11 ; float spare3; - glm::vec3 L2m2 ; float spare4; - glm::vec3 L2m1 ; float spare5; - glm::vec3 L20 ; float spare6; - glm::vec3 L21 ; float spare7; - glm::vec3 L22 ; float spare8; - - static const int NUM_COEFFICIENTS = 9; - - void assignPreset(int p) { - switch (p) { - case DeferredLightingEffect::OLD_TOWN_SQUARE: { - L00 = glm::vec3( 0.871297f, 0.875222f, 0.864470f); - L1m1 = glm::vec3( 0.175058f, 0.245335f, 0.312891f); - L10 = glm::vec3( 0.034675f, 0.036107f, 0.037362f); - L11 = glm::vec3(-0.004629f,-0.029448f,-0.048028f); - L2m2 = glm::vec3(-0.120535f,-0.121160f,-0.117507f); - L2m1 = glm::vec3( 0.003242f, 0.003624f, 0.007511f); - L20 = glm::vec3(-0.028667f,-0.024926f,-0.020998f); - L21 = glm::vec3(-0.077539f,-0.086325f,-0.091591f); - L22 = glm::vec3(-0.161784f,-0.191783f,-0.219152f); - } - break; - case DeferredLightingEffect::GRACE_CATHEDRAL: { - L00 = glm::vec3( 0.79f, 0.44f, 0.54f); - L1m1 = glm::vec3( 0.39f, 0.35f, 0.60f); - L10 = glm::vec3(-0.34f, -0.18f, -0.27f); - L11 = glm::vec3(-0.29f, -0.06f, 0.01f); - L2m2 = glm::vec3(-0.11f, -0.05f, -0.12f); - L2m1 = glm::vec3(-0.26f, -0.22f, -0.47f); - L20 = glm::vec3(-0.16f, -0.09f, -0.15f); - L21 = glm::vec3( 0.56f, 0.21f, 0.14f); - L22 = glm::vec3( 0.21f, -0.05f, -0.30f); - } - break; - case DeferredLightingEffect::EUCALYPTUS_GROVE: { - L00 = glm::vec3( 0.38f, 0.43f, 0.45f); - L1m1 = glm::vec3( 0.29f, 0.36f, 0.41f); - L10 = glm::vec3( 0.04f, 0.03f, 0.01f); - L11 = glm::vec3(-0.10f, -0.10f, -0.09f); - L2m2 = glm::vec3(-0.06f, -0.06f, -0.04f); - L2m1 = glm::vec3( 0.01f, -0.01f, -0.05f); - L20 = glm::vec3(-0.09f, -0.13f, -0.15f); - L21 = glm::vec3(-0.06f, -0.05f, -0.04f); - L22 = glm::vec3( 0.02f, 0.00f, -0.05f); - } - break; - case DeferredLightingEffect::ST_PETERS_BASILICA: { - L00 = glm::vec3( 0.36f, 0.26f, 0.23f); - L1m1 = glm::vec3( 0.18f, 0.14f, 0.13f); - L10 = glm::vec3(-0.02f, -0.01f, 0.00f); - L11 = glm::vec3( 0.03f, 0.02f, -0.00f); - L2m2 = glm::vec3( 0.02f, 0.01f, -0.00f); - L2m1 = glm::vec3(-0.05f, -0.03f, -0.01f); - L20 = glm::vec3(-0.09f, -0.08f, -0.07f); - L21 = glm::vec3( 0.01f, 0.00f, 0.00f); - L22 = glm::vec3(-0.08f, -0.03f, -0.00f); - } - break; - case DeferredLightingEffect::UFFIZI_GALLERY: { - L00 = glm::vec3( 0.32f, 0.31f, 0.35f); - L1m1 = glm::vec3( 0.37f, 0.37f, 0.43f); - L10 = glm::vec3( 0.00f, 0.00f, 0.00f); - L11 = glm::vec3(-0.01f, -0.01f, -0.01f); - L2m2 = glm::vec3(-0.02f, -0.02f, -0.03f); - L2m1 = glm::vec3(-0.01f, -0.01f, -0.01f); - L20 = glm::vec3(-0.28f, -0.28f, -0.32f); - L21 = glm::vec3( 0.00f, 0.00f, 0.00f); - L22 = glm::vec3(-0.24f, -0.24f, -0.28f); - } - break; - case DeferredLightingEffect::GALILEOS_TOMB: { - L00 = glm::vec3( 1.04f, 0.76f, 0.71f); - L1m1 = glm::vec3( 0.44f, 0.34f, 0.34f); - L10 = glm::vec3(-0.22f, -0.18f, -0.17f); - L11 = glm::vec3( 0.71f, 0.54f, 0.56f); - L2m2 = glm::vec3( 0.64f, 0.50f, 0.52f); - L2m1 = glm::vec3(-0.12f, -0.09f, -0.08f); - L20 = glm::vec3(-0.37f, -0.28f, -0.32f); - L21 = glm::vec3(-0.17f, -0.13f, -0.13f); - L22 = glm::vec3( 0.55f, 0.42f, 0.42f); - } - break; - case DeferredLightingEffect::VINE_STREET_KITCHEN: { - L00 = glm::vec3( 0.64f, 0.67f, 0.73f); - L1m1 = glm::vec3( 0.28f, 0.32f, 0.33f); - L10 = glm::vec3( 0.42f, 0.60f, 0.77f); - L11 = glm::vec3(-0.05f, -0.04f, -0.02f); - L2m2 = glm::vec3(-0.10f, -0.08f, -0.05f); - L2m1 = glm::vec3( 0.25f, 0.39f, 0.53f); - L20 = glm::vec3( 0.38f, 0.54f, 0.71f); - L21 = glm::vec3( 0.06f, 0.01f, -0.02f); - L22 = glm::vec3(-0.03f, -0.02f, -0.03f); - } - break; - case DeferredLightingEffect::BREEZEWAY: { - L00 = glm::vec3( 0.32f, 0.36f, 0.38f); - L1m1 = glm::vec3( 0.37f, 0.41f, 0.45f); - L10 = glm::vec3(-0.01f, -0.01f, -0.01f); - L11 = glm::vec3(-0.10f, -0.12f, -0.12f); - L2m2 = glm::vec3(-0.13f, -0.15f, -0.17f); - L2m1 = glm::vec3(-0.01f, -0.02f, 0.02f); - L20 = glm::vec3(-0.07f, -0.08f, -0.09f); - L21 = glm::vec3( 0.02f, 0.03f, 0.03f); - L22 = glm::vec3(-0.29f, -0.32f, -0.36f); - } - break; - case DeferredLightingEffect::CAMPUS_SUNSET: { - L00 = glm::vec3( 0.79f, 0.94f, 0.98f); - L1m1 = glm::vec3( 0.44f, 0.56f, 0.70f); - L10 = glm::vec3(-0.10f, -0.18f, -0.27f); - L11 = glm::vec3( 0.45f, 0.38f, 0.20f); - L2m2 = glm::vec3( 0.18f, 0.14f, 0.05f); - L2m1 = glm::vec3(-0.14f, -0.22f, -0.31f); - L20 = glm::vec3(-0.39f, -0.40f, -0.36f); - L21 = glm::vec3( 0.09f, 0.07f, 0.04f); - L22 = glm::vec3( 0.67f, 0.67f, 0.52f); - } - break; - case DeferredLightingEffect::FUNSTON_BEACH_SUNSET: { - L00 = glm::vec3( 0.68f, 0.69f, 0.70f); - L1m1 = glm::vec3( 0.32f, 0.37f, 0.44f); - L10 = glm::vec3(-0.17f, -0.17f, -0.17f); - L11 = glm::vec3(-0.45f, -0.42f, -0.34f); - L2m2 = glm::vec3(-0.17f, -0.17f, -0.15f); - L2m1 = glm::vec3(-0.08f, -0.09f, -0.10f); - L20 = glm::vec3(-0.03f, -0.02f, -0.01f); - L21 = glm::vec3( 0.16f, 0.14f, 0.10f); - L22 = glm::vec3( 0.37f, 0.31f, 0.20f); - } - break; - } - } -}; - void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { _viewState = viewState; _simpleProgram.addShaderFromSourceCode(QGLShader::Vertex, simple_vert); @@ -206,6 +69,18 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { loadLightProgram(point_light_frag, true, _pointLight, _pointLightLocations); loadLightProgram(spot_light_frag, true, _spotLight, _spotLightLocations); + + // Allocate a global light representing the Global Directional light casting shadow (the sun) and the ambient light + _globalLights.push_back(0); + _allocatedLights.push_back(model::LightPointer(new model::Light())); + + model::LightPointer lp = _allocatedLights[0]; + + lp->setDirection(-glm::vec3(1.0f, 1.0f, 1.0f)); + lp->setColor(glm::vec3(1.0f)); + lp->setIntensity(1.0f); + lp->setType(model::Light::SUN); + lp->setAmbientSpherePreset(model::SphericalHarmonics::Preset(_ambientLightMode % model::SphericalHarmonics::NUM_PRESET)); } void DeferredLightingEffect::bindSimpleProgram() { @@ -260,30 +135,29 @@ void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radi void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radius, const glm::vec3& ambient, const glm::vec3& diffuse, const glm::vec3& specular, float constantAttenuation, float linearAttenuation, float quadraticAttenuation, const glm::vec3& direction, float exponent, float cutoff) { + + int lightID = _pointLights.size() + _spotLights.size() + _globalLights.size(); + if (lightID >= _allocatedLights.size()) { + _allocatedLights.push_back(model::LightPointer(new model::Light())); + } + model::LightPointer lp = _allocatedLights[lightID]; + + lp->setPosition(position); + lp->setMaximumRadius(radius); + lp->setColor(diffuse); + lp->setIntensity(1.0f); + //lp->setShowContour(quadraticAttenuation); + if (exponent == 0.0f && cutoff == PI) { - PointLight light; - light.position = glm::vec4(position, 1.0f); - light.radius = radius; - light.ambient = glm::vec4(ambient, 1.0f); - light.diffuse = glm::vec4(diffuse, 1.0f); - light.specular = glm::vec4(specular, 1.0f); - light.constantAttenuation = constantAttenuation; - light.linearAttenuation = linearAttenuation; - _pointLights.append(light); + lp->setType(model::Light::POINT); + _pointLights.push_back(lightID); } else { - SpotLight light; - light.position = glm::vec4(position, 1.0f); - light.radius = radius; - light.ambient = glm::vec4(ambient, 1.0f); - light.diffuse = glm::vec4(diffuse, 1.0f); - light.specular = glm::vec4(specular, 1.0f); - light.constantAttenuation = constantAttenuation; - light.linearAttenuation = linearAttenuation; - light.direction = direction; - light.exponent = exponent; - light.cutoff = cutoff; - _spotLights.append(light); + lp->setDirection(direction); + lp->setSpotAngle(cutoff); + lp->setSpotExponent(exponent); + lp->setType(model::Light::SPOT); + _spotLights.push_back(lightID); } } @@ -317,7 +191,7 @@ void DeferredLightingEffect::render() { QOpenGLFramebufferObject* freeFBO = DependencyManager::get()->getFreeFramebufferObject(); freeFBO->bind(); glClear(GL_COLOR_BUFFER_BIT); - glEnable(GL_FRAMEBUFFER_SRGB); + // glEnable(GL_FRAMEBUFFER_SRGB); glBindTexture(GL_TEXTURE_2D, primaryFBO->texture()); @@ -342,6 +216,10 @@ void DeferredLightingEffect::render() { float tMin = viewport[VIEWPORT_Y_INDEX] / (float)primaryFBO->height(); float tHeight = viewport[VIEWPORT_HEIGHT_INDEX] / (float)primaryFBO->height(); + // Fetch the ViewMatrix; + glm::mat4 invViewMat; + _viewState->getViewTransform().getMatrix(invViewMat); + ProgramObject* program = &_directionalLight; const LightLocations* locations = &_directionalLightLocations; bool shadowsEnabled = _viewState->getShadowsEnabled(); @@ -379,19 +257,24 @@ void DeferredLightingEffect::render() { program->bind(); } - if (locations->ambientSphere >= 0) { - SphericalHarmonics sh; - if (_ambientLightMode < NUM_PRESET) { - sh.assignPreset(_ambientLightMode); - } else { - sh.assignPreset(0); - } - - for (int i =0; i setUniformValue(locations->ambientSphere + i, *(((QVector4D*) &sh) + i)); - } - } + { + auto globalLight = _allocatedLights[_globalLights.front()]; + if (locations->ambientSphere >= 0) { + auto sh = globalLight->getAmbientSphere(); + for (int i =0; i setUniformValue(locations->ambientSphere + i, *(((QVector4D*) &sh) + i)); + } + } + + if (locations->lightBufferUnit >= 0) { + gpu::Batch batch; + batch.setUniformBuffer(locations->lightBufferUnit, globalLight->getSchemaBuffer()); + gpu::GLBackend::renderBatch(batch); + } + glUniformMatrix4fv(locations->invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat)); + } + float left, right, bottom, top, nearVal, farVal; glm::vec4 nearClipPlane, farClipPlane; _viewState->computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); @@ -434,27 +317,27 @@ void DeferredLightingEffect::render() { auto geometryCache = DependencyManager::get(); - if (!_pointLights.isEmpty()) { + if (!_pointLights.empty()) { _pointLight.bind(); _pointLight.setUniformValue(_pointLightLocations.nearLocation, nearVal); _pointLight.setUniformValue(_pointLightLocations.depthScale, depthScale); _pointLight.setUniformValue(_pointLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT); _pointLight.setUniformValue(_pointLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT); - foreach (const PointLight& light, _pointLights) { - _pointLight.setUniformValue(_pointLightLocations.radius, light.radius); - glLightfv(GL_LIGHT1, GL_AMBIENT, (const GLfloat*)&light.ambient); - glLightfv(GL_LIGHT1, GL_DIFFUSE, (const GLfloat*)&light.diffuse); - glLightfv(GL_LIGHT1, GL_SPECULAR, (const GLfloat*)&light.specular); - glLightfv(GL_LIGHT1, GL_POSITION, (const GLfloat*)&light.position); - glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, (light.constantAttenuation > 0.0f ? light.constantAttenuation : 0.0f)); - glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, (light.linearAttenuation > 0.0f ? light.linearAttenuation : 0.0f)); - glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, (light.quadraticAttenuation > 0.0f ? light.quadraticAttenuation : 0.0f)); - + for (auto lightID : _pointLights) { + auto light = _allocatedLights[lightID]; + + if (_pointLightLocations.lightBufferUnit >= 0) { + gpu::Batch batch; + batch.setUniformBuffer(_pointLightLocations.lightBufferUnit, light->getSchemaBuffer()); + gpu::GLBackend::renderBatch(batch); + } + glUniformMatrix4fv(_pointLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat)); + glPushMatrix(); - float expandedRadius = light.radius * (1.0f + SCALE_EXPANSION); - if (glm::distance(eyePoint, glm::vec3(light.position)) < expandedRadius + nearRadius) { + float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION); + if (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius) { glLoadIdentity(); glTranslatef(0.0f, 0.0f, -1.0f); @@ -468,7 +351,7 @@ void DeferredLightingEffect::render() { glMatrixMode(GL_MODELVIEW); } else { - glTranslatef(light.position.x, light.position.y, light.position.z); + glTranslatef(light->getPosition().x, light->getPosition().y, light->getPosition().z); geometryCache->renderSphere(expandedRadius, 32, 32, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); } @@ -479,31 +362,28 @@ void DeferredLightingEffect::render() { _pointLight.release(); } - if (!_spotLights.isEmpty()) { + if (!_spotLights.empty()) { _spotLight.bind(); _spotLight.setUniformValue(_spotLightLocations.nearLocation, nearVal); _spotLight.setUniformValue(_spotLightLocations.depthScale, depthScale); _spotLight.setUniformValue(_spotLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT); _spotLight.setUniformValue(_spotLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT); - foreach (const SpotLight& light, _spotLights) { - _spotLight.setUniformValue(_spotLightLocations.radius, light.radius); - glLightfv(GL_LIGHT1, GL_AMBIENT, (const GLfloat*)&light.ambient); - glLightfv(GL_LIGHT1, GL_DIFFUSE, (const GLfloat*)&light.diffuse); - glLightfv(GL_LIGHT1, GL_SPECULAR, (const GLfloat*)&light.specular); - glLightfv(GL_LIGHT1, GL_POSITION, (const GLfloat*)&light.position); - glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, (light.constantAttenuation > 0.0f ? light.constantAttenuation : 0.0f)); - glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, (light.linearAttenuation > 0.0f ? light.linearAttenuation : 0.0f)); - glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, (light.quadraticAttenuation > 0.0f ? light.quadraticAttenuation : 0.0f)); - glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, (const GLfloat*)&light.direction); - glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, light.exponent); - glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, glm::degrees(light.cutoff)); - + for (auto lightID : _spotLights) { + auto light = _allocatedLights[lightID]; + + if (_spotLightLocations.lightBufferUnit >= 0) { + gpu::Batch batch; + batch.setUniformBuffer(_spotLightLocations.lightBufferUnit, light->getSchemaBuffer()); + gpu::GLBackend::renderBatch(batch); + } + glUniformMatrix4fv(_spotLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat)); + glPushMatrix(); - float expandedRadius = light.radius * (1.0f + SCALE_EXPANSION); - float edgeRadius = expandedRadius / glm::cos(light.cutoff); - if (glm::distance(eyePoint, glm::vec3(light.position)) < edgeRadius + nearRadius) { + float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION); + float edgeRadius = expandedRadius / glm::cos(light->getSpotAngle()); + if (glm::distance(eyePoint, glm::vec3(light->getPosition())) < edgeRadius + nearRadius) { glLoadIdentity(); glTranslatef(0.0f, 0.0f, -1.0f); @@ -517,12 +397,12 @@ void DeferredLightingEffect::render() { glMatrixMode(GL_MODELVIEW); } else { - glTranslatef(light.position.x, light.position.y, light.position.z); - glm::quat spotRotation = rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), light.direction); + glTranslatef(light->getPosition().x, light->getPosition().y, light->getPosition().z); + glm::quat spotRotation = rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), light->getDirection()); glm::vec3 axis = glm::axis(spotRotation); glRotatef(glm::degrees(glm::angle(spotRotation)), axis.x, axis.y, axis.z); - glTranslatef(0.0f, 0.0f, -light.radius * (1.0f + SCALE_EXPANSION * 0.5f)); - geometryCache->renderCone(expandedRadius * glm::tan(light.cutoff), + glTranslatef(0.0f, 0.0f, -light->getMaximumRadius() * (1.0f + SCALE_EXPANSION * 0.5f)); + geometryCache->renderCone(expandedRadius * glm::tan(light->getSpotAngle()), expandedRadius, 32, 1); } @@ -545,7 +425,7 @@ void DeferredLightingEffect::render() { glBindTexture(GL_TEXTURE_2D, 0); freeFBO->release(); - glDisable(GL_FRAMEBUFFER_SRGB); + // glDisable(GL_FRAMEBUFFER_SRGB); glDisable(GL_CULL_FACE); @@ -607,11 +487,46 @@ void DeferredLightingEffect::loadLightProgram(const char* fragSource, bool limit locations.depthTexCoordScale = program.uniformLocation("depthTexCoordScale"); locations.radius = program.uniformLocation("radius"); locations.ambientSphere = program.uniformLocation("ambientSphere.L00"); + locations.invViewMat = program.uniformLocation("invViewMat"); + + GLint loc = -1; +#if defined(Q_OS_MAC) + loc = program.uniformLocation("lightBuffer"); + if (loc >= 0) { + locations.lightBufferUnit = loc; + } else { + locations.lightBufferUnit = -1; + } +#elif defined(Q_OS_WIN) + loc = glGetUniformBlockIndex(program.programId(), "lightBuffer"); + if (loc >= 0) { + glUniformBlockBinding(program.programId(), loc, 0); + locations.lightBufferUnit = 0; + } else { + locations.lightBufferUnit = -1; + } +#else + loc = program.uniformLocation("lightBuffer"); + if (loc >= 0) { + locations.lightBufferUnit = loc; + } else { + locations.lightBufferUnit = -1; + } +#endif program.release(); } void DeferredLightingEffect::setAmbientLightMode(int preset) { - if ((preset >= -1) && (preset < NUM_PRESET)) { + if ((preset >= -1) && (preset < model::SphericalHarmonics::NUM_PRESET)) { _ambientLightMode = preset; + auto light = _allocatedLights.front(); + light->setAmbientSpherePreset(model::SphericalHarmonics::Preset(preset % model::SphericalHarmonics::NUM_PRESET)); } } + +void DeferredLightingEffect::setGlobalLight(const glm::vec3& direction, const glm::vec3& diffuse, float intensity) { + auto light = _allocatedLights.front(); + light->setDirection(direction); + light->setColor(diffuse); + light->setIntensity(intensity); +} diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 6c563605ea..eeb92f19c7 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -19,6 +19,8 @@ #include "ProgramObject.h" +#include "model/Light.h" + class AbstractViewStateInterface; class PostLightingRenderable; @@ -71,25 +73,12 @@ public: void prepare(); void render(); - enum AmbientLightPreset { - OLD_TOWN_SQUARE = 0, - GRACE_CATHEDRAL, - EUCALYPTUS_GROVE, - ST_PETERS_BASILICA, - UFFIZI_GALLERY, - GALILEOS_TOMB, - VINE_STREET_KITCHEN, - BREEZEWAY, - CAMPUS_SUNSET, - FUNSTON_BEACH_SUNSET, - - NUM_PRESET, - }; - + // update global lighting void setAmbientLightMode(int preset); + void setGlobalLight(const glm::vec3& direction, const glm::vec3& diffuse, float intensity); private: - DeferredLightingEffect() { } + DeferredLightingEffect() {} virtual ~DeferredLightingEffect() { } class LightLocations { @@ -102,6 +91,8 @@ private: int depthTexCoordScale; int radius; int ambientSphere; + int lightBufferUnit; + int invViewMat; }; static void loadLightProgram(const char* fragSource, bool limited, ProgramObject& program, LightLocations& locations); @@ -146,9 +137,13 @@ private: float exponent; float cutoff; }; - - QVector _pointLights; - QVector _spotLights; + + typedef std::vector< model::LightPointer > Lights; + + Lights _allocatedLights; + std::vector _globalLights; + std::vector _pointLights; + std::vector _spotLights; QVector _postLightingRenderables; AbstractViewStateInterface* _viewState; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 2d9791409f..dffadc730f 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1154,7 +1154,7 @@ void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxC gpu::Batch batch; - glEnable(GL_TEXTURE_2D); +// glEnable(GL_TEXTURE_2D); //glBindTexture(GL_TEXTURE_2D, _currentTextureID); // this is quad specific... batch.setInputFormat(details.streamFormat); @@ -1168,8 +1168,8 @@ void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxC glDisableClientState(GL_COLOR_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); + // glBindTexture(GL_TEXTURE_2D, 0); + // glDisable(GL_TEXTURE_2D); } void GeometryCache::renderQuad(const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color, int id) { diff --git a/libraries/render-utils/src/Light.slh b/libraries/render-utils/src/Light.slh new file mode 100755 index 0000000000..abb9fb6c2a --- /dev/null +++ b/libraries/render-utils/src/Light.slh @@ -0,0 +1,107 @@ + +<@if not LIGHT_SLH@> +<@def LIGHT_SLH@> + +struct Light { + vec4 _position; + vec4 _direction; + vec4 _color; + vec4 _attenuation; + vec4 _spot; + + vec4 _shadow; + + vec4 _control; +}; + +vec3 getLightPosition(Light l) { return l._position.xyz; } +vec3 getLightDirection(Light l) { return l._direction.xyz; } // direction is -Z axis + +vec3 getLightColor(Light l) { return l._color.rgb; } +float getLightIntensity(Light l) { return l._color.w; } + +float evalLightAttenuation(Light l, float r) { + float d = max(r - l._attenuation.x, 0.0); + float denom = d * l._attenuation.y + 1.0; + float attenuation = 1.0 / (denom * denom); + return max((attenuation - l._attenuation.z)/(1.0 - l._attenuation.z), 0.0); + // return clamp(1.0/(l._attenuation.x + l._attenuation.y * r + l._attenuation.z * r * r), 0.0, 1.0); +} + +float getLightSpotAngleCos(Light l) { + return l._spot.x; +} + +vec2 getLightSpotOutsideNormal2(Light l) { + return vec2(-l._spot.y, l._spot.x); +} + +float evalLightSpotAttenuation(Light l, float cosA) { + return pow(cosA, l._spot.w); +} + +float getLightSquareRadius(Light l) { + return l._attenuation.w * l._attenuation.w; +} + +float getLightRadius(Light l) { + return l._attenuation.w; +} + +float getLightAttenuationCutoff(Light l) { + return l._attenuation.z; +} + +float getLightShowContour(Light l) { + return l._control.w; +} + +<@if GLPROFILE == PC_GL@> +uniform lightBuffer { + Light light; +}; +Light getLight() { + return light; +} +<@elif GLPROFILE == MAC_GL@> +uniform vec4 lightBuffer[9]; +Light getLight() { + Light light; + light._position = lightBuffer[0]; + light._direction = lightBuffer[1]; + light._color = lightBuffer[2]; + light._attenuation = lightBuffer[3]; + light._spot = lightBuffer[4]; + light._shadow = lightBuffer[5]; + light._control = lightBuffer[6]; + + return light; +} +<@else@> +uniform vec4 lightBuffer[9]; +Light getLight() { + Light light; + light._position = lightBuffer[0]; + light._direction = lightBuffer[1]; + light._color = lightBuffer[2]; + light._attenuation = lightBuffer[3]; + light._spot = lightBuffer[4]; + light._shadow = lightBuffer[5]; + light._control = lightBuffer[6]; + + return light; +} +<@endif@> + + + +<@endif@> diff --git a/libraries/render-utils/src/TextRenderer.cpp b/libraries/render-utils/src/TextRenderer.cpp index fb720b3040..9238db91e3 100644 --- a/libraries/render-utils/src/TextRenderer.cpp +++ b/libraries/render-utils/src/TextRenderer.cpp @@ -511,6 +511,10 @@ TextRenderer::TextRenderer(const char* family, float pointSize, int weight, bool italic, EffectType effect, int effectThickness, const QColor& color) : _effectType(effect), _effectThickness(effectThickness), _pointSize(pointSize), _color(color), _font(loadFont(family)) { + if (!_font) { + qWarning() << "Unable to load font with family " << family; + _font = loadFont("Courier"); + } if (1 != _effectThickness) { qWarning() << "Effect thickness not current supported"; } @@ -524,7 +528,10 @@ TextRenderer::~TextRenderer() { glm::vec2 TextRenderer::computeExtent(const QString & str) const { float scale = (_pointSize / DEFAULT_POINT_SIZE) * 0.25f; - return _font->computeExtent(str) * scale; + if (_font) { + return _font->computeExtent(str) * scale; + } + return glm::vec2(0.1f,0.1f); } float TextRenderer::draw(float x, float y, const QString & str, @@ -544,7 +551,9 @@ float TextRenderer::draw(float x, float y, const QString & str, // scale at all. mv.translate(glm::vec2(x, y)).scale(glm::vec3(scale, -scale, scale)); // The font does all the OpenGL work - result = _font->drawString(x, y, str, actualColor, _effectType, bounds / scale); + if (_font) { + result = _font->drawString(x, y, str, actualColor, _effectType, bounds / scale); + } }); return result.x; } diff --git a/libraries/render-utils/src/directional_ambient_light.slf b/libraries/render-utils/src/directional_ambient_light.slf index 803bd5ac30..308a8a73a7 100755 --- a/libraries/render-utils/src/directional_ambient_light.slf +++ b/libraries/render-utils/src/directional_ambient_light.slf @@ -15,22 +15,23 @@ // Everything about deferred buffer <@include DeferredBuffer.slh@> -<@include DeferredLighting.slh@> +<@include DeferredGlobalLight.slh@> void main(void) { DeferredFragment frag = unpackDeferredFragment(gl_TexCoord[0].st); + // Light mapped or not ? if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) { - gl_FragColor = vec4( evalLightmappedColor( + vec3 color = evalLightmappedColor( 1.0, frag.normal, frag.diffuse, - frag.specularVal.xyz), - 1.0); + frag.specularVal.xyz); + + gl_FragColor = vec4(color, 1.0); } else { - vec3 color = evalAmbientSphereColor(frag.normal, frag.diffuse, frag.specular, frag.gloss) - + evalDirectionalColor(1.0, + vec3 color = evalAmbienSphereGlobalColor(1.0, frag.position.xyz, frag.normal, frag.diffuse, diff --git a/libraries/render-utils/src/directional_ambient_light_cascaded_shadow_map.slf b/libraries/render-utils/src/directional_ambient_light_cascaded_shadow_map.slf index 5f88c558d3..db017cf5ac 100755 --- a/libraries/render-utils/src/directional_ambient_light_cascaded_shadow_map.slf +++ b/libraries/render-utils/src/directional_ambient_light_cascaded_shadow_map.slf @@ -15,7 +15,7 @@ // Everything about deferred buffer <@include DeferredBuffer.slh@> -<@include DeferredLighting.slh@> +<@include DeferredGlobalLight.slh@> // Everything about shadow <@include Shadow.slh@> @@ -36,8 +36,7 @@ void main(void) { frag.specularVal.xyz), 1.0); } else { - vec3 color = evalAmbientSphereColor(frag.normal, frag.diffuse, frag.specular, frag.gloss) - + evalDirectionalColor(shadowAttenuation, + vec3 color = evalAmbienSphereGlobalColor(shadowAttenuation, frag.position.xyz, frag.normal, frag.diffuse, diff --git a/libraries/render-utils/src/directional_ambient_light_shadow_map.slf b/libraries/render-utils/src/directional_ambient_light_shadow_map.slf index 6c241853e3..43d3e91dbe 100755 --- a/libraries/render-utils/src/directional_ambient_light_shadow_map.slf +++ b/libraries/render-utils/src/directional_ambient_light_shadow_map.slf @@ -15,7 +15,7 @@ // Everything about deferred buffer <@include DeferredBuffer.slh@> -<@include DeferredLighting.slh@> +<@include DeferredGlobalLight.slh@> // Everything about shadow <@include Shadow.slh@> @@ -37,8 +37,7 @@ void main(void) { frag.specularVal.xyz), 1.0); } else { - vec3 color = evalAmbientSphereColor(frag.normal, frag.diffuse, frag.specular, frag.gloss) - + evalDirectionalColor(shadowAttenuation, + vec3 color = evalAmbienSphereGlobalColor(shadowAttenuation, frag.position.xyz, frag.normal, frag.diffuse, diff --git a/libraries/render-utils/src/directional_light.slf b/libraries/render-utils/src/directional_light.slf index 8ff6cd6c87..3e708f849e 100644 --- a/libraries/render-utils/src/directional_light.slf +++ b/libraries/render-utils/src/directional_light.slf @@ -15,7 +15,7 @@ // Everything about deferred buffer <@include DeferredBuffer.slh@> -<@include DeferredLighting.slh@> +<@include DeferredGlobalLight.slh@> void main(void) { DeferredFragment frag = unpackDeferredFragment(gl_TexCoord[0].st); @@ -29,8 +29,7 @@ void main(void) { frag.specularVal.xyz), 1.0); } else { - vec3 color = evalAmbientColor(frag.normal, frag.diffuse, frag.specular, frag.gloss) - + evalDirectionalColor(1.0, + vec3 color = evalAmbienGlobalColor(1.0, frag.position.xyz, frag.normal, frag.diffuse, diff --git a/libraries/render-utils/src/directional_light_cascaded_shadow_map.slf b/libraries/render-utils/src/directional_light_cascaded_shadow_map.slf index ccf8909b64..90b3bf1d2b 100644 --- a/libraries/render-utils/src/directional_light_cascaded_shadow_map.slf +++ b/libraries/render-utils/src/directional_light_cascaded_shadow_map.slf @@ -15,7 +15,7 @@ // Everything about deferred buffer <@include DeferredBuffer.slh@> -<@include DeferredLighting.slh@> +<@include DeferredGlobalLight.slh@> // Everything about shadow <@include Shadow.slh@> @@ -36,8 +36,7 @@ void main(void) { frag.specularVal.xyz), 1.0); } else { - vec3 color = evalAmbientColor(frag.normal, frag.diffuse, frag.specular, frag.gloss) - + evalDirectionalColor(shadowAttenuation, + vec3 color = evalAmbienGlobalColor(shadowAttenuation, frag.position.xyz, frag.normal, frag.diffuse, diff --git a/libraries/render-utils/src/directional_light_shadow_map.slf b/libraries/render-utils/src/directional_light_shadow_map.slf index 13435e9101..5029b57020 100644 --- a/libraries/render-utils/src/directional_light_shadow_map.slf +++ b/libraries/render-utils/src/directional_light_shadow_map.slf @@ -15,7 +15,7 @@ // Everything about deferred buffer <@include DeferredBuffer.slh@> -<@include DeferredLighting.slh@> +<@include DeferredGlobalLight.slh@> // Everything about shadow <@include Shadow.slh@> @@ -37,8 +37,7 @@ void main(void) { frag.specularVal.xyz), 1.0); } else { - vec3 color = evalAmbientColor(frag.normal, frag.diffuse, frag.specular, frag.gloss) - + evalDirectionalColor(shadowAttenuation, + vec3 color = evalAmbienGlobalColor(shadowAttenuation, frag.position.xyz, frag.normal, frag.diffuse, diff --git a/libraries/render-utils/src/model_lightmap.slv b/libraries/render-utils/src/model_lightmap.slv index 23d99b399a..1ea5b7b68a 100755 --- a/libraries/render-utils/src/model_lightmap.slv +++ b/libraries/render-utils/src/model_lightmap.slv @@ -33,8 +33,9 @@ void main(void) { // and the texture coordinates gl_TexCoord[0] = texcoordMatrices[0] * vec4(gl_MultiTexCoord0.xy, 0.0, 1.0); + // interpolatedTexcoord1 = vec2(texcoordMatrices[1] * vec4(gl_MultiTexCoord0.xy, 0.0, 1.0)).xy; interpolatedTexcoord1 = vec2(texcoordMatrices[1] * vec4(texcoord1.xy, 0.0, 1.0)).xy; - + // use standard pipeline transform gl_Position = ftransform(); } diff --git a/libraries/render-utils/src/point_light.slf b/libraries/render-utils/src/point_light.slf index e5142b25ce..abe904ecce 100644 --- a/libraries/render-utils/src/point_light.slf +++ b/libraries/render-utils/src/point_light.slf @@ -15,41 +15,61 @@ // Everything about deferred buffer <@include DeferredBuffer.slh@> -// the radius (hard cutoff) of the light effect -uniform float radius; +//Everything about deferred lighting +<@include DeferredLighting.slh@> + +// Everything about light +<@include Light.slh@> + +// The view Matrix +uniform mat4 invViewMat; void main(void) { - // get the depth and exit early if it doesn't pass the test + // Grab the fragment data from the uv vec2 texCoord = gl_TexCoord[0].st / gl_TexCoord[0].q; - float depth = texture2D(depthMap, texCoord).r; + DeferredFragment frag = unpackDeferredFragment(texCoord); + + // Kill if in front of the light volume + float depth = frag.depthVal; if (depth < gl_FragCoord.z) { discard; } - // compute the view space position using the depth - float z = near / (depth * depthScale - 1.0); - vec4 position = vec4((depthTexCoordOffset + texCoord * depthTexCoordScale) * z, z, 1.0); - - // get the normal from the map - vec4 normal = texture2D(normalMap, texCoord); - vec4 normalizedNormal = normalize(normal * 2.0 - vec4(1.0, 1.0, 1.0, 2.0)); - - // compute the base color based on OpenGL lighting model - vec4 lightVector = gl_LightSource[1].position - position; - float lightDistance = length(lightVector); - lightVector = lightVector / lightDistance; - float diffuse = dot(normalizedNormal, lightVector); - float facingLight = step(0.0, diffuse); - vec4 baseColor = texture2D(diffuseMap, texCoord) * (gl_FrontLightProduct[1].ambient + - gl_FrontLightProduct[1].diffuse * (diffuse * facingLight)); - - // compute attenuation based on distance, etc. - float attenuation = step(lightDistance, radius) / dot(vec3(gl_LightSource[1].constantAttenuation, - gl_LightSource[1].linearAttenuation, gl_LightSource[1].quadraticAttenuation), - vec3(1.0, lightDistance, lightDistance * lightDistance)); - - // add base to specular, modulate by attenuation - float specular = facingLight * max(0.0, dot(normalize(lightVector - normalize(vec4(position.xyz, 0.0))), - normalizedNormal)); - vec4 specularColor = texture2D(specularMap, texCoord); - gl_FragColor = vec4((baseColor.rgb + pow(specular, specularColor.a * 128.0) * specularColor.rgb) * attenuation, 0.0); + + // Need the light now + Light light = getLight(); + + // Make the Light vector going from fragment to light center in world space + vec4 fragPos = invViewMat * frag.position; + vec3 fragLightVec = getLightPosition(light) - fragPos.xyz; + + // Kill if too far from the light center + if (dot(fragLightVec, fragLightVec) > getLightSquareRadius(light)) { + discard; + } + + // Allright we re valid in the volume + float fragLightDistance = length(fragLightVec); + vec3 fragLightDir = fragLightVec / fragLightDistance; + + // Eval shading + vec3 fragNormal = vec3(invViewMat * vec4(frag.normal, 0.0)); + vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0); + vec3 fragEyeDir = normalize(fragEyeVector.xyz); + vec4 shading = evalFragShading(fragNormal, fragLightDir, fragEyeDir, frag.specular, frag.gloss); + + // Eval attenuation + float radialAttenuation = evalLightAttenuation(light, fragLightDistance); + + // Final Lighting color + vec3 fragColor = shading.w * (frag.diffuse + shading.xyz); + gl_FragColor = vec4(fragColor * radialAttenuation * getLightColor(light) * getLightIntensity(light), 0.0); + + if (getLightShowContour(light) > 0.0) { + // Show edge + float edge = abs(2.0 * ((getLightRadius(light) - fragLightDistance) / (0.1)) - 1.0); + if (edge < 1) { + float edgeCoord = exp2(-8.0*edge*edge); + gl_FragColor = vec4(edgeCoord * edgeCoord * getLightShowContour(light) * getLightColor(light), 0.0); + } + } } diff --git a/libraries/render-utils/src/spot_light.slf b/libraries/render-utils/src/spot_light.slf index f987760eb8..95fabae3ed 100644 --- a/libraries/render-utils/src/spot_light.slf +++ b/libraries/render-utils/src/spot_light.slf @@ -15,43 +15,73 @@ // Everything about deferred buffer <@include DeferredBuffer.slh@> -// the radius (hard cutoff) of the light effect -uniform float radius; +//Everything about deferred lighting +<@include DeferredLighting.slh@> + +// Everything about light +<@include Light.slh@> + +// The view Matrix +uniform mat4 invViewMat; void main(void) { - // get the depth and exit early if it doesn't pass the test + // Grab the fragment data from the uv vec2 texCoord = gl_TexCoord[0].st / gl_TexCoord[0].q; - float depth = texture2D(depthMap, texCoord).r; + DeferredFragment frag = unpackDeferredFragment(texCoord); + + // Kill if in front of the light volume + float depth = frag.depthVal; if (depth < gl_FragCoord.z) { discard; } - // compute the view space position using the depth - float z = near / (depth * depthScale - 1.0); - vec4 position = vec4((depthTexCoordOffset + texCoord * depthTexCoordScale) * z, z, 1.0); - - // get the normal from the map - vec4 normal = texture2D(normalMap, texCoord); - vec4 normalizedNormal = normalize(normal * 2.0 - vec4(1.0, 1.0, 1.0, 2.0)); - - // compute the base color based on OpenGL lighting model - vec4 lightVector = gl_LightSource[1].position - position; - float lightDistance = length(lightVector); - lightVector = lightVector / lightDistance; - float diffuse = dot(normalizedNormal, lightVector); - float facingLight = step(0.0, diffuse); - vec4 baseColor = texture2D(diffuseMap, texCoord) * (gl_FrontLightProduct[1].ambient + - gl_FrontLightProduct[1].diffuse * (diffuse * facingLight)); - - // compute attenuation based on spot angle, distance, etc. - float cosSpotAngle = max(-dot(lightVector.xyz, gl_LightSource[1].spotDirection), 0.0); - float attenuation = step(lightDistance, radius) * step(gl_LightSource[1].spotCosCutoff, cosSpotAngle) * - pow(cosSpotAngle, gl_LightSource[1].spotExponent) / dot(vec3(gl_LightSource[1].constantAttenuation, - gl_LightSource[1].linearAttenuation, gl_LightSource[1].quadraticAttenuation), - vec3(1.0, lightDistance, lightDistance * lightDistance)); - - // add base to specular, modulate by attenuation - float specular = facingLight * max(0.0, dot(normalize(lightVector - normalize(vec4(position.xyz, 0.0))), - normalizedNormal)); - vec4 specularColor = texture2D(specularMap, texCoord); - gl_FragColor = vec4((baseColor.rgb + pow(specular, specularColor.a * 128.0) * specularColor.rgb) * attenuation, 0.0); + + // Need the light now + Light light = getLight(); + + // Make the Light vector going from fragment to light center in world space + vec4 fragPos = invViewMat * frag.position; + vec3 fragLightVec = getLightPosition(light) - fragPos.xyz; + + // Kill if too far from the light center + if (dot(fragLightVec, fragLightVec) > getLightSquareRadius(light)) { + discard; + } + + // Allright we re valid in the volume + float fragLightDistance = length(fragLightVec); + vec3 fragLightDir = fragLightVec / fragLightDistance; + + // Kill if not in the spot light (ah ah !) + vec3 lightSpotDir = getLightDirection(light); + float cosSpotAngle = max(-dot(fragLightDir, lightSpotDir), 0.0); + if (cosSpotAngle < getLightSpotAngleCos(light)) { + discard; + } + + // Eval shading + vec3 fragNormal = vec3(invViewMat * vec4(frag.normal, 0.0)); + vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0); + vec3 fragEyeDir = normalize(fragEyeVector.xyz); + vec4 shading = evalFragShading(fragNormal, fragLightDir, fragEyeDir, frag.specular, frag.gloss); + + // Eval attenuation + float radialAttenuation = evalLightAttenuation(light, fragLightDistance); + float angularAttenuation = evalLightSpotAttenuation(light, cosSpotAngle); + + // Final Lighting color + vec3 fragColor = shading.w * (frag.diffuse + shading.xyz); + gl_FragColor = vec4(fragColor * angularAttenuation * radialAttenuation * getLightColor(light) * getLightIntensity(light), 0.0); + + if (getLightShowContour(light) > 0.0) { + // Show edges + float edgeDistR = (getLightRadius(light) - fragLightDistance); + float edgeDistS = dot(fragLightDistance * vec2(cosSpotAngle, sqrt(1.0 - cosSpotAngle * cosSpotAngle)), -getLightSpotOutsideNormal2(light)); + float edgeDist = min(edgeDistR, edgeDistS); + float edge = abs(2.0 * (edgeDist / (0.1)) - 1.0); + if (edge < 1) { + float edgeCoord = exp2(-8.0*edge*edge); + gl_FragColor = vec4(edgeCoord * edgeCoord * getLightColor(light), 0.0); + } + } } + diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index 32b9eb23e2..387852fe00 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -31,44 +31,41 @@ AudioScriptingInterface::AudioScriptingInterface() : } ScriptAudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) { - AudioInjector* injector = NULL; - QMetaObject::invokeMethod(this, "invokedPlaySound", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(AudioInjector*, injector), - Q_ARG(Sound*, sound), Q_ARG(const AudioInjectorOptions&, injectorOptions)); - if (injector) { - return new ScriptAudioInjector(injector); - } else { - return NULL; + if (QThread::currentThread() != thread()) { + ScriptAudioInjector* injector = NULL; + + QMetaObject::invokeMethod(this, "playSound", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(ScriptAudioInjector*, injector), + Q_ARG(Sound*, sound), Q_ARG(const AudioInjectorOptions&, injectorOptions)); + return injector; } -} - -AudioInjector* AudioScriptingInterface::invokedPlaySound(Sound* sound, const AudioInjectorOptions& injectorOptions) { + if (sound) { // stereo option isn't set from script, this comes from sound metadata or filename AudioInjectorOptions optionsCopy = injectorOptions; optionsCopy.stereo = sound->isStereo(); - + QThread* injectorThread = new QThread(); injectorThread->setObjectName("Audio Injector Thread"); - + AudioInjector* injector = new AudioInjector(sound, optionsCopy); injector->setLocalAudioInterface(_localAudioInterface); - + injector->moveToThread(injectorThread); - + // start injecting when the injector thread starts connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio); - + // connect the right slots and signals for AudioInjector and thread cleanup connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit); connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater); - + injectorThread->start(); - - return injector; - + + return new ScriptAudioInjector(injector); + } else { qDebug() << "AudioScriptingInterface::playSound called with null Sound object."; return NULL; } -} +} \ No newline at end of file diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index ed52d951ad..b74c520670 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -32,9 +32,6 @@ protected: signals: void mutedByMixer(); void environmentMuted(); - -private slots: - AudioInjector* invokedPlaySound(Sound* sound, const AudioInjectorOptions& injectorOptions); private: AudioScriptingInterface(); diff --git a/libraries/script-engine/src/ScriptAudioInjector.cpp b/libraries/script-engine/src/ScriptAudioInjector.cpp index beb056468f..7bdf78be63 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.cpp +++ b/libraries/script-engine/src/ScriptAudioInjector.cpp @@ -13,7 +13,9 @@ QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in) { // when the script goes down we want to cleanup the injector - QObject::connect(engine, &QScriptEngine::destroyed, in, &ScriptAudioInjector::stopInjectorImmediately); + + QObject::connect(engine, &QScriptEngine::destroyed, in, &ScriptAudioInjector::stopInjectorImmediately, + Qt::DirectConnection); return engine->newQObject(in, QScriptEngine::ScriptOwnership); } @@ -36,5 +38,6 @@ ScriptAudioInjector::~ScriptAudioInjector() { } void ScriptAudioInjector::stopInjectorImmediately() { + qDebug() << "ScriptAudioInjector::stopInjectorImmediately called to stop audio injector immediately."; _injector->stopAndDeleteLater(); } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 062bb0ca06..0956374238 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -272,8 +272,12 @@ QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* ob } void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments) { + registerFunction(globalObject(), name, fun, numArguments); +} + +void ScriptEngine::registerFunction(QScriptValue parent, const QString& name, QScriptEngine::FunctionSignature fun, int numArguments) { QScriptValue scriptFun = newFunction(fun, numArguments); - globalObject().setProperty(name, scriptFun); + parent.setProperty(name, scriptFun); } void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 6cc9f64d4c..f2911842e6 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -58,6 +58,8 @@ public: void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, QScriptEngine::FunctionSignature setter, QScriptValue object = QScriptValue::NullValue); void registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments = -1); + void registerFunction(QScriptValue parent, const QString& name, QScriptEngine::FunctionSignature fun, + int numArguments = -1); Q_INVOKABLE void setIsAvatar(bool isAvatar); bool isAvatar() const { return _isAvatar; } diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index e08f00c0ac..346a82a3b0 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -100,6 +100,8 @@ public: // Left will be inversed before the multiplication static Transform& inverseMult(Transform& result, const Transform& left, const Transform& right); + Vec4 transform(const Vec4& pos) const; + protected: enum Flag { @@ -414,6 +416,13 @@ inline Transform& Transform::inverseMult( Transform& result, const Transform& le return result; } +inline Transform::Vec4 Transform::transform(const Vec4& pos) const { + Mat4 m; + getMatrix(m); + return m * pos; +} + + inline Transform::Mat4& Transform::getCachedMatrix(Transform::Mat4& result) const { updateCache(); result = (*_matrix); diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 106c56b0fe..938b5470e8 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -219,14 +219,14 @@ static QScriptValue createRandomScriptValue(bool complex = false, bool ensureHas case 4: { int length = randIntInRange(2, 6); - QScriptValue value = ScriptCache::getInstance()->getEngine()->newArray(length); + QScriptValue value = DependencyManager::get()->getEngine()->newArray(length); for (int i = 0; i < length; i++) { value.setProperty(i, createRandomScriptValue()); } return value; } default: { - QScriptValue value = ScriptCache::getInstance()->getEngine()->newObject(); + QScriptValue value = DependencyManager::get()->getEngine()->newObject(); if (ensureHashOrder) { // we can't depend on the iteration order, so if we need it to be the same (as when comparing bytes), we // can only have one property @@ -747,7 +747,7 @@ static SharedObjectPointer mutate(const SharedObjectPointer& state) { case 3: { SharedObjectPointer newState = state->clone(true); QScriptValue oldValue = static_cast(newState.data())->getBizzle(); - QScriptValue newValue = ScriptCache::getInstance()->getEngine()->newObject(); + QScriptValue newValue = DependencyManager::get()->getEngine()->newObject(); for (QScriptValueIterator it(oldValue); it.hasNext(); ) { it.next(); newValue.setProperty(it.scriptName(), it.value()); @@ -755,8 +755,8 @@ static SharedObjectPointer mutate(const SharedObjectPointer& state) { switch (randIntInRange(0, 2)) { case 0: { QScriptValue oldArray = oldValue.property("foo"); - int oldLength = oldArray.property(ScriptCache::getInstance()->getLengthString()).toInt32(); - QScriptValue newArray = ScriptCache::getInstance()->getEngine()->newArray(oldLength); + int oldLength = oldArray.property(DependencyManager::get()->getLengthString()).toInt32(); + QScriptValue newArray = DependencyManager::get()->getEngine()->newArray(oldLength); for (int i = 0; i < oldLength; i++) { newArray.setProperty(i, oldArray.property(i)); } @@ -1203,8 +1203,8 @@ TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) : _bong(bong) { sharedObjectsCreated++; - _bizzle = ScriptCache::getInstance()->getEngine()->newObject(); - _bizzle.setProperty("foo", ScriptCache::getInstance()->getEngine()->newArray(4)); + _bizzle = DependencyManager::get()->getEngine()->newObject(); + _bizzle.setProperty("foo", DependencyManager::get()->getEngine()->newArray(4)); } TestSharedObjectA::~TestSharedObjectA() {