diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index cfee18941c..e125a44783 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -66,6 +66,9 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri // set the logging target to the the CHILD_TARGET_NAME LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); + // make sure we output process IDs for a child AC otherwise it's insane to parse + LogHandler::getInstance().setShouldOutputPID(true); + // setup our _requestAssignment member variable from the passed arguments _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool); diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index d19eb90df6..8c6478b59f 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -39,9 +39,9 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen _walletUUID(walletUUID), _assignmentServerHostname(assignmentServerHostname), _assignmentServerPort(assignmentServerPort) -{ +{ qDebug() << "_requestAssignmentType =" << _requestAssignmentType; - + // start the Logging class with the parent's target name LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME); @@ -77,13 +77,13 @@ void AssignmentClientMonitor::simultaneousWaitOnChildren(int waitMsecs) { while(_childProcesses.size() > 0 && !waitTimer.hasExpired(waitMsecs)) { // continue processing events so we can handle a process finishing up QCoreApplication::processEvents(); - } + } } void AssignmentClientMonitor::childProcessFinished() { QProcess* childProcess = qobject_cast(sender()); qint64 processID = _childProcesses.key(childProcess); - + if (processID > 0) { qDebug() << "Child process" << processID << "has finished. Removing from internal map."; _childProcesses.remove(processID); @@ -98,17 +98,17 @@ void AssignmentClientMonitor::stopChildProcesses() { qDebug() << "Attempting to terminate child process" << childProcess->processId(); childProcess->terminate(); } - + simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS); - + if (_childProcesses.size() > 0) { // ask even more firmly foreach(QProcess* childProcess, _childProcesses) { qDebug() << "Attempting to kill child process" << childProcess->processId(); childProcess->kill(); } - - simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS); + + simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS); } } @@ -122,7 +122,7 @@ void AssignmentClientMonitor::aboutToQuit() { void AssignmentClientMonitor::spawnChildClient() { QProcess* assignmentClient = new QProcess(this); - + // unparse the parts of the command-line that the child cares about QStringList _childArguments; if (_assignmentPool != "") { @@ -153,7 +153,7 @@ void AssignmentClientMonitor::spawnChildClient() { // make sure that the output from the child process appears in our output assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels); - + assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments); // make sure we hear that this process has finished when it does @@ -194,7 +194,7 @@ void AssignmentClientMonitor::checkSpares() { qDebug() << "asking child" << aSpareId << "to exit."; SharedNodePointer childNode = nodeList->nodeWithUUID(aSpareId); childNode->activateLocalSocket(); - + QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode); nodeList->writeUnverifiedDatagram(diePacket, childNode); } @@ -239,7 +239,7 @@ void AssignmentClientMonitor::readPendingDatagrams() { // update our records about how to reach this child matchingNode->setLocalSocket(senderSockAddr); - QVariantMap packetVariantMap = + QVariantMap packetVariantMap = JSONBreakableMarshal::fromStringBuffer(receivedPacket.mid(numBytesForPacketHeader(receivedPacket))); QJsonObject unpackedStatsJSON = QJsonObject::fromVariantMap(packetVariantMap); diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index ea7ac0648b..c3ca6796bc 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -850,7 +850,9 @@ void AudioMixer::run() { ++_numStatFrames; + // since we're a while loop we need to help Qt's event processing QCoreApplication::processEvents(); + QCoreApplication::sendPostedEvents(this, 0); if (_isFinished) { break; diff --git a/cmake/externals/polyvox/CMakeLists.txt b/cmake/externals/polyvox/CMakeLists.txt index 28aec6dab7..c28a8e1319 100644 --- a/cmake/externals/polyvox/CMakeLists.txt +++ b/cmake/externals/polyvox/CMakeLists.txt @@ -5,7 +5,7 @@ ExternalProject_Add( ${EXTERNAL_NAME} URL http://hifi-public.s3.amazonaws.com/dependencies/polyvox.zip URL_MD5 904b840328278c9b36fa7a14be730c34 - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= + CMAKE_ARGS -DENABLE_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX:PATH= BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build LOG_DOWNLOAD 1 LOG_CONFIGURE 1 diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index 146f9daca3..7d024e2fd3 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -99,6 +99,13 @@ var NUM_BUTTONS = 3; var screenSize = Controller.getViewportDimensions(); var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2; +Script.include(["../../libraries/toolBars.js"]); +const persistKey = "highfidelity.gun.toolbar.position"; +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); +toolBar.save = function () { + Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); +}; +var old = JSON.parse(Settings.getValue(persistKey) || '0'); var reticle = Overlays.addOverlay("image", { x: screenSize.x / 2 - (BUTTON_SIZE / 2), y: screenSize.y / 2 - (BUTTON_SIZE / 2), @@ -108,9 +115,9 @@ var reticle = Overlays.addOverlay("image", { alpha: 1 }); -var offButton = Overlays.addOverlay("image", { - x: startX, - y: screenSize.y - (BUTTON_SIZE + PADDING), +var offButton = toolBar.addOverlay("image", { + x: old ? old[0] : startX, + y: old ? old[1] : (screenSize.y - (BUTTON_SIZE + PADDING)), width: BUTTON_SIZE, height: BUTTON_SIZE, imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg", @@ -118,7 +125,7 @@ var offButton = Overlays.addOverlay("image", { }); startX += BUTTON_SIZE + PADDING; -var platformButton = Overlays.addOverlay("image", { +var platformButton = toolBar.addOverlay("image", { x: startX, y: screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, @@ -128,7 +135,7 @@ var platformButton = Overlays.addOverlay("image", { }); startX += BUTTON_SIZE + PADDING; -var gridButton = Overlays.addOverlay("image", { +var gridButton = toolBar.addOverlay("image", { x: startX, y: screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, @@ -493,10 +500,8 @@ function mousePressEvent(event) { } function scriptEnding() { - Overlays.deleteOverlay(reticle); - Overlays.deleteOverlay(offButton); - Overlays.deleteOverlay(platformButton); - Overlays.deleteOverlay(gridButton); + Overlays.deleteOverlay(reticle); + toolBar.cleanup(); Overlays.deleteOverlay(pointer[0]); Overlays.deleteOverlay(pointer[1]); Overlays.deleteOverlay(text); diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index 545830fe3e..601b5254df 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -19,3 +19,4 @@ Script.load("grab.js"); Script.load("pointer.js"); Script.load("directory.js"); Script.load("mouseLook.js"); +Script.load("dialTone.js"); diff --git a/examples/dialTone.js b/examples/dialTone.js index 135acb17f4..eb1fc5ec1c 100644 --- a/examples/dialTone.js +++ b/examples/dialTone.js @@ -3,6 +3,7 @@ // examples // // Created by Stephen Birarda on 06/08/15. +// Added disconnect HRS 6/11/15. // Copyright 2015 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -10,14 +11,26 @@ // // setup the local sound we're going to use -var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/short1.wav"); +var connectSound = SoundCache.getSound("file:///" + Paths.resources + "sounds/hello.wav"); +var disconnectSound = SoundCache.getSound("file:///" + Paths.resources + "sounds/goodbye.wav"); +var micMutedSound = SoundCache.getSound("file:///" + Paths.resources + "sounds/goodbye.wav"); // setup the options needed for that sound -var connectSoundOptions = { +var soundOptions = { localOnly: true -} +}; // play the sound locally once we get the first audio packet from a mixer Audio.receivedFirstPacket.connect(function(){ - Audio.playSound(connectSound, connectSoundOptions); + Audio.playSound(connectSound, soundOptions); +}); + +Audio.disconnected.connect(function(){ + Audio.playSound(disconnectSound, soundOptions); +}); + +AudioDevice.muteToggled.connect(function () { + if (AudioDevice.getMuted()) { + Audio.playSound(micMutedSound, soundOptions); + } }); diff --git a/examples/dice.js b/examples/dice.js index 515019e740..ac5d1b7426 100644 --- a/examples/dice.js +++ b/examples/dice.js @@ -3,6 +3,7 @@ // examples // // Created by Philip Rosedale on February 2, 2015 +// Persist toolbar by HRS 6/11/15. // Copyright 2015 High Fidelity, Inc. // // Press the dice button to throw some dice from the center of the screen. @@ -31,9 +32,16 @@ var screenSize = Controller.getViewportDimensions(); var BUTTON_SIZE = 32; var PADDING = 3; -var offButton = Overlays.addOverlay("image", { - x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING, - y: screenSize.y - (BUTTON_SIZE + PADDING), +Script.include(["libraries/toolBars.js"]); +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); +const persistKey = "highfidelity.dice.toolbar.position"; +toolBar.save = function () { + Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); +}; +var old = JSON.parse(Settings.getValue(persistKey) || '0'); +var offButton = toolBar.addOverlay("image", { + x: old ? old[0] : (screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING), + y: old ? old[1] : (screenSize.y - (BUTTON_SIZE + PADDING)), width: BUTTON_SIZE, height: BUTTON_SIZE, imageURL: HIFI_PUBLIC_BUCKET + "images/close.png", @@ -45,7 +53,7 @@ var offButton = Overlays.addOverlay("image", { alpha: 1 }); -var deleteButton = Overlays.addOverlay("image", { +var deleteButton = toolBar.addOverlay("image", { x: screenSize.x / 2 - BUTTON_SIZE, y: screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, @@ -59,7 +67,7 @@ var deleteButton = Overlays.addOverlay("image", { alpha: 1 }); -var diceButton = Overlays.addOverlay("image", { +var diceButton = toolBar.addOverlay("image", { x: screenSize.x / 2 + PADDING, y: screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, @@ -140,10 +148,8 @@ function mousePressEvent(event) { } function scriptEnding() { - Overlays.deleteOverlay(offButton); - Overlays.deleteOverlay(diceButton); - Overlays.deleteOverlay(deleteButton); + toolBar.cleanup(); } Controller.mousePressEvent.connect(mousePressEvent); -Script.scriptEnding.connect(scriptEnding); \ No newline at end of file +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/edit.js b/examples/edit.js index 6055400289..2974397cde 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -2,6 +2,7 @@ // examples // // Created by Brad Hefta-Gaub on 10/2/14. +// Persist toolbar by HRS 6/11/15. // Copyright 2014 High Fidelity, Inc. // // This script allows you to edit entities with a new UI/UX for mouse and trackpad based editing @@ -320,6 +321,7 @@ var toolBar = (function () { } } + const persistKey = "highfidelity.edit.toolbar.position"; that.move = function () { var newViewPort, toolsX, @@ -330,6 +332,15 @@ var toolBar = (function () { if (toolBar === undefined) { initialize(); + toolBar.save = function () { + Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); + }; + var old = JSON.parse(Settings.getValue(persistKey) || '0'); + if (old) { + windowDimensions = newViewPort; + toolBar.move(old[0], old[1]); + return; + } } else if (windowDimensions.x === newViewPort.x && windowDimensions.y === newViewPort.y) { return; diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index f029088b1a..27a2929d1c 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -354,7 +354,8 @@ var elZoneAtmosphereScatteringWavelengthsZ = document.getElementById("property-zone-atmosphere-scattering-wavelengths-z"); var elZoneAtmosphereHasStars = document.getElementById("property-zone-atmosphere-has-stars"); - var elPolyVoxSelections = document.querySelectorAll(".poly-vox-section"); + var elPolyVoxSections = document.querySelectorAll(".poly-vox-section"); + allSections.push(elPolyVoxSections); var elVoxelVolumeSizeX = document.getElementById("property-voxel-volume-size-x"); var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y"); var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z"); @@ -602,6 +603,10 @@ elParticleLocalGravity.value = properties.localGravity.toFixed(2); elParticleRadius.value = properties.particleRadius.toFixed(3); } else if (properties.type == "PolyVox") { + for (var i = 0; i < elPolyVoxSections.length; i++) { + elPolyVoxSections[i].style.display = 'block'; + } + elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2); elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2); elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2); @@ -1014,9 +1019,9 @@
Voxel Volume Size
-
X
-
Y
-
Z
+
X
+
Y
+
Z
Surface Extractor
diff --git a/examples/libraries/toolBars.js b/examples/libraries/toolBars.js index 670a69dec7..6e72795ea5 100644 --- a/examples/libraries/toolBars.js +++ b/examples/libraries/toolBars.js @@ -3,6 +3,7 @@ // examples // // Created by Clément Brisset on 5/7/14. +// Persistable drag position by HRS 6/11/15. // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -236,6 +237,7 @@ ToolBar = function(x, y, direction) { y: y - ToolBar.SPACING }); } + this.save(); } this.setAlpha = function(alpha, tool) { @@ -313,9 +315,8 @@ ToolBar = function(x, y, direction) { this.cleanup = function() { for(var tool in this.tools) { this.tools[tool].cleanup(); - delete this.tools[tool]; } - + if (this.back != null) { Overlays.deleteOverlay(this.back); this.back = null; @@ -327,7 +328,71 @@ ToolBar = function(x, y, direction) { this.width = 0; this.height = 0; } + + var that = this; + this.contains = function (xOrPoint, optionalY) { + var x = (optionalY === undefined) ? xOrPoint.x : xOrPoint, + y = (optionalY === undefined) ? xOrPoint.y : optionalY; + return (that.x <= x) && (x <= (that.x + that.width)) && + (that.y <= y) && (y <= (that.y + that.height)); + } + that.hover = function (enable) { + that.isHovering = enable; + if (that.back) { + if (enable) { + that.oldAlpha = Overlays.getProperty(that.back, 'backgroundAlpha'); + } + Overlays.editOverlay(this.back, { + visible: enable, + backgroundAlpha: enable ? 0.5 : that.oldAlpha + }); + } + }; + // These are currently only doing that which is necessary for toolbar hover and toolbar drag. + // They have not yet been extended to tool hover/click/release, etc. + this.mousePressEvent = function (event) { + if (!that.contains(event)) { + that.mightBeDragging = false; + return; + } + that.mightBeDragging = true; + that.dragOffsetX = that.x - event.x; + that.dragOffsetY = that.y - event.y; + }; + this.mouseMove = function (event) { + if (!that.mightBeDragging || !event.isLeftButton) { + that.mightBeDragging = false; + if (!that.contains(event)) { + if (that.isHovering) { + that.hover(false); + } + return; + } + if (!that.isHovering) { + that.hover(true); + } + return; + } + that.move(that.dragOffsetX + event.x, that.dragOffsetY + event.y); + }; + Controller.mousePressEvent.connect(this.mousePressEvent); + Controller.mouseMoveEvent.connect(this.mouseMove); + // Called on move. A different approach would be to have all this on the prototype, + // and let apps extend where needed. Ex. app defines its toolbar.move() to call this.__proto__.move and then save. + this.save = function () { }; + // This compatability hack breaks the model, but makes converting existing scripts easier: + this.addOverlay = function (ignored, oldSchoolProperties) { + var properties = JSON.parse(JSON.stringify(oldSchoolProperties)); // a copy + if (that.numberOfTools() === 0) { + that.move(properties.x, properties.y); + } + delete properties.x; + delete properties.y; + var index = that.addTool(properties); + var id = that.tools[index].overlay(); + return id; + } } ToolBar.SPACING = 4; ToolBar.VERTICAL = 0; -ToolBar.HORIZONTAL = 1; \ No newline at end of file +ToolBar.HORIZONTAL = 1; diff --git a/examples/paint.js b/examples/paint.js index 8bba4e2571..c0cc93afc7 100644 --- a/examples/paint.js +++ b/examples/paint.js @@ -12,8 +12,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // Script.include('lineRider.js') -var MAX_POINTS_PER_LINE = 80; - +var MAX_POINTS_PER_LINE = 30; +var LINE_LIFETIME = 60 * 5 //5 minute lifetime var colorPalette = [{ red: 236, @@ -120,10 +120,12 @@ function MousePaint() { y: 10, z: 10 }, - lineWidth: LINE_WIDTH + lineWidth: LINE_WIDTH, + lifetime: LINE_LIFETIME }); points = []; if (point) { + points.push(point); path.push(point); } @@ -133,22 +135,22 @@ function MousePaint() { function mouseMoveEvent(event) { + if (!isDrawing) { + return; + } var pickRay = Camera.computePickRay(event.x, event.y); var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DRAWING_DISTANCE); var point = Vec3.sum(Camera.getPosition(), addVector); + points.push(point); + path.push(point); Entities.editEntity(line, { linePoints: points }); Entities.editEntity(brush, { position: point }); - if (!isDrawing) { - return; - } - points.push(point); - path.push(point); if (points.length === MAX_POINTS_PER_LINE) { //We need to start a new line! @@ -253,7 +255,6 @@ function HydraPaint() { var maxLineWidth = 10; var currentLineWidth = minLineWidth; var MIN_PAINT_TRIGGER_THRESHOLD = .01; - var LINE_LIFETIME = 20; var COLOR_CHANGE_TIME_FACTOR = 0.1; var RIGHT_BUTTON_1 = 7 @@ -330,7 +331,7 @@ function HydraPaint() { z: 10 }, lineWidth: 5, - // lifetime: LINE_LIFETIME + lifetime: LINE_LIFETIME }); this.points = []; if (point) { diff --git a/examples/pointer.js b/examples/pointer.js index cca46709ee..ea6b0c233f 100644 --- a/examples/pointer.js +++ b/examples/pointer.js @@ -3,6 +3,7 @@ // // Created by Seth Alves on May 15th // Modified by Eric Levin on June 4 +// Persist toolbar by HRS 6/11/15. // Copyright 2015 High Fidelity, Inc. // // Provides a pointer with option to draw on surfaces @@ -31,9 +32,16 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; var screenSize = Controller.getViewportDimensions(); var userCanPoint = false; -var pointerButton = Overlays.addOverlay("image", { - x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING, - y: screenSize.y - (BUTTON_SIZE + PADDING), +Script.include(["libraries/toolBars.js"]); +const persistKey = "highfidelity.pointer.toolbar.position"; +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); +toolBar.save = function () { + Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); +}; +var old = JSON.parse(Settings.getValue(persistKey) || '0'); +var pointerButton = toolBar.addOverlay("image", { + x: old ? old[0] : screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING, + y: old ? old[1] : screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, height: BUTTON_SIZE, imageURL: HIFI_PUBLIC_BUCKET + "images/laser.png", @@ -150,4 +158,4 @@ Script.scriptEnding.connect(cleanup); Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mousePressEvent.connect(mousePressEvent); -Controller.mouseReleaseEvent.connect(mouseReleaseEvent); \ No newline at end of file +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 30dab3644a..00f6d4c3b2 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -140,7 +140,7 @@ target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES}) # link required hifi libraries link_hifi_libraries(shared octree environment gpu model render fbx networking entities avatars audio audio-client animation script-engine physics - render-utils entities-renderer ui) + render-utils entities-renderer ui auto-updater) add_dependency_external_projects(sdl2) diff --git a/interface/resources/images/reticleLink.png b/interface/resources/images/reticleLink.png new file mode 100644 index 0000000000..b36ee79d00 Binary files /dev/null and b/interface/resources/images/reticleLink.png differ diff --git a/interface/resources/images/reticleWhite.png b/interface/resources/images/reticleWhite.png new file mode 100644 index 0000000000..4c35a186b2 Binary files /dev/null and b/interface/resources/images/reticleWhite.png differ diff --git a/interface/resources/qml/UpdateDialog.qml b/interface/resources/qml/UpdateDialog.qml new file mode 100644 index 0000000000..e5216ff619 --- /dev/null +++ b/interface/resources/qml/UpdateDialog.qml @@ -0,0 +1,172 @@ +import Hifi 1.0 +import QtQuick 2.3 +import QtQuick.Controls.Styles 1.3 +import QtGraphicalEffects 1.0 +import "controls" +import "styles" + +DialogContainer { + HifiConstants { id: hifi } + id: root + objectName: "UpdateDialog" + implicitWidth: updateDialog.width + implicitHeight: updateDialog.height + x: parent ? parent.width / 2 - width / 2 : 0 + y: parent ? parent.height / 2 - height / 2 : 0 + + UpdateDialog { + id: updateDialog + + implicitWidth: backgroundRectangle.width + implicitHeight: backgroundRectangle.height + + readonly property int inputWidth: 500 + readonly property int inputHeight: 60 + readonly property int borderWidth: 30 + readonly property int closeMargin: 16 + readonly property int inputSpacing: 16 + readonly property int buttonWidth: 150 + readonly property int buttonHeight: 50 + readonly property int buttonRadius: 15 + + signal triggerBuildDownload + signal closeUpdateDialog + + Column { + id: mainContent + width: updateDialog.inputWidth + spacing: updateDialog.inputSpacing + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + + Rectangle { + id: backgroundRectangle + color: "#2c86b1" + opacity: 0.85 + radius: updateDialog.closeMargin * 2 + + width: updateDialog.inputWidth + updateDialog.borderWidth * 2 + height: updateDialog.inputHeight * 6 + updateDialog.closeMargin * 2 + + Rectangle { + id: dialogTitle + width: updateDialog.inputWidth + height: updateDialog.inputHeight + radius: height / 2 + color: "#ebebeb" + + anchors { + top: parent.top + topMargin: updateDialog.inputSpacing + horizontalCenter: parent.horizontalCenter + } + + Text { + id: updateAvailableText + text: "Update Available" + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: updateDialog.inputSpacing + } + } + + Text { + text: updateDialog.updateAvailableDetails + font.pixelSize: 14 + color: hifi.colors.text + anchors { + verticalCenter: parent.verticalCenter + left: updateAvailableText.right + leftMargin: 13 + } + } + } + + Flickable { + id: scrollArea + anchors { + top: dialogTitle.bottom + } + contentWidth: updateDialog.inputWidth + contentHeight: backgroundRectangle.height - (dialogTitle.height * 2.5) + width: updateDialog.inputWidth + height: backgroundRectangle.height - (dialogTitle.height * 2.5) + flickableDirection: Flickable.VerticalFlick + clip: true + + TextEdit { + id: releaseNotes + wrapMode: TextEdit.Wrap + width: parent.width + readOnly: true + text: updateDialog.releaseNotes + font.pixelSize: 14 + color: hifi.colors.text + anchors { + left: parent.left + leftMargin: updateDialog.borderWidth + } + } + } + + Rectangle { + id: downloadButton + width: updateDialog.buttonWidth + height: updateDialog.buttonHeight + radius: updateDialog.buttonRadius + color: "green" + anchors { + top: scrollArea.bottom + topMargin: 10 + right: backgroundRectangle.right + rightMargin: 15 + } + Text { + text: "Upgrade" + anchors { + verticalCenter: parent.verticalCenter + horizontalCenter: parent.horizontalCenter + } + } + MouseArea { + id: downloadButtonAction + anchors.fill: parent + onClicked: updateDialog.triggerUpgrade() + cursorShape: "PointingHandCursor" + } + } + + Rectangle { + id: cancelButton + width: updateDialog.buttonWidth + height: updateDialog.buttonHeight + radius: updateDialog.buttonRadius + color: "red" + anchors { + top: scrollArea.bottom + topMargin: 10 + right: downloadButton.left + rightMargin: 15 + } + + Text { + text: "Cancel" + anchors { + verticalCenter: parent.verticalCenter + horizontalCenter: parent.horizontalCenter + } + } + MouseArea { + id: cancelButtonAction + anchors.fill: parent + onClicked: updateDialog.closeDialog() + cursorShape: "PointingHandCursor" + } + } + } + } + } +} \ No newline at end of file diff --git a/interface/resources/sounds/goodbye.wav b/interface/resources/sounds/goodbye.wav new file mode 100644 index 0000000000..bd6c51a5c0 Binary files /dev/null and b/interface/resources/sounds/goodbye.wav differ diff --git a/interface/resources/sounds/hello.wav b/interface/resources/sounds/hello.wav new file mode 100644 index 0000000000..6269dab5db Binary files /dev/null and b/interface/resources/sounds/hello.wav differ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a1ab6d35ae..651e343087 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -57,8 +57,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -146,6 +148,7 @@ #include "ui/StandAloneJSConsole.h" #include "ui/Stats.h" #include "ui/AddressBarDialog.h" +#include "ui/UpdateDialog.h" // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU @@ -294,6 +297,7 @@ bool setupEssentials(int& argc, char** argv) { auto discoverabilityManager = DependencyManager::set(); auto sceneScriptingInterface = DependencyManager::set(); auto offscreenUi = DependencyManager::set(); + auto autoUpdater = DependencyManager::set(); auto pathUtils = DependencyManager::set(); auto actionFactory = DependencyManager::set(); @@ -423,6 +427,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled); connect(audioIO.data(), &AudioClient::receivedFirstPacket, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::receivedFirstPacket); + connect(audioIO.data(), &AudioClient::disconnected, + &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::disconnected); audioThread->start(); @@ -559,8 +565,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // allow you to move an entity around in your hand _entityEditSender.setPacketsPerSecond(3000); // super high!! - checkVersion(); - _overlays.init(); // do this before scripts load _runningScriptsWidget->setRunningScripts(getRunningScripts()); @@ -632,9 +636,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : ddeTracker->init(); connect(ddeTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled); #endif + + auto applicationUpdater = DependencyManager::get(); + connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog); + applicationUpdater->checkForUpdate(); } - void Application::aboutToQuit() { emit beforeAboutToQuit(); @@ -824,6 +831,7 @@ void Application::initializeUi() { LoginDialog::registerType(); MessageDialog::registerType(); VrMenu::registerType(); + UpdateDialog::registerType(); auto offscreenUi = DependencyManager::get(); offscreenUi->create(_glWidget->context()->contextHandle()); @@ -955,12 +963,15 @@ void Application::paintGL() { displaySide(&renderArgs, _myCamera); glPopMatrix(); + renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { _rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos())); } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { renderRearViewMirror(&renderArgs, _mirrorViewRect); } + renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE; + auto finalFbo = DependencyManager::get()->render(&renderArgs); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); @@ -1241,9 +1252,20 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; - case Qt::Key_Apostrophe: - resetSensors(); + case Qt::Key_Apostrophe: { + if (isMeta) { + auto cursor = Cursor::Manager::instance().getCursor(); + auto curIcon = cursor->getIcon(); + if (curIcon == Cursor::Icon::DEFAULT) { + cursor->setIcon(Cursor::Icon::LINK); + } else { + cursor->setIcon(Cursor::Icon::DEFAULT); + } + } else { + resetSensors(); + } break; + } case Qt::Key_A: if (isShifted) { @@ -1367,12 +1389,27 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_Slash: Menu::getInstance()->triggerOption(MenuOption::Stats); break; - case Qt::Key_Plus: - _myAvatar->increaseSize(); + + case Qt::Key_Plus: { + if (isMeta && event->modifiers().testFlag(Qt::KeypadModifier)) { + auto& cursorManager = Cursor::Manager::instance(); + cursorManager.setScale(cursorManager.getScale() * 1.1f); + } else { + _myAvatar->increaseSize(); + } break; - case Qt::Key_Minus: - _myAvatar->decreaseSize(); + } + + case Qt::Key_Minus: { + if (isMeta && event->modifiers().testFlag(Qt::KeypadModifier)) { + auto& cursorManager = Cursor::Manager::instance(); + cursorManager.setScale(cursorManager.getScale() / 1.1f); + } else { + _myAvatar->decreaseSize(); + } break; + } + case Qt::Key_Equal: _myAvatar->resetSize(); break; @@ -3231,6 +3268,9 @@ namespace render { template <> const Item::Bound payloadGetBound(const BackgroundRenderData::Pointer& stuff) { return Item::Bound(); } template <> void payloadRender(const BackgroundRenderData::Pointer& background, RenderArgs* args) { + Q_ASSERT(args->_batch); + gpu::Batch& batch = *args->_batch; + // Background rendering decision auto skyStage = DependencyManager::get()->getSkyStage(); auto skybox = model::SkyboxPointer(); @@ -3298,7 +3338,8 @@ namespace render { PerformanceTimer perfTimer("atmosphere"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... atmosphere..."); - background->_environment->renderAtmospheres(*(args->_viewFrustum)); + + background->_environment->renderAtmospheres(batch, *(args->_viewFrustum)); } } @@ -3307,13 +3348,13 @@ namespace render { skybox = skyStage->getSkybox(); if (skybox) { - gpu::Batch batch; model::Skybox::render(batch, *(Application::getInstance()->getDisplayViewFrustum()), *skybox); - - gpu::GLBackend::renderBatch(batch, true); - glUseProgram(0); } } + // FIX ME - If I don't call this renderBatch() here, then the atmosphere and skybox don't render, but it + // seems like these payloadRender() methods shouldn't be doing this. We need to investigate why the engine + // isn't rendering our batch + gpu::GLBackend::renderBatch(batch, true); } } @@ -3419,7 +3460,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se "Application::displaySide() ... entities..."); RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE; - RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE; if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) { renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_HULLS); @@ -3428,10 +3468,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP); } - if (theCamera.getMode() == CAMERA_MODE_MIRROR) { - renderMode = RenderArgs::MIRROR_RENDER_MODE; - } - renderArgs->_renderMode = renderMode; renderArgs->_debugFlags = renderDebugFlags; _entities.render(renderArgs); } @@ -4503,72 +4539,6 @@ void Application::toggleLogDialog() { } } -void Application::checkVersion() { - QNetworkRequest latestVersionRequest((QUrl(CHECK_VERSION_URL))); - latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - latestVersionRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); - QNetworkReply* reply = NetworkAccessManager::getInstance().get(latestVersionRequest); - connect(reply, SIGNAL(finished()), SLOT(parseVersionXml())); -} - -void Application::parseVersionXml() { - - #ifdef Q_OS_WIN32 - QString operatingSystem("win"); - #endif - - #ifdef Q_OS_MAC - QString operatingSystem("mac"); - #endif - - #ifdef Q_OS_LINUX - QString operatingSystem("ubuntu"); - #endif - - QString latestVersion; - QUrl downloadUrl; - QString releaseNotes("Unavailable"); - QNetworkReply* sender = qobject_cast(QObject::sender()); - - QXmlStreamReader xml(sender); - - while (!xml.atEnd() && !xml.hasError()) { - if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == operatingSystem) { - while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == operatingSystem)) { - if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "version") { - xml.readNext(); - latestVersion = xml.text().toString(); - } - if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "url") { - xml.readNext(); - downloadUrl = QUrl(xml.text().toString()); - } - xml.readNext(); - } - } - xml.readNext(); - } - - if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) { - new UpdateDialog(_glWidget, releaseNotes, latestVersion, downloadUrl); - } - sender->deleteLater(); -} - -bool Application::shouldSkipVersion(QString latestVersion) { - QFile skipFile(SKIP_FILENAME); - skipFile.open(QIODevice::ReadWrite); - QString skipVersion(skipFile.readAll()); - return (skipVersion == latestVersion || applicationVersion() == "dev"); -} - -void Application::skipVersion(QString latestVersion) { - QFile skipFile(SKIP_FILENAME); - skipFile.open(QIODevice::WriteOnly | QIODevice::Truncate); - skipFile.seek(0); - skipFile.write(latestVersion.toStdString().c_str()); -} - void Application::takeSnapshot() { QMediaPlayer* player = new QMediaPlayer(); QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav"); diff --git a/interface/src/Application.h b/interface/src/Application.h index 68bf60781a..c9e5bea76e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -66,7 +66,6 @@ #include "ui/SnapshotShareDialog.h" #include "ui/LodToolsDialog.h" #include "ui/LogDialog.h" -#include "ui/UpdateDialog.h" #include "ui/overlays/Overlays.h" #include "ui/ApplicationOverlay.h" #include "ui/RunningScriptsWidget.h" @@ -312,8 +311,6 @@ public: NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; } - void skipVersion(QString latestVersion); - QStringList getRunningScripts() { return _scriptEnginesHash.keys(); } ScriptEngine* getScriptEngine(QString scriptHash) { return _scriptEnginesHash.contains(scriptHash) ? _scriptEnginesHash[scriptHash] : NULL; } @@ -460,8 +457,6 @@ private slots: void restoreMirrorView(); void shrinkMirrorView(); - void parseVersionXml(); - void manageRunningScriptsWidgetVisibility(bool shown); void runTests(); @@ -627,9 +622,6 @@ private: FileLogger* _logger; - void checkVersion(); - void displayUpdateDialog(); - bool shouldSkipVersion(QString latestVersion); void takeSnapshot(); TouchEvent _lastTouchEvent; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6242318170..216f733709 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -90,6 +90,30 @@ Menu::Menu() { addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J, qApp, SLOT(toggleRunningScriptsWidget())); + auto addressManager = DependencyManager::get(); + + addDisabledActionAndSeparator(fileMenu, "History"); + + QAction* backAction = addActionToQMenuAndActionHash(fileMenu, + MenuOption::Back, + 0, + addressManager.data(), + SLOT(goBack())); + + QAction* forwardAction = addActionToQMenuAndActionHash(fileMenu, + MenuOption::Forward, + 0, + addressManager.data(), + SLOT(goForward())); + + // connect to the AddressManager signal to enable and disable the back and forward menu items + connect(addressManager.data(), &AddressManager::goBackPossible, backAction, &QAction::setEnabled); + connect(addressManager.data(), &AddressManager::goForwardPossible, forwardAction, &QAction::setEnabled); + + // set the two actions to start disabled since the stacks are clear on startup + backAction->setDisabled(true); + forwardAction->setDisabled(true); + addDisabledActionAndSeparator(fileMenu, "Location"); qApp->getBookmarks()->setupMenus(this, fileMenu); @@ -98,7 +122,6 @@ Menu::Menu() { Qt::CTRL | Qt::Key_L, dialogsManager.data(), SLOT(toggleAddressBar())); - auto addressManager = DependencyManager::get(); addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyAddress, 0, addressManager.data(), SLOT(copyAddress())); addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0, @@ -407,7 +430,7 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::MuteFaceTracking, Qt::CTRL | Qt::SHIFT | Qt::Key_F, true, // DDE face tracking is on by default qApp, SLOT(toggleFaceTrackerMute())); - addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, true); + addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, false); #endif auto avatarManager = DependencyManager::get(); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 6107744abc..6b892ebc3c 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -149,6 +149,7 @@ namespace MenuOption { const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams"; const QString AutoMuteAudio = "Auto Mute Microphone"; const QString AvatarReceiveStats = "Show Receive Stats"; + const QString Back = "Back"; const QString BandwidthDetails = "Bandwidth Details"; const QString BinaryEyelidControl = "Binary Eyelid Control"; const QString BlueSpeechSphere = "Blue Sphere While Speaking"; @@ -194,12 +195,12 @@ namespace MenuOption { const QString Faceshift = "Faceshift"; const QString FilterSixense = "Smooth Sixense Movement"; const QString FirstPerson = "First Person"; + const QString Forward = "Forward"; const QString FrameTimer = "Show Timer"; const QString Fullscreen = "Fullscreen"; const QString FullscreenMirror = "Fullscreen Mirror"; const QString GlowWhenSpeaking = "Glow When Speaking"; const QString NamesAboveHeads = "Names Above Heads"; - const QString GoToUser = "Go To User"; const QString HMDTools = "HMD Tools"; const QString IncreaseAvatarSize = "Increase Avatar Size"; const QString KeyboardMotorControl = "Enable Keyboard Motor Control"; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index dbe7d52a3f..71f4621205 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -328,6 +328,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo if (postLighting && glm::distance(DependencyManager::get()->getMyAvatar()->getPosition(), _position) < 10.0f) { auto geometryCache = DependencyManager::get(); + auto deferredLighting = DependencyManager::get(); // render pointing lasers glm::vec3 laserColor = glm::vec3(1.0f, 0.0f, 1.0f); @@ -354,6 +355,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo pointerTransform.setTranslation(position); pointerTransform.setRotation(rotation); batch->setModelTransform(pointerTransform); + deferredLighting->bindSimpleProgram(*batch); geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor); } } @@ -376,6 +378,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo pointerTransform.setTranslation(position); pointerTransform.setRotation(rotation); batch->setModelTransform(pointerTransform); + deferredLighting->bindSimpleProgram(*batch); geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor); } } @@ -462,7 +465,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo Transform transform; transform.setTranslation(position); batch->setModelTransform(transform); - DependencyManager::get()->renderSphere(*batch, LOOK_AT_INDICATOR_RADIUS, 15, 15, LOOK_AT_INDICATOR_COLOR); + DependencyManager::get()->renderSolidSphere(*batch, LOOK_AT_INDICATOR_RADIUS + , 15, 15, LOOK_AT_INDICATOR_COLOR); } } @@ -494,6 +498,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo _voiceSphereID = DependencyManager::get()->allocateID(); } + DependencyManager::get()->bindSimpleProgram(*batch); DependencyManager::get()->renderSphere(*batch, sphereRadius, 15, 15, glm::vec4(SPHERE_COLOR[0], SPHERE_COLOR[1], SPHERE_COLOR[2], 1.0f - angle / MAX_SPHERE_ANGLE), true, _voiceSphereID); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index bc8047fc98..0c1783f8ac 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -143,7 +143,6 @@ ApplicationOverlay::ApplicationOverlay() : _alpha(1.0f), _oculusUIRadius(1.0f), _trailingAudioLoudness(0.0f), - _crosshairTexture(0), _previousBorderWidth(-1), _previousBorderHeight(-1), _previousMagnifierBottomLeft(), @@ -591,8 +590,8 @@ void ApplicationOverlay::renderPointers() { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); + //glActiveTexture(GL_TEXTURE0); + //bindCursorTexture(); if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) { //If we are in oculus, render reticle later @@ -637,8 +636,8 @@ void ApplicationOverlay::renderPointers() { _magActive[MOUSE] = false; renderControllerPointers(); } - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); + //glBindTexture(GL_TEXTURE_2D, 0); + //glDisable(GL_TEXTURE_2D); } void ApplicationOverlay::renderControllerPointers() { diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index ee82c9274a..eb397fe3c6 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -96,7 +96,6 @@ private: }; float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; - void renderReticle(glm::quat orientation, float alpha); void renderPointers();; void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder); @@ -108,6 +107,7 @@ private: void renderCameraToggle(); void renderStatsAndLogs(); void renderDomainConnectionStatusBorder(); + void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0); TexturedHemisphere _overlays; @@ -125,9 +125,12 @@ private: float _alpha = 1.0f; float _oculusUIRadius; float _trailingAudioLoudness; + + gpu::TexturePointer _crosshairTexture; - gpu::TexturePointer _crosshairTexture; + QMap _cursors; + GLuint _newUiTexture{ 0 }; int _reticleQuad; diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 1170e3c3a6..81c7cd2770 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -31,6 +31,7 @@ #include "OctreeStatsDialog.h" #include "PreferencesDialog.h" #include "ScriptEditorWindow.h" +#include "UpdateDialog.h" void DialogsManager::toggleAddressBar() { @@ -50,6 +51,10 @@ void DialogsManager::showLoginDialog() { LoginDialog::show(); } +void DialogsManager::showUpdateDialog() { + UpdateDialog::show(); +} + void DialogsManager::octreeStatsDetails() { if (!_octreeStatsDialog) { _octreeStatsDialog = new OctreeStatsDialog(qApp->getWindow(), qApp->getOcteeSceneStats()); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index fc2dad072b..7eb716b73c 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -35,6 +35,7 @@ class ScriptEditorWindow; class QMessageBox; class AvatarAppearanceDialog; class DomainConnectionDialog; +class UpdateDialog; class DialogsManager : public QObject, public Dependency { Q_OBJECT @@ -64,6 +65,9 @@ public slots: void showIRCLink(); void changeAvatarAppearance(); void showDomainConnectionDialog(); + + // Application Update + void showUpdateDialog(); private slots: void toggleToolWindow(); @@ -101,6 +105,7 @@ private: QPointer _scriptEditor; QPointer _avatarAppearanceDialog; QPointer _domainConnectionDialog; + QPointer _updateDialog; }; #endif // hifi_DialogsManager_h diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 32df75c46d..8359480b03 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -161,6 +161,10 @@ void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int heigh ((rgba >> 8) & 0xff) / 255.0f, (rgba & 0xff) / 255.0f); + // FIX ME: is this correct? It seems to work to fix textures bleeding into us... + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + DependencyManager::get()->renderQuad(x, y, width, height, color); } @@ -460,7 +464,7 @@ void Stats::display( verticalOffset = STATS_PELS_INITIALOFFSET; horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3; - lines = _expanded ? 10 : 2; + lines = _expanded ? 10 : 3; drawBackground(backgroundColor, horizontalOffset, 0, canvasSize.x - horizontalOffset, (lines + 1) * STATS_PELS_PER_LINE); @@ -608,12 +612,10 @@ void Stats::display( } // LOD Details - if (_expanded) { - octreeStats.str(""); - QString displayLODDetails = DependencyManager::get()->getLODFeedbackText(); - octreeStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed()); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - } + octreeStats.str(""); + QString displayLODDetails = DependencyManager::get()->getLODFeedbackText(); + octreeStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed()); + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); } diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 40b32d66e3..69dfc343d9 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -2,54 +2,50 @@ // UpdateDialog.cpp // interface/src/ui // -// Copyright 2014 High Fidelity, Inc. +// Created by Leonardo Murillo on 6/3/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 - -#include "ui_updateDialog.h" - -#include "Application.h" #include "UpdateDialog.h" +#include -UpdateDialog::UpdateDialog(QWidget *parent, const QString& releaseNotes, const QString& latestVersion, const QUrl& downloadURL) : - QDialog(parent), - _latestVersion(latestVersion), - _downloadUrl(downloadURL) +#include "DependencyManager.h" + +HIFI_QML_DEF(UpdateDialog) + +UpdateDialog::UpdateDialog(QQuickItem* parent) : + OffscreenQmlDialog(parent) { - Ui::Dialog dialogUI; - dialogUI.setupUi(this); - - QString updateRequired = QString("You are currently running build %1, the latest build released is %2." - "\n\nPlease download and install the most recent release to access the latest features and bug fixes.") - .arg(Application::getInstance()->applicationVersion(), latestVersion); - - setAttribute(Qt::WA_DeleteOnClose); - - QPushButton* downloadButton = findChild("downloadButton"); - QPushButton* skipButton = findChild("skipButton"); - QPushButton* closeButton = findChild("closeButton"); - QLabel* updateContent = findChild("updateContent"); - - updateContent->setText(updateRequired); - - connect(downloadButton, SIGNAL(released()), this, SLOT(handleDownload())); - connect(skipButton, SIGNAL(released()), this, SLOT(handleSkip())); - connect(closeButton, SIGNAL(released()), this, SLOT(close())); - - QMetaObject::invokeMethod(this, "show", Qt::QueuedConnection); + auto applicationUpdater = DependencyManager::get(); + int currentVersion = QCoreApplication::applicationVersion().toInt(); + int latestVersion = applicationUpdater.data()->getBuildData().lastKey(); + int versionsBehind = latestVersion - currentVersion; + _updateAvailableDetails = "v" + QString::number(latestVersion) + " released on " + applicationUpdater.data()->getBuildData()[latestVersion]["releaseTime"]; + _updateAvailableDetails += "\nYou are " + QString::number(versionsBehind) + " versions behind"; + _releaseNotes = applicationUpdater.data()->getBuildData()[latestVersion]["releaseNotes"]; } -void UpdateDialog::handleDownload() { - QDesktopServices::openUrl(_downloadUrl); - Application::getInstance()->quit(); +const QString& UpdateDialog::updateAvailableDetails() const { + return _updateAvailableDetails; } -void UpdateDialog::handleSkip() { - Application::getInstance()->skipVersion(_latestVersion); - this->close(); +const QString& UpdateDialog::releaseNotes() const { + return _releaseNotes; } + +void UpdateDialog::closeDialog() { + hide(); +} + +void UpdateDialog::hide() { + ((QQuickItem*)parent())->setEnabled(false); +} + +void UpdateDialog::triggerUpgrade() { + auto applicationUpdater = DependencyManager::get(); + applicationUpdater.data()->performAutoUpdate(applicationUpdater.data()->getBuildData().lastKey()); +} \ No newline at end of file diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index 15a97bf024..84d390c942 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -2,30 +2,42 @@ // UpdateDialog.h // interface/src/ui // -// Copyright 2014 High Fidelity, Inc. +// Created by Leonardo Murillo on 6/3/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 // +#pragma once #ifndef hifi_UpdateDialog_h #define hifi_UpdateDialog_h -#include +#include -class UpdateDialog : public QDialog { +#include + +class UpdateDialog : public OffscreenQmlDialog { Q_OBJECT + HIFI_QML_DECL + + Q_PROPERTY(QString updateAvailableDetails READ updateAvailableDetails) + Q_PROPERTY(QString releaseNotes READ releaseNotes) public: - UpdateDialog(QWidget* parent, const QString& releaseNotes, const QString& latestVersion, const QUrl& downloadURL); + UpdateDialog(QQuickItem* parent = nullptr); + const QString& updateAvailableDetails() const; + const QString& releaseNotes() const; private: - QString _latestVersion; - QUrl _downloadUrl; - -private slots: - void handleDownload(); - void handleSkip(); + QString _updateAvailableDetails; + QString _releaseNotes; + +protected: + void hide(); + Q_INVOKABLE void triggerUpgrade(); + Q_INVOKABLE void closeDialog(); + }; #endif // hifi_UpdateDialog_h diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 3e3e823737..e7b043f44f 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -43,72 +43,90 @@ void BillboardOverlay::render(RenderArgs* args) { return; } - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.5f); + glm::quat rotation; + if (_isFacingAvatar) { + // rotate about vertical to face the camera + rotation = Application::getInstance()->getCamera()->getRotation(); + rotation *= glm::angleAxis(glm::pi(), glm::vec3(0.0f, 1.0f, 0.0f)); + rotation *= getRotation(); + } else { + rotation = getRotation(); + } - glEnable(GL_TEXTURE_2D); - glDisable(GL_LIGHTING); + float imageWidth = _texture->getWidth(); + float imageHeight = _texture->getHeight(); - glBindTexture(GL_TEXTURE_2D, _texture->getID()); + QRect fromImage; + if (_fromImage.isNull()) { + fromImage.setX(0); + fromImage.setY(0); + fromImage.setWidth(imageWidth); + fromImage.setHeight(imageHeight); + } else { + float scaleX = imageWidth / _texture->getOriginalWidth(); + float scaleY = imageHeight / _texture->getOriginalHeight(); - glPushMatrix(); { - glTranslatef(_position.x, _position.y, _position.z); - glm::quat rotation; - if (_isFacingAvatar) { - // rotate about vertical to face the camera - rotation = Application::getInstance()->getCamera()->getRotation(); - rotation *= glm::angleAxis(glm::pi(), glm::vec3(0.0f, 1.0f, 0.0f)); - rotation *= getRotation(); - } else { - rotation = getRotation(); - } - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glScalef(_scale, _scale, _scale); + fromImage.setX(scaleX * _fromImage.x()); + fromImage.setY(scaleY * _fromImage.y()); + fromImage.setWidth(scaleX * _fromImage.width()); + fromImage.setHeight(scaleY * _fromImage.height()); + } - const float MAX_COLOR = 255.0f; - xColor color = getColor(); - float alpha = getAlpha(); + float maxSize = glm::max(fromImage.width(), fromImage.height()); + float x = fromImage.width() / (2.0f * maxSize); + float y = -fromImage.height() / (2.0f * maxSize); - float imageWidth = _texture->getWidth(); - float imageHeight = _texture->getHeight(); + glm::vec2 topLeft(-x, -y); + glm::vec2 bottomRight(x, y); + glm::vec2 texCoordTopLeft(fromImage.x() / imageWidth, fromImage.y() / imageHeight); + glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width()) / imageWidth, + (fromImage.y() + fromImage.height()) / imageHeight); - QRect fromImage; - if (_fromImage.isNull()) { - fromImage.setX(0); - fromImage.setY(0); - fromImage.setWidth(imageWidth); - fromImage.setHeight(imageHeight); - } else { - float scaleX = imageWidth / _texture->getOriginalWidth(); - float scaleY = imageHeight / _texture->getOriginalHeight(); + const float MAX_COLOR = 255.0f; + xColor color = getColor(); + float alpha = getAlpha(); - fromImage.setX(scaleX * _fromImage.x()); - fromImage.setY(scaleY * _fromImage.y()); - fromImage.setWidth(scaleX * _fromImage.width()); - fromImage.setHeight(scaleY * _fromImage.height()); - } + auto batch = args->_batch; - float maxSize = glm::max(fromImage.width(), fromImage.height()); - float x = fromImage.width() / (2.0f * maxSize); - float y = -fromImage.height() / (2.0f * maxSize); + if (batch) { + Transform transform; + transform.setTranslation(_position); + transform.setRotation(rotation); + transform.setScale(_scale); - glm::vec2 topLeft(-x, -y); - glm::vec2 bottomRight(x, y); - glm::vec2 texCoordTopLeft(fromImage.x() / imageWidth, fromImage.y() / imageHeight); - glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width()) / imageWidth, - (fromImage.y() + fromImage.height()) / imageHeight); - - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + batch->setModelTransform(transform); + batch->setUniformTexture(0, _texture->getGPUTexture()); + + DependencyManager::get()->renderQuad(*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)); + + batch->setUniformTexture(0, args->_whiteTexture); // restore default white color after me + } else { + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.5f); - } glPopMatrix(); + glEnable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); - glDisable(GL_TEXTURE_2D); - glEnable(GL_LIGHTING); - glDisable(GL_ALPHA_TEST); + glBindTexture(GL_TEXTURE_2D, _texture->getID()); - glBindTexture(GL_TEXTURE_2D, 0); + glPushMatrix(); { + glTranslatef(_position.x, _position.y, _position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glScalef(_scale, _scale, _scale); + + DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)); + + } glPopMatrix(); + + glDisable(GL_TEXTURE_2D); + glEnable(GL_LIGHTING); + glDisable(GL_ALPHA_TEST); + + glBindTexture(GL_TEXTURE_2D, 0); + } } void BillboardOverlay::setProperties(const QScriptValue &properties) { diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 6fc9fe6e27..37b30a5bcb 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -35,13 +35,6 @@ void Cube3DOverlay::render(RenderArgs* args) { return; // do nothing if we're not visible } - - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } - float alpha = getAlpha(); xColor color = getColor(); const float MAX_COLOR = 255.0f; @@ -49,91 +42,161 @@ void Cube3DOverlay::render(RenderArgs* args) { //glDisable(GL_LIGHTING); - // TODO: handle registration point?? + // TODO: handle registration point?? glm::vec3 position = getPosition(); glm::vec3 center = getCenter(); glm::vec3 dimensions = getDimensions(); glm::quat rotation = getRotation(); - - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glPushMatrix(); - glm::vec3 positionToCenter = center - position; - glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); - if (_isSolid) { - if (_borderSize > 0) { - // Draw a cube at a larger size behind the main cube, creating - // a border effect. - // Disable writing to the depth mask so that the "border" cube will not - // occlude the main cube. This means the border could be covered by - // overlays that are further back and drawn later, but this is good - // enough for the use-case. - glDepthMask(GL_FALSE); - glPushMatrix(); - glScalef(dimensions.x * _borderSize, dimensions.y * _borderSize, dimensions.z * _borderSize); - if (_drawOnHUD) { - DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); - } else { - DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); - } + auto batch = args->_batch; - glPopMatrix(); - glDepthMask(GL_TRUE); - } + if (batch) { + Transform transform; + transform.setTranslation(position); + transform.setRotation(rotation); + if (_isSolid) { + // if (_borderSize > 0) { + // // Draw a cube at a larger size behind the main cube, creating + // // a border effect. + // // Disable writing to the depth mask so that the "border" cube will not + // // occlude the main cube. This means the border could be covered by + // // overlays that are further back and drawn later, but this is good + // // enough for the use-case. + // transform.setScale(dimensions * _borderSize); + // batch->setModelTransform(transform); + // DependencyManager::get()->renderSolidCube(*batch, 1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); + // } + + transform.setScale(dimensions); + batch->setModelTransform(transform); + DependencyManager::get()->renderSolidCube(*batch, 1.0f, cubeColor); + } else { + + if (getIsDashedLine()) { + transform.setScale(1.0f); + batch->setModelTransform(transform); + + glm::vec3 halfDimensions = dimensions / 2.0f; + glm::vec3 bottomLeftNear(-halfDimensions.x, -halfDimensions.y, -halfDimensions.z); + glm::vec3 bottomRightNear(halfDimensions.x, -halfDimensions.y, -halfDimensions.z); + glm::vec3 topLeftNear(-halfDimensions.x, halfDimensions.y, -halfDimensions.z); + glm::vec3 topRightNear(halfDimensions.x, halfDimensions.y, -halfDimensions.z); + + glm::vec3 bottomLeftFar(-halfDimensions.x, -halfDimensions.y, halfDimensions.z); + glm::vec3 bottomRightFar(halfDimensions.x, -halfDimensions.y, halfDimensions.z); + glm::vec3 topLeftFar(-halfDimensions.x, halfDimensions.y, halfDimensions.z); + glm::vec3 topRightFar(halfDimensions.x, halfDimensions.y, halfDimensions.z); + + auto geometryCache = DependencyManager::get(); + + geometryCache->renderDashedLine(*batch, bottomLeftNear, bottomRightNear, cubeColor); + geometryCache->renderDashedLine(*batch, bottomRightNear, bottomRightFar, cubeColor); + geometryCache->renderDashedLine(*batch, bottomRightFar, bottomLeftFar, cubeColor); + geometryCache->renderDashedLine(*batch, bottomLeftFar, bottomLeftNear, cubeColor); + + geometryCache->renderDashedLine(*batch, topLeftNear, topRightNear, cubeColor); + geometryCache->renderDashedLine(*batch, topRightNear, topRightFar, cubeColor); + geometryCache->renderDashedLine(*batch, topRightFar, topLeftFar, cubeColor); + geometryCache->renderDashedLine(*batch, topLeftFar, topLeftNear, cubeColor); + + geometryCache->renderDashedLine(*batch, bottomLeftNear, topLeftNear, cubeColor); + geometryCache->renderDashedLine(*batch, bottomRightNear, topRightNear, cubeColor); + geometryCache->renderDashedLine(*batch, bottomLeftFar, topLeftFar, cubeColor); + geometryCache->renderDashedLine(*batch, bottomRightFar, topRightFar, cubeColor); - glPushMatrix(); - glScalef(dimensions.x, dimensions.y, dimensions.z); - if (_drawOnHUD) { - DependencyManager::get()->renderSolidCube(1.0f, cubeColor); - } else { - DependencyManager::get()->renderSolidCube(1.0f, cubeColor); - } - glPopMatrix(); } else { - glLineWidth(_lineWidth); - - if (getIsDashedLine()) { - glm::vec3 halfDimensions = dimensions / 2.0f; - glm::vec3 bottomLeftNear(-halfDimensions.x, -halfDimensions.y, -halfDimensions.z); - glm::vec3 bottomRightNear(halfDimensions.x, -halfDimensions.y, -halfDimensions.z); - glm::vec3 topLeftNear(-halfDimensions.x, halfDimensions.y, -halfDimensions.z); - glm::vec3 topRightNear(halfDimensions.x, halfDimensions.y, -halfDimensions.z); - - glm::vec3 bottomLeftFar(-halfDimensions.x, -halfDimensions.y, halfDimensions.z); - glm::vec3 bottomRightFar(halfDimensions.x, -halfDimensions.y, halfDimensions.z); - glm::vec3 topLeftFar(-halfDimensions.x, halfDimensions.y, halfDimensions.z); - glm::vec3 topRightFar(halfDimensions.x, halfDimensions.y, halfDimensions.z); - - auto geometryCache = DependencyManager::get(); - - geometryCache->renderDashedLine(bottomLeftNear, bottomRightNear, cubeColor); - geometryCache->renderDashedLine(bottomRightNear, bottomRightFar, cubeColor); - geometryCache->renderDashedLine(bottomRightFar, bottomLeftFar, cubeColor); - geometryCache->renderDashedLine(bottomLeftFar, bottomLeftNear, cubeColor); - - geometryCache->renderDashedLine(topLeftNear, topRightNear, cubeColor); - geometryCache->renderDashedLine(topRightNear, topRightFar, cubeColor); - geometryCache->renderDashedLine(topRightFar, topLeftFar, cubeColor); - geometryCache->renderDashedLine(topLeftFar, topLeftNear, cubeColor); - - geometryCache->renderDashedLine(bottomLeftNear, topLeftNear, cubeColor); - geometryCache->renderDashedLine(bottomRightNear, topRightNear, cubeColor); - geometryCache->renderDashedLine(bottomLeftFar, topLeftFar, cubeColor); - geometryCache->renderDashedLine(bottomRightFar, topRightFar, cubeColor); - - } else { - glScalef(dimensions.x, dimensions.y, dimensions.z); - DependencyManager::get()->renderWireCube(1.0f, cubeColor); - } + transform.setScale(dimensions); + batch->setModelTransform(transform); + DependencyManager::get()->renderWireCube(*batch, 1.0f, cubeColor); } - glPopMatrix(); - glPopMatrix(); + } + } else { + float glowLevel = getGlowLevel(); + Glower* glower = NULL; + if (glowLevel > 0.0f) { + glower = new Glower(glowLevel); + } - if (glower) { - delete glower; + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glPushMatrix(); + glm::vec3 positionToCenter = center - position; + glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); + if (_isSolid) { + if (_borderSize > 0) { + // Draw a cube at a larger size behind the main cube, creating + // a border effect. + // Disable writing to the depth mask so that the "border" cube will not + // occlude the main cube. This means the border could be covered by + // overlays that are further back and drawn later, but this is good + // enough for the use-case. + glDepthMask(GL_FALSE); + glPushMatrix(); + glScalef(dimensions.x * _borderSize, dimensions.y * _borderSize, dimensions.z * _borderSize); + + if (_drawOnHUD) { + DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); + } else { + DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); + } + + glPopMatrix(); + glDepthMask(GL_TRUE); + } + + glPushMatrix(); + glScalef(dimensions.x, dimensions.y, dimensions.z); + if (_drawOnHUD) { + DependencyManager::get()->renderSolidCube(1.0f, cubeColor); + } else { + DependencyManager::get()->renderSolidCube(1.0f, cubeColor); + } + glPopMatrix(); + } else { + glLineWidth(_lineWidth); + + if (getIsDashedLine()) { + glm::vec3 halfDimensions = dimensions / 2.0f; + glm::vec3 bottomLeftNear(-halfDimensions.x, -halfDimensions.y, -halfDimensions.z); + glm::vec3 bottomRightNear(halfDimensions.x, -halfDimensions.y, -halfDimensions.z); + glm::vec3 topLeftNear(-halfDimensions.x, halfDimensions.y, -halfDimensions.z); + glm::vec3 topRightNear(halfDimensions.x, halfDimensions.y, -halfDimensions.z); + + glm::vec3 bottomLeftFar(-halfDimensions.x, -halfDimensions.y, halfDimensions.z); + glm::vec3 bottomRightFar(halfDimensions.x, -halfDimensions.y, halfDimensions.z); + glm::vec3 topLeftFar(-halfDimensions.x, halfDimensions.y, halfDimensions.z); + glm::vec3 topRightFar(halfDimensions.x, halfDimensions.y, halfDimensions.z); + + auto geometryCache = DependencyManager::get(); + + geometryCache->renderDashedLine(bottomLeftNear, bottomRightNear, cubeColor); + geometryCache->renderDashedLine(bottomRightNear, bottomRightFar, cubeColor); + geometryCache->renderDashedLine(bottomRightFar, bottomLeftFar, cubeColor); + geometryCache->renderDashedLine(bottomLeftFar, bottomLeftNear, cubeColor); + + geometryCache->renderDashedLine(topLeftNear, topRightNear, cubeColor); + geometryCache->renderDashedLine(topRightNear, topRightFar, cubeColor); + geometryCache->renderDashedLine(topRightFar, topLeftFar, cubeColor); + geometryCache->renderDashedLine(topLeftFar, topLeftNear, cubeColor); + + geometryCache->renderDashedLine(bottomLeftNear, topLeftNear, cubeColor); + geometryCache->renderDashedLine(bottomRightNear, topRightNear, cubeColor); + geometryCache->renderDashedLine(bottomLeftFar, topLeftFar, cubeColor); + geometryCache->renderDashedLine(bottomRightFar, topRightFar, cubeColor); + + } else { + glScalef(dimensions.x, dimensions.y, dimensions.z); + DependencyManager::get()->renderWireCube(1.0f, cubeColor); + } + } + glPopMatrix(); + glPopMatrix(); + + if (glower) { + delete glower; + } } } diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index b39b2c3274..e68e5b47f2 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -36,87 +36,128 @@ void Grid3DOverlay::render(RenderArgs* args) { return; // do nothing if we're not visible } - if (!_gridProgram.isLinked()) { - if (!_gridProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/grid.vert")) { - qDebug() << "Failed to compile: " + _gridProgram.log(); - return; - } - if (!_gridProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/grid.frag")) { - qDebug() << "Failed to compile: " + _gridProgram.log(); - return; - } - if (!_gridProgram.link()) { - qDebug() << "Failed to link: " + _gridProgram.log(); - return; - } - } - - // Render code largely taken from MetavoxelEditor::render() - glDisable(GL_LIGHTING); - - glDepthMask(GL_FALSE); - - glPushMatrix(); - - glm::quat rotation = getRotation(); - - glm::vec3 axis = glm::axis(rotation); - - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - - glLineWidth(1.5f); - - // center the grid around the camera position on the plane - glm::vec3 rotated = glm::inverse(rotation) * Application::getInstance()->getCamera()->getPosition(); - float spacing = _minorGridWidth; - - float alpha = getAlpha(); - xColor color = getColor(); - glm::vec3 position = getPosition(); - const int MINOR_GRID_DIVISIONS = 200; const int MAJOR_GRID_DIVISIONS = 100; const float MAX_COLOR = 255.0f; + // center the grid around the camera position on the plane + glm::vec3 rotated = glm::inverse(_rotation) * Application::getInstance()->getCamera()->getPosition(); + float spacing = _minorGridWidth; + + float alpha = getAlpha(); + xColor color = getColor(); glm::vec4 gridColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - _gridProgram.bind(); + auto batch = args->_batch; - // Minor grid - glPushMatrix(); - { - glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2), - spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), position.z); + if (batch) { + Transform transform; + transform.setRotation(_rotation); - float scale = MINOR_GRID_DIVISIONS * spacing; - glScalef(scale, scale, scale); - DependencyManager::get()->renderGrid(MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor); + // Minor grid + { + batch->_glLineWidth(1.0f); + auto position = glm::vec3(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), + _position.z); + float scale = MINOR_GRID_DIVISIONS * spacing; + + transform.setTranslation(position); + transform.setScale(scale); + + batch->setModelTransform(transform); + + DependencyManager::get()->renderGrid(*batch, MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor); + } + + // Major grid + { + batch->_glLineWidth(4.0f); + spacing *= _majorGridEvery; + auto position = glm::vec3(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), + _position.z); + float scale = MAJOR_GRID_DIVISIONS * spacing; + + transform.setTranslation(position); + transform.setScale(scale); + + batch->setModelTransform(transform); + + DependencyManager::get()->renderGrid(*batch, MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor); + } + } else { + if (!_gridProgram.isLinked()) { + if (!_gridProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/grid.vert")) { + qDebug() << "Failed to compile: " + _gridProgram.log(); + return; + } + if (!_gridProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/grid.frag")) { + qDebug() << "Failed to compile: " + _gridProgram.log(); + return; + } + if (!_gridProgram.link()) { + qDebug() << "Failed to link: " + _gridProgram.log(); + return; + } + } + + // Render code largely taken from MetavoxelEditor::render() + glDisable(GL_LIGHTING); + + glDepthMask(GL_FALSE); + + glPushMatrix(); + + glm::quat rotation = getRotation(); + + glm::vec3 axis = glm::axis(rotation); + + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + + glLineWidth(1.5f); + + glm::vec3 position = getPosition(); + + _gridProgram.bind(); + + // Minor grid + glPushMatrix(); + { + glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), position.z); + + float scale = MINOR_GRID_DIVISIONS * spacing; + glScalef(scale, scale, scale); + + DependencyManager::get()->renderGrid(MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor); + } + glPopMatrix(); + + // Major grid + glPushMatrix(); + { + glLineWidth(4.0f); + spacing *= _majorGridEvery; + glTranslatef(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), position.z); + + float scale = MAJOR_GRID_DIVISIONS * spacing; + glScalef(scale, scale, scale); + + DependencyManager::get()->renderGrid(MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor); + } + glPopMatrix(); + + _gridProgram.release(); + + glPopMatrix(); + + glEnable(GL_LIGHTING); + glDepthMask(GL_TRUE); } - glPopMatrix(); - - // Major grid - glPushMatrix(); - { - glLineWidth(4.0f); - spacing *= _majorGridEvery; - glTranslatef(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2), - spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), position.z); - - float scale = MAJOR_GRID_DIVISIONS * spacing; - glScalef(scale, scale, scale); - - DependencyManager::get()->renderGrid(MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor); - } - glPopMatrix(); - - _gridProgram.release(); - - glPopMatrix(); - - glEnable(GL_LIGHTING); - glDepthMask(GL_TRUE); } void Grid3DOverlay::setProperties(const QScriptValue& properties) { diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index 6672a88e45..34c983d7b2 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -37,41 +37,57 @@ void Line3DOverlay::render(RenderArgs* args) { return; // do nothing if we're not visible } - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } - - glPushMatrix(); - - glDisable(GL_LIGHTING); - glLineWidth(_lineWidth); - float alpha = getAlpha(); xColor color = getColor(); const float MAX_COLOR = 255.0f; glm::vec4 colorv4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - glm::vec3 position = getPosition(); - glm::quat rotation = getRotation(); + auto batch = args->_batch; - glTranslatef(position.x, position.y, position.z); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + if (batch) { + Transform transform; + transform.setTranslation(_position); + transform.setRotation(_rotation); + batch->setModelTransform(transform); - if (getIsDashedLine()) { - // TODO: add support for color to renderDashedLine() - DependencyManager::get()->renderDashedLine(_position, _end, colorv4, _geometryCacheID); + if (getIsDashedLine()) { + // TODO: add support for color to renderDashedLine() + DependencyManager::get()->renderDashedLine(*batch, _position, _end, colorv4, _geometryCacheID); + } else { + DependencyManager::get()->renderLine(*batch, _start, _end, colorv4, _geometryCacheID); + } } else { - DependencyManager::get()->renderLine(_start, _end, colorv4, _geometryCacheID); - } - glEnable(GL_LIGHTING); + float glowLevel = getGlowLevel(); + Glower* glower = NULL; + if (glowLevel > 0.0f) { + glower = new Glower(glowLevel); + } - glPopMatrix(); + glPushMatrix(); - if (glower) { - delete glower; + glDisable(GL_LIGHTING); + glLineWidth(_lineWidth); + + glm::vec3 position = getPosition(); + glm::quat rotation = getRotation(); + + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + + if (getIsDashedLine()) { + // TODO: add support for color to renderDashedLine() + DependencyManager::get()->renderDashedLine(_position, _end, colorv4, _geometryCacheID); + } else { + DependencyManager::get()->renderLine(_start, _end, colorv4, _geometryCacheID); + } + glEnable(GL_LIGHTING); + + glPopMatrix(); + + if (glower) { + delete glower; + } } } diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index d5e4b34f6b..bcfba67313 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -66,8 +66,8 @@ namespace render { } template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) { if (args) { - glPushMatrix(); if (overlay->getAnchor() == Overlay::MY_AVATAR) { + glPushMatrix(); MyAvatar* avatar = DependencyManager::get()->getMyAvatar(); glm::quat myAvatarRotation = avatar->getOrientation(); glm::vec3 myAvatarPosition = avatar->getPosition(); @@ -78,9 +78,11 @@ namespace render { glTranslatef(myAvatarPosition.x, myAvatarPosition.y, myAvatarPosition.z); glRotatef(angle, axis.x, axis.y, axis.z); glScalef(myAvatarScale, myAvatarScale, myAvatarScale); + overlay->render(args); + glPopMatrix(); + } else { + overlay->render(args); } - overlay->render(args); - glPopMatrix(); } } } diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 8662030e23..dc5fdeabb2 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -41,74 +41,116 @@ void Rectangle3DOverlay::render(RenderArgs* args) { const float MAX_COLOR = 255.0f; glm::vec4 rectangleColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - glDisable(GL_LIGHTING); - glm::vec3 position = getPosition(); glm::vec3 center = getCenter(); glm::vec2 dimensions = getDimensions(); glm::vec2 halfDimensions = dimensions * 0.5f; glm::quat rotation = getRotation(); - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } + auto batch = args->_batch; - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glPushMatrix(); - glm::vec3 positionToCenter = center - position; - glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); - //glScalef(dimensions.x, dimensions.y, 1.0f); + if (batch) { + Transform transform; + transform.setTranslation(position); + transform.setRotation(rotation); - glLineWidth(_lineWidth); + batch->setModelTransform(transform); + if (getIsSolid()) { + glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f); + DependencyManager::get()->renderQuad(*batch, topLeft, bottomRight, rectangleColor); + } else { auto geometryCache = DependencyManager::get(); + if (getIsDashedLine()) { + glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f); + glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f); - // for our overlay, is solid means we draw a solid "filled" rectangle otherwise we just draw a border line... - if (getIsSolid()) { - glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f); - DependencyManager::get()->renderQuad(topLeft, bottomRight, rectangleColor); + geometryCache->renderDashedLine(*batch, point1, point2, rectangleColor); + geometryCache->renderDashedLine(*batch, point2, point3, rectangleColor); + geometryCache->renderDashedLine(*batch, point3, point4, rectangleColor); + geometryCache->renderDashedLine(*batch, point4, point1, rectangleColor); } else { - if (getIsDashedLine()) { + if (halfDimensions != _previousHalfDimensions) { + QVector border; + border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); + border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f); + border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f); + border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f); + border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); + geometryCache->updateVertices(_geometryCacheID, border, rectangleColor); - glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f); - glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f); - - geometryCache->renderDashedLine(point1, point2, rectangleColor); - geometryCache->renderDashedLine(point2, point3, rectangleColor); - geometryCache->renderDashedLine(point3, point4, rectangleColor); - geometryCache->renderDashedLine(point4, point1, rectangleColor); - - } else { - - if (halfDimensions != _previousHalfDimensions) { - QVector border; - border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); - border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f); - border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f); - border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f); - border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); - geometryCache->updateVertices(_geometryCacheID, border, rectangleColor); - - _previousHalfDimensions = halfDimensions; - - } - geometryCache->renderVertices(gpu::LINE_STRIP, _geometryCacheID); + _previousHalfDimensions = halfDimensions; } + geometryCache->renderVertices(*batch, gpu::LINE_STRIP, _geometryCacheID); } - + } + } else { + glDisable(GL_LIGHTING); + + float glowLevel = getGlowLevel(); + Glower* glower = NULL; + if (glowLevel > 0.0f) { + glower = new Glower(glowLevel); + } + + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glPushMatrix(); + glm::vec3 positionToCenter = center - position; + glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); + //glScalef(dimensions.x, dimensions.y, 1.0f); + + glLineWidth(_lineWidth); + + auto geometryCache = DependencyManager::get(); + + // for our overlay, is solid means we draw a solid "filled" rectangle otherwise we just draw a border line... + if (getIsSolid()) { + glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f); + DependencyManager::get()->renderQuad(topLeft, bottomRight, rectangleColor); + } else { + if (getIsDashedLine()) { + + glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f); + glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f); + + geometryCache->renderDashedLine(point1, point2, rectangleColor); + geometryCache->renderDashedLine(point2, point3, rectangleColor); + geometryCache->renderDashedLine(point3, point4, rectangleColor); + geometryCache->renderDashedLine(point4, point1, rectangleColor); + + } else { + + if (halfDimensions != _previousHalfDimensions) { + QVector border; + border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); + border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f); + border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f); + border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f); + border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); + geometryCache->updateVertices(_geometryCacheID, border, rectangleColor); + + _previousHalfDimensions = halfDimensions; + + } + geometryCache->renderVertices(gpu::LINE_STRIP, _geometryCacheID); + } + } + + glPopMatrix(); glPopMatrix(); - glPopMatrix(); - - if (glower) { - delete glower; + + if (glower) { + delete glower; + } } } diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index a0e8d06b41..f5fba0ed05 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -39,33 +39,45 @@ void Sphere3DOverlay::render(RenderArgs* args) { const float MAX_COLOR = 255.0f; glm::vec4 sphereColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - glDisable(GL_LIGHTING); - - glm::vec3 position = getPosition(); - glm::vec3 center = getCenter(); - glm::vec3 dimensions = getDimensions(); - glm::quat rotation = getRotation(); + auto batch = args->_batch; - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } + if (batch) { + Transform transform; + transform.setTranslation(_position); + transform.setRotation(_rotation); + transform.setScale(_dimensions); + + batch->setModelTransform(transform); + DependencyManager::get()->renderSphere(*batch, 1.0f, SLICES, SLICES, sphereColor, _isSolid); + } else { + glDisable(GL_LIGHTING); + + glm::vec3 position = getPosition(); + glm::vec3 center = getCenter(); + glm::vec3 dimensions = getDimensions(); + glm::quat rotation = getRotation(); + + float glowLevel = getGlowLevel(); + Glower* glower = NULL; + if (glowLevel > 0.0f) { + glower = new Glower(glowLevel); + } - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); glPushMatrix(); - glm::vec3 positionToCenter = center - position; - glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); - glScalef(dimensions.x, dimensions.y, dimensions.z); - DependencyManager::get()->renderSphere(1.0f, SLICES, SLICES, sphereColor, _isSolid); + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glPushMatrix(); + glm::vec3 positionToCenter = center - position; + glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); + glScalef(dimensions.x, dimensions.y, dimensions.z); + DependencyManager::get()->renderSphere(1.0f, SLICES, SLICES, sphereColor, _isSolid); + glPopMatrix(); glPopMatrix(); - glPopMatrix(); - - if (glower) { - delete glower; + + if (glower) { + delete glower; + } } } diff --git a/interface/ui/updateDialog.ui b/interface/ui/updateDialog.ui deleted file mode 100644 index fc375e17c2..0000000000 --- a/interface/ui/updateDialog.ui +++ /dev/null @@ -1,132 +0,0 @@ - - - Dialog - - - Qt::NonModal - - - - 0 - 0 - 750 - 213 - - - - PointingHandCursor - - - Update Required - - - background-color: rgb(255, 255, 255); - - - - - 50 - 20 - 641 - 111 - - - - - Arial - -1 - - - - font-family: Arial; -font-size: 20px; - - - - - - true - - - - - - 360 - 160 - 374 - 42 - - - - - - - PointingHandCursor - - - background-color: #333333; -border-width: 0; -border-radius: 9px; -border-radius: 9px; -font-family: Arial; -font-size: 18px; -font-weight: 100; -color: #b7b7b7; -width: 120px; -height: 40px; - - - Download - - - - - - - PointingHandCursor - - - background-color: #333333; -border-width: 0; -border-radius: 9px; -border-radius: 9px; -font-family: Arial; -font-size: 18px; -font-weight: 100; -color: #b7b7b7; -width: 120px; -height: 40px; - - - Skip Version - - - - - - - PointingHandCursor - - - background-color: #333333; -border-width: 0; -border-radius: 9px; -border-radius: 9px; -font-family: Arial; -font-size: 18px; -font-weight: 100; -color: #b7b7b7; -width: 120px; -height: 40px; - - - Close - - - - - - - - - diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 340ca9374d..6450f25208 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -156,6 +156,7 @@ void AudioClient::audioMixerKilled() { _hasReceivedFirstPacket = false; _outgoingAvatarAudioSequenceNumber = 0; _stats.reset(); + emit disconnected(); } diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 3b2c1c1ae6..642edde84a 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -186,6 +186,7 @@ signals: void deviceChanged(); void receivedFirstPacket(); + void disconnected(); protected: AudioClient(); diff --git a/libraries/auto-updater/CMakeLists.txt b/libraries/auto-updater/CMakeLists.txt new file mode 100644 index 0000000000..b3665af2cb --- /dev/null +++ b/libraries/auto-updater/CMakeLists.txt @@ -0,0 +1,3 @@ +set(TARGET_NAME auto-updater) +setup_hifi_library(Network) +link_hifi_libraries(shared networking) diff --git a/libraries/auto-updater/src/AutoUpdater.cpp b/libraries/auto-updater/src/AutoUpdater.cpp new file mode 100644 index 0000000000..8c6aa5605d --- /dev/null +++ b/libraries/auto-updater/src/AutoUpdater.cpp @@ -0,0 +1,144 @@ +// +// AutoUpdater.cpp +// libraries/auto-update/src +// +// Created by Leonardo Murillo on 6/1/2015. +// 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 "AutoUpdater.h" + +#include +#include + +AutoUpdater::AutoUpdater() { +#if defined Q_OS_WIN32 + _operatingSystem = "windows"; +#elif defined Q_OS_MAC + _operatingSystem = "mac"; +#elif defined Q_OS_LINUX + _operatingSystem = "ubuntu"; +#endif + + connect(this, SIGNAL(latestVersionDataParsed()), this, SLOT(checkVersionAndNotify())); +} + +void AutoUpdater::checkForUpdate() { + this->getLatestVersionData(); +} + +void AutoUpdater::getLatestVersionData() { + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest latestVersionRequest(BUILDS_XML_URL); + latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + QNetworkReply* reply = networkAccessManager.get(latestVersionRequest); + connect(reply, &QNetworkReply::finished, this, &AutoUpdater::parseLatestVersionData); +} + +void AutoUpdater::parseLatestVersionData() { + QNetworkReply* sender = qobject_cast(QObject::sender()); + + QXmlStreamReader xml(sender); + + int version; + QString downloadUrl; + QString releaseTime; + QString releaseNotes; + QString commitSha; + QString pullRequestNumber; + + while (!xml.atEnd() && !xml.hasError()) { + if (xml.name().toString() == "project" && + xml.attributes().hasAttribute("name") && + xml.attributes().value("name").toString() == "interface") { + xml.readNext(); + + while (!xml.atEnd() && !xml.hasError() && xml.name().toString() != "project") { + if (xml.name().toString() == "platform" && + xml.attributes().hasAttribute("name") && + xml.attributes().value("name").toString() == _operatingSystem) { + xml.readNext(); + while (!xml.atEnd() && !xml.hasError() && + xml.name().toString() != "platform") { + + if (xml.name().toString() == "build" && xml.tokenType() != QXmlStreamReader::EndElement) { + xml.readNext(); + version = xml.readElementText().toInt(); + xml.readNext(); + downloadUrl = xml.readElementText(); + xml.readNext(); + releaseTime = xml.readElementText(); + xml.readNext(); + if (xml.name().toString() == "notes" && xml.tokenType() != QXmlStreamReader::EndElement) { + xml.readNext(); + while (!xml.atEnd() && !xml.hasError() && xml.name().toString() != "notes") { + if (xml.name().toString() == "note" && xml.tokenType() != QXmlStreamReader::EndElement) { + releaseNotes = releaseNotes + "\n" + xml.readElementText(); + } + xml.readNext(); + } + } + xml.readNext(); + commitSha = xml.readElementText(); + xml.readNext(); + pullRequestNumber = xml.readElementText(); + appendBuildData(version, downloadUrl, releaseTime, releaseNotes, pullRequestNumber); + releaseNotes = ""; + } + + xml.readNext(); + } + } + xml.readNext(); + } + + } else { + xml.readNext(); + } + } + sender->deleteLater(); + emit latestVersionDataParsed(); +} + +void AutoUpdater::checkVersionAndNotify() { + if (QCoreApplication::applicationVersion() == "dev" || _builds.empty()) { + // No version checking is required in dev builds or when no build + // data was found for the platform + return; + } + int latestVersionAvailable = _builds.lastKey(); + if (QCoreApplication::applicationVersion().toInt() < latestVersionAvailable) { + emit newVersionIsAvailable(); + } +} + +void AutoUpdater::performAutoUpdate(int version) { + // NOTE: This is not yet auto updating - however this is a checkpoint towards that end + // Next PR will handle the automatic download, upgrading and application restart + const QMap& chosenVersion = _builds.value(version); + const QUrl& downloadUrl = chosenVersion.value("downloadUrl"); + QDesktopServices::openUrl(downloadUrl); + QCoreApplication::quit(); +} + +void AutoUpdater::downloadUpdateVersion(int version) { + emit newVersionIsDownloaded(); +} + +void AutoUpdater::appendBuildData(int versionNumber, + const QString& downloadURL, + const QString& releaseTime, + const QString& releaseNotes, + const QString& pullRequestNumber) { + + QMap thisBuildDetails; + thisBuildDetails.insert("downloadUrl", downloadURL); + thisBuildDetails.insert("releaseTime", releaseTime); + thisBuildDetails.insert("releaseNotes", releaseNotes); + thisBuildDetails.insert("pullRequestNumber", pullRequestNumber); + _builds.insert(versionNumber, thisBuildDetails); + +} \ No newline at end of file diff --git a/libraries/auto-updater/src/AutoUpdater.h b/libraries/auto-updater/src/AutoUpdater.h new file mode 100644 index 0000000000..70867e5a44 --- /dev/null +++ b/libraries/auto-updater/src/AutoUpdater.h @@ -0,0 +1,68 @@ +// +// AutoUpdater.h +// libraries/auto-update/src +// +// Created by Leonardo Murillo on 6/1/2015. +// 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_AutoUpdater_h +#define hifi_AutoUpdater_h + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml"); + +class AutoUpdater : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + +public: + AutoUpdater(); + + void checkForUpdate(); + const QMap>& getBuildData() { return _builds; } + void performAutoUpdate(int version); + +signals: + void latestVersionDataParsed(); + void newVersionIsAvailable(); + void newVersionIsDownloaded(); + +private: + QMap> _builds; + QString _operatingSystem; + + void getLatestVersionData(); + void downloadUpdateVersion(int version); + void appendBuildData(int versionNumber, + const QString& downloadURL, + const QString& releaseTime, + const QString& releaseNotes, + const QString& pullRequestNumber); + +private slots: + void parseLatestVersionData(); + void checkVersionAndNotify(); +}; + +#endif // _hifi_AutoUpdater_h diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index 4e5de331bb..65407c74e7 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -49,6 +49,7 @@ void RenderableLineEntityItem::render(RenderArgs* args) { batch._glLineWidth(getLineWidth()); if (getLinePoints().size() > 1) { + DependencyManager::get()->bindSimpleProgram(batch); DependencyManager::get()->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID); } batch._glLineWidth(1.0f); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index ac1d4b494f..4842063af3 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -41,7 +41,11 @@ public: ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData); - virtual void somethingChangedNotification() { _needsInitialSimulation = true; } + virtual void somethingChangedNotification() { + // FIX ME: this is overly aggressive. We only really need to simulate() if something about + // the world space transform has changed and/or if some animation is occurring. + _needsInitialSimulation = true; + } virtual bool readyToAddToScene(RenderArgs* renderArgs = nullptr); virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 6a50cbf1cb..d00728a9eb 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -53,6 +53,7 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) { batch.setUniformTexture(0, _texture->getGPUTexture()); } batch.setModelTransform(getTransformToCenter()); + DependencyManager::get()->bindSimpleProgram(batch, textured); DependencyManager::get()->renderVertices(batch, gpu::QUADS, _cacheID); }; @@ -74,31 +75,34 @@ void RenderableParticleEffectEntityItem::updateQuads(RenderArgs* args, bool text vertices.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE); if (textured) { - positions.reserve(getLivingParticleCount()); textureCoords.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE); - - for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) { - positions.append(_particlePositions[i]); - + } + positions.reserve(getLivingParticleCount()); + + + for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) { + positions.append(_particlePositions[i]); + if (textured) { textureCoords.append(glm::vec2(0, 1)); textureCoords.append(glm::vec2(1, 1)); textureCoords.append(glm::vec2(1, 0)); textureCoords.append(glm::vec2(0, 0)); } - - // sort particles back to front - ::zSortAxis = args->_viewFrustum->getDirection(); - qSort(positions.begin(), positions.end(), zSort); } + + // sort particles back to front + ::zSortAxis = args->_viewFrustum->getDirection(); + qSort(positions.begin(), positions.end(), zSort); for (int i = 0; i < positions.size(); i++) { glm::vec3 pos = (textured) ? positions[i] : _particlePositions[i]; // generate corners of quad aligned to face the camera. - vertices.append(pos - rightOffset + upOffset); vertices.append(pos + rightOffset + upOffset); - vertices.append(pos + rightOffset - upOffset); + vertices.append(pos - rightOffset + upOffset); vertices.append(pos - rightOffset - upOffset); + vertices.append(pos + rightOffset - upOffset); + } if (textured) { diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index bb5932d70c..71c3537a0c 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -121,7 +121,7 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) _volData->setBorderValue(255); #ifdef WANT_DEBUG - qDebug() << " new size is" << _volData->getWidth() << _volData->getHeight() << _volData->getDepth(); + qDebug() << " new voxel-space size is" << _volData->getWidth() << _volData->getHeight() << _volData->getDepth(); #endif // I'm not sure this is needed... the docs say that each element is initialized with its default @@ -220,12 +220,15 @@ uint8_t RenderablePolyVoxEntityItem::getVoxel(int x, int y, int z) { return _volData->getVoxelAt(x, y, z); } -void RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) { +void RenderablePolyVoxEntityItem::setVoxelInternal(int x, int y, int z, uint8_t toValue) { + // set a voxel without recompressing the voxel data assert(_volData); if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { return; } + updateOnCount(x, y, z, toValue); + if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC) { _volData->setVoxelAt(x + 1, y + 1, z + 1, toValue); } else { @@ -234,6 +237,14 @@ void RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) } +void RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) { + if (_locked) { + return; + } + setVoxelInternal(x, y, z, toValue); + compressVolumeData(); +} + void RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toValue) { // keep _onCount up to date if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { @@ -255,11 +266,15 @@ void RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toV } void RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { + if (_locked) { + return; + } + for (int z = 0; z < _voxelVolumeSize.z; z++) { for (int y = 0; y < _voxelVolumeSize.y; y++) { for (int x = 0; x < _voxelVolumeSize.x; x++) { updateOnCount(x, y, z, toValue); - setVoxel(x, y, z, toValue); + setVoxelInternal(x, y, z, toValue); } } } @@ -267,11 +282,19 @@ void RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { } void RenderablePolyVoxEntityItem::setVoxelInVolume(glm::vec3 position, uint8_t toValue) { - updateOnCount(position.x, position.y, position.z, toValue); - setVoxel(position.x, position.y, position.z, toValue); + if (_locked) { + return; + } + + // same as setVoxel but takes a vector rather than 3 floats. + setVoxel((int)position.x, (int)position.y, (int)position.z, toValue); } void RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) { + if (_locked) { + return; + } + // This three-level for loop iterates over every voxel in the volume for (int z = 0; z < _voxelVolumeSize.z; z++) { for (int y = 0; y < _voxelVolumeSize.y; y++) { @@ -283,7 +306,7 @@ void RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radi // If the current voxel is less than 'radius' units from the center then we make it solid. if (fDistToCenter <= radius) { updateOnCount(x, y, z, toValue); - setVoxel(x, y, z, toValue); + setVoxelInternal(x, y, z, toValue); } } } @@ -380,10 +403,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { getModel(); } - Transform transform; - transform.setTranslation(getPosition() - getRegistrationPoint() * getDimensions()); - transform.setRotation(getRotation()); - transform.setScale(getDimensions() / _voxelVolumeSize); + Transform transform(voxelToWorldMatrix()); auto mesh = _modelGeometry.getMesh(); Q_ASSERT(args->_batch); @@ -514,6 +534,11 @@ void RenderablePolyVoxEntityItem::compressVolumeData() { QByteArray newVoxelData; QDataStream writer(&newVoxelData, QIODevice::WriteOnly | QIODevice::Truncate); + + #ifdef WANT_DEBUG + qDebug() << "compressing voxel data of size:" << voxelXSize << voxelYSize << voxelZSize; + #endif + writer << voxelXSize << voxelYSize << voxelZSize; QByteArray compressedData = qCompress(uncompressedData, 9); @@ -573,7 +598,7 @@ void RenderablePolyVoxEntityItem::decompressVolumeData() { for (int x = 0; x < voxelXSize; x++) { int uncompressedIndex = (z * voxelYSize * voxelXSize) + (y * voxelZSize) + x; updateOnCount(x, y, z, uncompressedData[uncompressedIndex]); - setVoxel(x, y, z, uncompressedData[uncompressedIndex]); + setVoxelInternal(x, y, z, uncompressedData[uncompressedIndex]); } } } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 77aeb24f2a..814f3deb07 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -73,11 +73,12 @@ public: virtual void setVoxelInVolume(glm::vec3 position, uint8_t toValue); SIMPLE_RENDERABLE(); - + private: // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions // may not match _voxelVolumeSize. + void setVoxelInternal(int x, int y, int z, uint8_t toValue); void compressVolumeData(); void decompressVolumeData(); diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 8a67bb99f2..d06ffb9400 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -47,12 +47,13 @@ void RenderableTextEntityItem::render(RenderArgs* args) { glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND); DependencyManager::get()->renderQuad(batch, minCorner, maxCorner, backgroundColor); - float scale = _lineHeight / _textRenderer->getRowHeight(); + float scale = _lineHeight / _textRenderer->getFontSize(); transformToTopLeft.setScale(scale); // Scale to have the correct line height batch.setModelTransform(transformToTopLeft); - float leftMargin = 0.5f * _lineHeight, topMargin = 0.5f * _lineHeight; - glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin, dimensions.y - 2.0f * topMargin); + float leftMargin = 0.1f * _lineHeight, topMargin = 0.1f * _lineHeight; + glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin, + dimensions.y - 2.0f * topMargin); _textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, _text, textColor, bounds / scale); } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index f64dad0ef4..d2881e5f3a 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -67,12 +67,12 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) : _simulatorIDChangedTime(0), _marketplaceID(ENTITY_ITEM_DEFAULT_MARKETPLACE_ID), _name(ENTITY_ITEM_DEFAULT_NAME), + _href(""), + _description(""), _dirtyFlags(0), _element(nullptr), _physicsInfo(nullptr), - _simulated(false), - _href(""), - _description("") + _simulated(false) { quint64 now = usecTimestampNow(); _lastSimulated = now; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 5a59636fe7..80333a44e5 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -392,17 +392,20 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData // If we wrote fewer entities than we expected, update the number of entities in our packet bool successUpdateEntityCount = true; - if (!noEntitiesFit && numberOfEntities != actualNumberOfEntities) { + if (numberOfEntities != actualNumberOfEntities) { successUpdateEntityCount = packetData->updatePriorBytes(numberOfEntitiesOffset, (const unsigned char*)&actualNumberOfEntities, sizeof(actualNumberOfEntities)); } // If we weren't able to update our entity count, or we couldn't fit any entities, then // we should discard our element and return a result of NONE - if (!successUpdateEntityCount || noEntitiesFit) { + if (!successUpdateEntityCount) { packetData->discardLevel(elementLevel); appendElementState = OctreeElement::NONE; } else { + if (noEntitiesFit) { + appendElementState = OctreeElement::PARTIAL; + } packetData->endLevel(elementLevel); } return appendElementState; diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 95ab7d1035..84fbf11311 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -23,7 +23,7 @@ const glm::vec3 PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE = glm::vec3(32, 32, 32); const float PolyVoxEntityItem::MAX_VOXEL_DIMENSION = 32.0f; -const QByteArray PolyVoxEntityItem::DEFAULT_VOXEL_DATA(qCompress(QByteArray(0), 9)); // XXX +const QByteArray PolyVoxEntityItem::DEFAULT_VOXEL_DATA(PolyVoxEntityItem::makeEmptyVoxelData()); const PolyVoxEntityItem::PolyVoxSurfaceStyle PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE = PolyVoxEntityItem::SURFACE_MARCHING_CUBES; @@ -31,6 +31,25 @@ EntityItemPointer PolyVoxEntityItem::factory(const EntityItemID& entityID, const return EntityItemPointer(new PolyVoxEntityItem(entityID, properties)); } + + + +QByteArray PolyVoxEntityItem::makeEmptyVoxelData(quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize) { + int rawSize = voxelXSize * voxelYSize * voxelZSize; + + QByteArray uncompressedData = QByteArray(rawSize, '\0'); + QByteArray newVoxelData; + QDataStream writer(&newVoxelData, QIODevice::WriteOnly | QIODevice::Truncate); + writer << voxelXSize << voxelYSize << voxelZSize; + + QByteArray compressedData = qCompress(uncompressedData, 9); + writer << compressedData; + + return newVoxelData; +} + + + PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : EntityItem(entityItemID), _voxelVolumeSize(PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE), diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index bf8214675b..e5d511c087 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -12,14 +12,14 @@ #ifndef hifi_PolyVoxEntityItem_h #define hifi_PolyVoxEntityItem_h -#include "EntityItem.h" +#include "EntityItem.h" class PolyVoxEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); PolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); - + ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of an entity @@ -29,22 +29,22 @@ class PolyVoxEntityItem : public EntityItem { // TODO: eventually only include properties changed since the params.lastViewFrustumSent time virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, + int& propertyCount, OctreeElement::AppendState& appendState) const; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData); - + // never have a ray intersection pick a PolyVoxEntityItem. virtual bool supportsDetailedRayIntersection() const { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { return false; } virtual void debugDump() const; @@ -58,12 +58,12 @@ class PolyVoxEntityItem : public EntityItem { enum PolyVoxSurfaceStyle { SURFACE_MARCHING_CUBES, SURFACE_CUBIC, - SURFACE_EDGED_CUBIC + SURFACE_EDGED_CUBIC }; virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { _voxelSurfaceStyle = voxelSurfaceStyle; } virtual void setVoxelSurfaceStyle(uint16_t voxelSurfaceStyle) { - setVoxelSurfaceStyle((PolyVoxSurfaceStyle) voxelSurfaceStyle); + setVoxelSurfaceStyle((PolyVoxSurfaceStyle) voxelSurfaceStyle); } virtual PolyVoxSurfaceStyle getVoxelSurfaceStyle() const { return _voxelSurfaceStyle; } @@ -82,10 +82,11 @@ class PolyVoxEntityItem : public EntityItem { virtual void setAll(uint8_t toValue) {} virtual void setVoxelInVolume(glm::vec3 position, uint8_t toValue) {} - + virtual uint8_t getVoxel(int x, int y, int z) { return 0; } virtual void setVoxel(int x, int y, int z, uint8_t toValue) {} + static QByteArray makeEmptyVoxelData(quint16 voxelXSize = 16, quint16 voxelYSize = 16, quint16 voxelZSize = 16); protected: glm::vec3 _voxelVolumeSize; // this is always 3 bytes diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 405ea9459d..bbd2121c54 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -150,6 +150,8 @@ public: void _glUseProgram(GLuint program); void _glUniform1f(GLint location, GLfloat v0); void _glUniform2f(GLint location, GLfloat v0, GLfloat v1); + void _glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + void _glUniform3fv(GLint location, GLsizei count, const GLfloat* value); void _glUniform4fv(GLint location, GLsizei count, const GLfloat* value); void _glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); @@ -210,6 +212,8 @@ public: COMMAND_glUseProgram, COMMAND_glUniform1f, COMMAND_glUniform2f, + COMMAND_glUniform3f, + COMMAND_glUniform3fv, COMMAND_glUniform4fv, COMMAND_glUniformMatrix4fv, diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index da6979fb27..20a6b60901 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -61,6 +61,8 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glUseProgram), (&::gpu::GLBackend::do_glUniform1f), (&::gpu::GLBackend::do_glUniform2f), + (&::gpu::GLBackend::do_glUniform3f), + (&::gpu::GLBackend::do_glUniform3fv), (&::gpu::GLBackend::do_glUniform4fv), (&::gpu::GLBackend::do_glUniformMatrix4fv), @@ -462,6 +464,7 @@ void Batch::_glUniform2f(GLint location, GLfloat v0, GLfloat v1) { DO_IT_NOW(_glUniform2f, 1); } + void GLBackend::do_glUniform2f(Batch& batch, uint32 paramOffset) { if (_pipeline._program == 0) { // We should call updatePipeline() to bind the program but we are not doing that @@ -475,6 +478,56 @@ void GLBackend::do_glUniform2f(Batch& batch, uint32 paramOffset) { (void) CHECK_GL_ERROR(); } +void Batch::_glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) { + ADD_COMMAND_GL(glUniform3f); + + _params.push_back(v2); + _params.push_back(v1); + _params.push_back(v0); + _params.push_back(location); + + DO_IT_NOW(_glUniform3f, 1); +} + +void GLBackend::do_glUniform3f(Batch& batch, uint32 paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + glUniform3f( + batch._params[paramOffset + 3]._int, + batch._params[paramOffset + 2]._float, + batch._params[paramOffset + 1]._float, + batch._params[paramOffset + 0]._float); + (void) CHECK_GL_ERROR(); +} + +void Batch::_glUniform3fv(GLint location, GLsizei count, const GLfloat* value) { + ADD_COMMAND_GL(glUniform3fv); + + const int VEC3_SIZE = 3 * sizeof(float); + _params.push_back(cacheData(count * VEC3_SIZE, value)); + _params.push_back(count); + _params.push_back(location); + + DO_IT_NOW(_glUniform3fv, 3); +} +void GLBackend::do_glUniform3fv(Batch& batch, uint32 paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + glUniform3fv( + batch._params[paramOffset + 2]._int, + batch._params[paramOffset + 1]._uint, + (const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint)); + + (void) CHECK_GL_ERROR(); +} + + void Batch::_glUniform4fv(GLint location, GLsizei count, const GLfloat* value) { ADD_COMMAND_GL(glUniform4fv); diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index e7a5e62df8..c87394869c 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -379,6 +379,8 @@ protected: void do_glUseProgram(Batch& batch, uint32 paramOffset); void do_glUniform1f(Batch& batch, uint32 paramOffset); void do_glUniform2f(Batch& batch, uint32 paramOffset); + void do_glUniform3f(Batch& batch, uint32 paramOffset); + void do_glUniform3fv(Batch& batch, uint32 paramOffset); void do_glUniform4fv(Batch& batch, uint32 paramOffset); void do_glUniformMatrix4fv(Batch& batch, uint32 paramOffset); diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index e0ea2f2d98..45adbcdb3c 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -61,7 +61,11 @@ void makeBindings(GLBackend::GLShader* shader) { if (loc >= 0) { glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "texcoord"); } - + loc = glGetAttribLocation(glprogram, "attribTexcoord"); + if (loc >= 0) { + glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "attribTexcoord"); + } + loc = glGetAttribLocation(glprogram, "tangent"); if (loc >= 0) { glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "tangent"); diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index a255a1f7c9..220ee31c65 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -14,9 +14,6 @@ #include #include -#include "SkyFromAtmosphere_vert.h" -#include "SkyFromAtmosphere_frag.h" - using namespace model; @@ -207,17 +204,6 @@ SunSkyStage::SunSkyStage() : // Begining of march setYearTime(60.0f); - auto skyFromAtmosphereVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(SkyFromAtmosphere_vert))); - auto skyFromAtmosphereFragment = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(SkyFromAtmosphere_frag))); - auto skyShader = gpu::ShaderPointer(gpu::Shader::createProgram(skyFromAtmosphereVertex, skyFromAtmosphereFragment)); - - auto skyState = gpu::StatePointer(new gpu::State()); - // skyState->setStencilEnable(false); - // skyState->setBlendEnable(false); - - _skyPipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState)); - - _skybox.reset(new Skybox()); _skybox->setColor(Color(1.0f, 0.0f, 0.0f)); @@ -306,12 +292,6 @@ void SunSkyStage::updateGraphicsObject() const { case NUM_BACKGROUND_MODES: Q_UNREACHABLE(); }; - - static int firstTime = 0; - if (firstTime == 0) { - firstTime++; - gpu::Shader::makeProgram(*(_skyPipeline->getProgram())); - } } void SunSkyStage::setBackgroundMode(BackgroundMode mode) { diff --git a/libraries/model/src/model/Stage.h b/libraries/model/src/model/Stage.h index fc90b0c903..b4df45e024 100644 --- a/libraries/model/src/model/Stage.h +++ b/libraries/model/src/model/Stage.h @@ -229,8 +229,6 @@ protected: AtmospherePointer _atmosphere; mutable SkyboxPointer _skybox; - gpu::PipelinePointer _skyPipeline; - float _dayTime = 12.0f; int _yearTime = 0; mutable EarthSunModel _earthSunModel; diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 642ca4748d..a8ed54fdd1 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -54,9 +54,33 @@ const QUrl AddressManager::currentAddress() const { void AddressManager::loadSettings(const QString& lookupString) { if (lookupString.isEmpty()) { - handleLookupString(currentAddressHandle.get().toString()); + handleUrl(currentAddressHandle.get().toString(), LookupTrigger::StartupFromSettings); } else { - handleLookupString(lookupString); + handleUrl(lookupString, LookupTrigger::StartupFromSettings); + } +} + +void AddressManager::goBack() { + if (_backStack.size() > 0) { + // go to that address + handleUrl(_backStack.pop(), LookupTrigger::Back); + + if (_backStack.size() == 0) { + // the back stack is now empty so it is no longer possible to go back - emit that signal + emit goBackPossible(false); + } + } +} + +void AddressManager::goForward() { + if (_forwardStack.size() > 0) { + // pop a URL from the forwardStack and go to that address + handleUrl(_forwardStack.pop(), LookupTrigger::Forward); + + if (_forwardStack.size() == 0) { + // the forward stack is empty so it is no longer possible to go forwards - emit that signal + emit goForwardPossible(false); + } } } @@ -102,7 +126,7 @@ const JSONCallbackParameters& AddressManager::apiCallbackParameters() { return callbackParams; } -bool AddressManager::handleUrl(const QUrl& lookupUrl) { +bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { if (lookupUrl.scheme() == HIFI_URL_SCHEME) { qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString(); @@ -121,17 +145,17 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { // we're assuming this is either a network address or global place name // check if it is a network address first if (handleNetworkAddress(lookupUrl.host() - + (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())))) { + + (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())), trigger)) { // we may have a path that defines a relative viewpoint - if so we should jump to that now - handlePath(lookupUrl.path()); + handlePath(lookupUrl.path(), trigger); } else if (handleDomainID(lookupUrl.host())){ // no place name - this is probably a domain ID // try to look up the domain ID on the metaverse API - attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path()); + attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path(), trigger); } else { // wasn't an address - lookup the place name // we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after - attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path()); + attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path(), trigger); } } @@ -140,8 +164,10 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { qCDebug(networking) << "Going to relative path" << lookupUrl.path(); // if this is a relative path then handle it as a relative viewpoint - handlePath(lookupUrl.path()); + handlePath(lookupUrl.path(), trigger, true); emit lookupResultsFinished(); + + return true; } return false; @@ -183,6 +209,7 @@ void AddressManager::handleAPIResponse(QNetworkReply& requestReply) { } const char OVERRIDE_PATH_KEY[] = "override_path"; +const char LOOKUP_TRIGGER_KEY[] = "lookup_trigger"; void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const QNetworkReply& reply) { @@ -218,7 +245,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const const QString DOMAIN_NETWORK_PORT_KEY = "network_port"; const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address"; - DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress); const QString DOMAIN_ID_KEY = "id"; QString domainIDString = domainObject[DOMAIN_ID_KEY].toString(); @@ -243,6 +270,8 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID); } + LookupTrigger trigger = (LookupTrigger) reply.property(LOOKUP_TRIGGER_KEY).toInt(); + // set our current root place id to the ID that came back const QString PLACE_ID_KEY = "id"; _rootPlaceID = rootMap[PLACE_ID_KEY].toUuid(); @@ -251,16 +280,16 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const const QString PLACE_NAME_KEY = "name"; QString placeName = rootMap[PLACE_NAME_KEY].toString(); if (!placeName.isEmpty()) { - setHost(placeName); + setHost(placeName, trigger); } else { - setHost(domainIDString); + setHost(domainIDString, trigger); } // check if we had a path to override the path returned QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString(); if (!overridePath.isEmpty()) { - handlePath(overridePath); + handlePath(overridePath, trigger); } else { // take the path that came back const QString PLACE_PATH_KEY = "path"; @@ -269,10 +298,14 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const bool shouldFaceViewpoint = locationMap.contains(LOCATION_API_ONLINE_KEY); if (!returnedPath.isEmpty()) { - // try to parse this returned path as a viewpoint, that's the only thing it could be for now - if (!handleViewpoint(returnedPath, shouldFaceViewpoint)) { - qCDebug(networking) << "Received a location path that was could not be handled as a viewpoint -" - << returnedPath; + if (shouldFaceViewpoint) { + // try to parse this returned path as a viewpoint, that's the only thing it could be for now + if (!handleViewpoint(returnedPath, shouldFaceViewpoint)) { + qCDebug(networking) << "Received a location path that was could not be handled as a viewpoint -" + << returnedPath; + } + } else { + handlePath(returnedPath, trigger); } } else { // we didn't override the path or get one back - ask the DS for the viewpoint of its index path @@ -306,15 +339,20 @@ void AddressManager::handleAPIError(QNetworkReply& errorReply) { const QString GET_PLACE = "/api/v1/places/%1"; -void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath) { +void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger) { // assume this is a place name and see if we can get any info on it QString placeName = QUrl::toPercentEncoding(lookupString); QVariantMap requestParams; + + // if the user asked for a specific path with this lookup then keep it with the request so we can use it later if (!overridePath.isEmpty()) { requestParams.insert(OVERRIDE_PATH_KEY, overridePath); } + // remember how this lookup was triggered for history storage handling later + requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast(trigger)); + AccountManager::getInstance().sendRequest(GET_PLACE.arg(placeName), AccountManagerAuth::None, QNetworkAccessManager::GetOperation, @@ -324,15 +362,20 @@ void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const Q const QString GET_DOMAIN_ID = "/api/v1/domains/%1"; -void AddressManager::attemptDomainIDLookup(const QString& lookupString, const QString& overridePath) { +void AddressManager::attemptDomainIDLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger) { // assume this is a domain ID and see if we can get any info on it QString domainID = QUrl::toPercentEncoding(lookupString); QVariantMap requestParams; + + // if the user asked for a specific path with this lookup then keep it with the request so we can use it later if (!overridePath.isEmpty()) { requestParams.insert(OVERRIDE_PATH_KEY, overridePath); } + // remember how this lookup was triggered for history storage handling later + requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast(trigger)); + AccountManager::getInstance().sendRequest(GET_DOMAIN_ID.arg(domainID), AccountManagerAuth::None, QNetworkAccessManager::GetOperation, @@ -340,7 +383,7 @@ void AddressManager::attemptDomainIDLookup(const QString& lookupString, const QS QByteArray(), NULL, requestParams); } -bool AddressManager::handleNetworkAddress(const QString& lookupString) { +bool AddressManager::handleNetworkAddress(const QString& lookupString, LookupTrigger trigger) { const QString IP_ADDRESS_REGEX_STRING = "^((?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" "(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))(?::(\\d{1,5}))?$"; @@ -358,7 +401,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) { } emit lookupResultsFinished(); - setDomainInfo(domainIPString, domainPort); + setDomainInfo(domainIPString, domainPort, trigger); return true; } @@ -375,7 +418,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) { } emit lookupResultsFinished(); - setDomainInfo(domainHostname, domainPort); + setDomainInfo(domainHostname, domainPort, trigger); return true; } @@ -391,15 +434,26 @@ bool AddressManager::handleDomainID(const QString& host) { return (domainIDRegex.indexIn(host) != -1); } -void AddressManager::handlePath(const QString& path) { - if (!handleViewpoint(path)) { +void AddressManager::handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly) { + if (!handleViewpoint(path, false, wasPathOnly)) { qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << path << "- wll attempt to ask domain-server to resolve."; + + if (!wasPathOnly) { + // if we received a path with a host then we need to remember what it was here so we can not + // double set add to the history stack once handle viewpoint is called with the result + _newHostLookupPath = path; + } else { + // clear the _newHostLookupPath so it doesn't match when this return comes in + _newHostLookupPath = QString(); + } + emit pathChangeRequired(path); } } -bool AddressManager::handleViewpoint(const QString& viewpointString, bool shouldFace) { +bool AddressManager::handleViewpoint(const QString& viewpointString, bool shouldFace, + bool definitelyPathOnly, const QString& pathString) { const QString FLOAT_REGEX_STRING = "([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)"; const QString SPACED_COMMA_REGEX_STRING = "\\s*,\\s*"; const QString POSITION_REGEX_STRING = QString("\\/") + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING + @@ -416,8 +470,18 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should positionRegex.cap(2).toFloat(), positionRegex.cap(3).toFloat()); - // we're about to jump positions - store the current address in our history - addCurrentAddressToHistory(); + // We need to use definitelyPathOnly, pathString and _newHostLookupPath to determine if the current address + // should be stored in the history before we ask for a position/orientation change. A relative path that was + // not associated with a host lookup should always trigger a history change (definitelyPathOnly) and a viewpointString + // with a non empty pathString (suggesting this is the result of a lookup with the domain-server) that does not match + // _newHostLookupPath should always trigger a history change. + // + // We use _newHostLookupPath to determine if the client has already stored its last address + // before moving to a new host thanks to the information in the same lookup URL. + + if (definitelyPathOnly || (!pathString.isEmpty() && pathString != _newHostLookupPath)) { + addCurrentAddressToHistory(LookupTrigger::UserInput); + } if (!isNaN(newPosition.x) && !isNaN(newPosition.y) && !isNaN(newPosition.z)) { glm::quat newOrientation; @@ -469,11 +533,11 @@ bool AddressManager::handleUsername(const QString& lookupString) { return false; } -void AddressManager::setHost(const QString& host) { +void AddressManager::setHost(const QString& host, LookupTrigger trigger) { if (host != _host) { // if the host is being changed we should store current address in the history - addCurrentAddressToHistory(); + addCurrentAddressToHistory(trigger); _host = host; emit hostChanged(_host); @@ -481,8 +545,8 @@ void AddressManager::setHost(const QString& host) { } -void AddressManager::setDomainInfo(const QString& hostname, quint16 port) { - setHost(hostname); +void AddressManager::setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger) { + setHost(hostname, trigger); _rootPlaceID = QUuid(); @@ -495,11 +559,17 @@ void AddressManager::setDomainInfo(const QString& hostname, quint16 port) { void AddressManager::goToUser(const QString& username) { QString formattedUsername = QUrl::toPercentEncoding(username); + + // for history storage handling we remember how this lookup was trigged - for a username it's always user input + QVariantMap requestParams; + requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast(LookupTrigger::UserInput)); + // this is a username - pull the captured name and lookup that user's location AccountManager::getInstance().sendRequest(GET_USER_LOCATION.arg(formattedUsername), AccountManagerAuth::Optional, QNetworkAccessManager::GetOperation, - apiCallbackParameters()); + apiCallbackParameters(), + QByteArray(), nullptr, requestParams); } void AddressManager::copyAddress() { @@ -510,21 +580,36 @@ void AddressManager::copyPath() { QApplication::clipboard()->setText(currentPath()); } -void AddressManager::addCurrentAddressToHistory() { - if (_lastHistoryAppend == 0) { - // we don't store the first address on application load - // just update the last append time so the next is stored - _lastHistoryAppend = usecTimestampNow(); - } else { - const quint64 DOUBLE_STORE_THRESHOLD_USECS = 500000; +void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) { - // avoid double storing when the host changes and the viewpoint changes immediately after - if (usecTimestampNow() - _lastHistoryAppend > DOUBLE_STORE_THRESHOLD_USECS) { - // add the current address to the history - _history.append(currentAddress()); + // if we're cold starting and this is called for the first address (from settings) we don't do anything + if (trigger != LookupTrigger::StartupFromSettings) { + if (trigger == LookupTrigger::UserInput) { + // anyime the user has manually looked up an address we know we should clear the forward stack + _forwardStack.clear(); - // change our last history append to now - _lastHistoryAppend = usecTimestampNow(); + emit goForwardPossible(false); + } + + if (trigger == LookupTrigger::Back) { + // we're about to push to the forward stack + // if it's currently empty emit our signal to say that going forward is now possible + if (_forwardStack.size() == 0) { + emit goForwardPossible(true); + } + + // when the user is going back, we move the current address to the forward stack + // and do not but it into the back stack + _forwardStack.push(currentAddress()); + } else { + // we're about to push to the back stack + // if it's currently empty emit our signal to say that going forward is now possible + if (_forwardStack.size() == 0) { + emit goBackPossible(true); + } + + // unless this was triggered from the result of a named path lookup, add the current address to the history + _backStack.push(currentAddress()); } } } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index d950ae0275..b4c34176a4 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -12,8 +12,8 @@ #ifndef hifi_AddressManager_h #define hifi_AddressManager_h -#include -#include +#include +#include #include #include @@ -38,6 +38,14 @@ class AddressManager : public QObject, public Dependency { Q_PROPERTY(QString hostname READ getHost) Q_PROPERTY(QString pathname READ currentPath) public: + + enum LookupTrigger { + UserInput, + Back, + Forward, + StartupFromSettings + }; + bool isConnected(); const QString& getProtocol() { return HIFI_URL_SCHEME; }; @@ -47,10 +55,7 @@ public: const QUuid& getRootPlaceID() const { return _rootPlaceID; } const QString& getHost() const { return _host; } - void setHost(const QString& host); - void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath = QString()); - void attemptDomainIDLookup(const QString& lookupString, const QString& overridePath = QString()); void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; } void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; } @@ -60,9 +65,14 @@ public: public slots: void handleLookupString(const QString& lookupString); + // we currently expect this to be called from NodeList once handleLookupString has been called with a path + bool goToViewpointForPath(const QString& viewpointString, const QString& pathString) + { return handleViewpoint(viewpointString, false, false, pathString); } + + void goBack(); + void goForward(); + void goToUser(const QString& username); - void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply); - bool goToViewpoint(const QString& viewpointString) { return handleViewpoint(viewpointString); } void storeCurrentAddress(); @@ -73,40 +83,56 @@ signals: void lookupResultsFinished(); void lookupResultIsOffline(); void lookupResultIsNotFound(); + void possibleDomainChangeRequired(const QString& newHostname, quint16 newPort); void possibleDomainChangeRequiredViaICEForID(const QString& iceServerHostname, const QUuid& domainID); + void locationChangeRequired(const glm::vec3& newPosition, bool hasOrientationChange, const glm::quat& newOrientation, bool shouldFaceLocation); void pathChangeRequired(const QString& newPath); void hostChanged(const QString& newHost); + + void goBackPossible(bool isPossible); + void goForwardPossible(bool isPossible); + protected: AddressManager(); private slots: void handleAPIResponse(QNetworkReply& requestReply); void handleAPIError(QNetworkReply& errorReply); + + void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply); private: - void setDomainInfo(const QString& hostname, quint16 port); + void setHost(const QString& host, LookupTrigger trigger); + void setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger); const JSONCallbackParameters& apiCallbackParameters(); - bool handleUrl(const QUrl& lookupUrl); + bool handleUrl(const QUrl& lookupUrl, LookupTrigger trigger = UserInput); - bool handleNetworkAddress(const QString& lookupString); - void handlePath(const QString& path); - bool handleViewpoint(const QString& viewpointString, bool shouldFace = false); + bool handleNetworkAddress(const QString& lookupString, LookupTrigger trigger); + void handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly = false); + bool handleViewpoint(const QString& viewpointString, bool shouldFace = false, + bool definitelyPathOnly = false, const QString& pathString = QString()); bool handleUsername(const QString& lookupString); bool handleDomainID(const QString& host); - void addCurrentAddressToHistory(); + void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger); + void attemptDomainIDLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger); + + void addCurrentAddressToHistory(LookupTrigger trigger); QString _host; QUuid _rootPlaceID; PositionGetter _positionGetter; OrientationGetter _orientationGetter; - QList _history; - quint64 _lastHistoryAppend = 0; + QStack _backStack; + QStack _forwardStack; + quint64 _lastBackPush = 0; + + QString _newHostLookupPath; }; #endif // hifi_AddressManager_h diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 44aa5bc644..e7bb4fbb6f 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -467,7 +467,7 @@ void NodeList::handleDSPathQueryResponse(const QByteArray& packet) { QString viewpoint = QString::fromUtf8(currentPosition, numViewpointBytes); // Hand it off to the AddressManager so it can handle it as a relative viewpoint - if (DependencyManager::get()->goToViewpoint(viewpoint)) { + if (DependencyManager::get()->goToViewpointForPath(viewpoint, pathQuery)) { qCDebug(networking) << "Going to viewpoint" << viewpoint << "which was the lookup result for path" << pathQuery; } else { qCDebug(networking) << "Could not go to viewpoint" << viewpoint diff --git a/interface/src/Environment.cpp b/libraries/render-utils/src/Environment.cpp similarity index 57% rename from interface/src/Environment.cpp rename to libraries/render-utils/src/Environment.cpp index 9f197920d9..411beca0ae 100644 --- a/interface/src/Environment.cpp +++ b/libraries/render-utils/src/Environment.cpp @@ -9,25 +9,25 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "InterfaceConfig.h" - #include #include #include +#include "GeometryCache.h" #include +#include +#include #include #include -#include #include -#include "Application.h" -#include "Camera.h" -#include "world.h" -#include "InterfaceLogging.h" - #include "Environment.h" +#include "SkyFromSpace_vert.h" +#include "SkyFromSpace_frag.h" +#include "SkyFromAtmosphere_vert.h" +#include "SkyFromAtmosphere_frag.h" + uint qHash(const HifiSockAddr& sockAddr) { if (sockAddr.getAddress().isNull()) { return 0; // shouldn't happen, but if it does, zero is a perfectly valid hash @@ -42,20 +42,15 @@ Environment::Environment() } Environment::~Environment() { - if (_initialized) { - delete _skyFromAtmosphereProgram; - delete _skyFromSpaceProgram; - } } void Environment::init() { if (_initialized) { - qCDebug(interfaceapp, "[ERROR] Environment is already initialized."); return; } - _skyFromAtmosphereProgram = createSkyProgram("Atmosphere", _skyFromAtmosphereUniformLocations); - _skyFromSpaceProgram = createSkyProgram("Space", _skyFromSpaceUniformLocations); + setupAtmosphereProgram(SkyFromSpace_vert, SkyFromSpace_frag, _skyFromSpaceProgram, _skyFromSpaceUniformLocations); + setupAtmosphereProgram(SkyFromAtmosphere_vert, SkyFromAtmosphere_frag, _skyFromAtmosphereProgram, _skyFromAtmosphereUniformLocations); // start off with a default-constructed environment data _data[HifiSockAddr()][0]; @@ -63,22 +58,60 @@ void Environment::init() { _initialized = true; } +void Environment::setupAtmosphereProgram(const char* vertSource, const char* fragSource, gpu::PipelinePointer& pipeline, int* locations) { + + auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(vertSource))); + auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fragSource))); + + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS)); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + state->setCullMode(gpu::State::CULL_NONE); + state->setDepthTest(false); + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + + pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + + locations[CAMERA_POS_LOCATION] = program->getUniforms().findLocation("v3CameraPos"); + locations[LIGHT_POS_LOCATION] = program->getUniforms().findLocation("v3LightPos"); + locations[INV_WAVELENGTH_LOCATION] = program->getUniforms().findLocation("v3InvWavelength"); + locations[CAMERA_HEIGHT2_LOCATION] = program->getUniforms().findLocation("fCameraHeight2"); + locations[OUTER_RADIUS_LOCATION] = program->getUniforms().findLocation("fOuterRadius"); + locations[OUTER_RADIUS2_LOCATION] = program->getUniforms().findLocation("fOuterRadius2"); + locations[INNER_RADIUS_LOCATION] = program->getUniforms().findLocation("fInnerRadius"); + locations[KR_ESUN_LOCATION] = program->getUniforms().findLocation("fKrESun"); + locations[KM_ESUN_LOCATION] = program->getUniforms().findLocation("fKmESun"); + locations[KR_4PI_LOCATION] = program->getUniforms().findLocation("fKr4PI"); + locations[KM_4PI_LOCATION] = program->getUniforms().findLocation("fKm4PI"); + locations[SCALE_LOCATION] = program->getUniforms().findLocation("fScale"); + locations[SCALE_DEPTH_LOCATION] = program->getUniforms().findLocation("fScaleDepth"); + locations[SCALE_OVER_SCALE_DEPTH_LOCATION] = program->getUniforms().findLocation("fScaleOverScaleDepth"); + locations[G_LOCATION] = program->getUniforms().findLocation("g"); + locations[G2_LOCATION] = program->getUniforms().findLocation("g2"); +} + void Environment::resetToDefault() { _data.clear(); _data[HifiSockAddr()][0]; } -void Environment::renderAtmospheres(ViewFrustum& camera) { +void Environment::renderAtmospheres(gpu::Batch& batch, ViewFrustum& camera) { // get the lock for the duration of the call QMutexLocker locker(&_mutex); if (_environmentIsOverridden) { - renderAtmosphere(camera, _overrideData); + renderAtmosphere(batch, camera, _overrideData); } else { foreach (const ServerData& serverData, _data) { // TODO: do something about EnvironmentData foreach (const EnvironmentData& environmentData, serverData) { - renderAtmosphere(camera, environmentData); + renderAtmosphere(batch, camera, environmentData); } } } @@ -201,87 +234,51 @@ int Environment::parseData(const HifiSockAddr& senderAddress, const QByteArray& return bytesRead; } -ProgramObject* Environment::createSkyProgram(const char* from, int* locations) { - ProgramObject* program = new ProgramObject(); - QByteArray prefix = QString(PathUtils::resourcesPath() + "/shaders/SkyFrom" + from).toUtf8(); - program->addShaderFromSourceFile(QGLShader::Vertex, prefix + ".vert"); - program->addShaderFromSourceFile(QGLShader::Fragment, prefix + ".frag"); - program->link(); - - locations[CAMERA_POS_LOCATION] = program->uniformLocation("v3CameraPos"); - locations[LIGHT_POS_LOCATION] = program->uniformLocation("v3LightPos"); - locations[INV_WAVELENGTH_LOCATION] = program->uniformLocation("v3InvWavelength"); - locations[CAMERA_HEIGHT2_LOCATION] = program->uniformLocation("fCameraHeight2"); - locations[OUTER_RADIUS_LOCATION] = program->uniformLocation("fOuterRadius"); - locations[OUTER_RADIUS2_LOCATION] = program->uniformLocation("fOuterRadius2"); - locations[INNER_RADIUS_LOCATION] = program->uniformLocation("fInnerRadius"); - locations[KR_ESUN_LOCATION] = program->uniformLocation("fKrESun"); - locations[KM_ESUN_LOCATION] = program->uniformLocation("fKmESun"); - locations[KR_4PI_LOCATION] = program->uniformLocation("fKr4PI"); - locations[KM_4PI_LOCATION] = program->uniformLocation("fKm4PI"); - locations[SCALE_LOCATION] = program->uniformLocation("fScale"); - locations[SCALE_DEPTH_LOCATION] = program->uniformLocation("fScaleDepth"); - locations[SCALE_OVER_SCALE_DEPTH_LOCATION] = program->uniformLocation("fScaleOverScaleDepth"); - locations[G_LOCATION] = program->uniformLocation("g"); - locations[G2_LOCATION] = program->uniformLocation("g2"); - - return program; -} +void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data) { -void Environment::renderAtmosphere(ViewFrustum& camera, const EnvironmentData& data) { glm::vec3 center = data.getAtmosphereCenter(); - glPushMatrix(); - glTranslatef(center.x, center.y, center.z); + Transform transform; + transform.setTranslation(center); + batch.setModelTransform(transform); glm::vec3 relativeCameraPos = camera.getPosition() - center; float height = glm::length(relativeCameraPos); // use the appropriate shader depending on whether we're inside or outside - ProgramObject* program; int* locations; if (height < data.getAtmosphereOuterRadius()) { - program = _skyFromAtmosphereProgram; + batch.setPipeline(_skyFromAtmosphereProgram); locations = _skyFromAtmosphereUniformLocations; } else { - program = _skyFromSpaceProgram; + batch.setPipeline(_skyFromSpaceProgram); locations = _skyFromSpaceUniformLocations; } // the constants here are from Sean O'Neil's GPU Gems entry // (http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html), GameEngine.cpp - program->bind(); - program->setUniform(locations[CAMERA_POS_LOCATION], relativeCameraPos); + batch._glUniform3f(locations[CAMERA_POS_LOCATION], relativeCameraPos.x, relativeCameraPos.y, relativeCameraPos.z); glm::vec3 lightDirection = glm::normalize(data.getSunLocation()); - program->setUniform(locations[LIGHT_POS_LOCATION], lightDirection); - program->setUniformValue(locations[INV_WAVELENGTH_LOCATION], - 1 / powf(data.getScatteringWavelengths().r, 4.0f), - 1 / powf(data.getScatteringWavelengths().g, 4.0f), - 1 / powf(data.getScatteringWavelengths().b, 4.0f)); - program->setUniformValue(locations[CAMERA_HEIGHT2_LOCATION], height * height); - program->setUniformValue(locations[OUTER_RADIUS_LOCATION], data.getAtmosphereOuterRadius()); - program->setUniformValue(locations[OUTER_RADIUS2_LOCATION], data.getAtmosphereOuterRadius() * data.getAtmosphereOuterRadius()); - program->setUniformValue(locations[INNER_RADIUS_LOCATION], data.getAtmosphereInnerRadius()); - program->setUniformValue(locations[KR_ESUN_LOCATION], data.getRayleighScattering() * data.getSunBrightness()); - program->setUniformValue(locations[KM_ESUN_LOCATION], data.getMieScattering() * data.getSunBrightness()); - program->setUniformValue(locations[KR_4PI_LOCATION], data.getRayleighScattering() * 4.0f * PI); - program->setUniformValue(locations[KM_4PI_LOCATION], data.getMieScattering() * 4.0f * PI); - program->setUniformValue(locations[SCALE_LOCATION], 1.0f / (data.getAtmosphereOuterRadius() - data.getAtmosphereInnerRadius())); - program->setUniformValue(locations[SCALE_DEPTH_LOCATION], 0.25f); - program->setUniformValue(locations[SCALE_OVER_SCALE_DEPTH_LOCATION], + batch._glUniform3f(locations[LIGHT_POS_LOCATION], lightDirection.x, lightDirection.y, lightDirection.z); + batch._glUniform3f(locations[INV_WAVELENGTH_LOCATION], + 1 / powf(data.getScatteringWavelengths().r, 4.0f), + 1 / powf(data.getScatteringWavelengths().g, 4.0f), + 1 / powf(data.getScatteringWavelengths().b, 4.0f)); + batch._glUniform1f(locations[CAMERA_HEIGHT2_LOCATION], height * height); + batch._glUniform1f(locations[OUTER_RADIUS_LOCATION], data.getAtmosphereOuterRadius()); + batch._glUniform1f(locations[OUTER_RADIUS2_LOCATION], data.getAtmosphereOuterRadius() * data.getAtmosphereOuterRadius()); + batch._glUniform1f(locations[INNER_RADIUS_LOCATION], data.getAtmosphereInnerRadius()); + batch._glUniform1f(locations[KR_ESUN_LOCATION], data.getRayleighScattering() * data.getSunBrightness()); + batch._glUniform1f(locations[KM_ESUN_LOCATION], data.getMieScattering() * data.getSunBrightness()); + batch._glUniform1f(locations[KR_4PI_LOCATION], data.getRayleighScattering() * 4.0f * PI); + batch._glUniform1f(locations[KM_4PI_LOCATION], data.getMieScattering() * 4.0f * PI); + batch._glUniform1f(locations[SCALE_LOCATION], 1.0f / (data.getAtmosphereOuterRadius() - data.getAtmosphereInnerRadius())); + batch._glUniform1f(locations[SCALE_DEPTH_LOCATION], 0.25f); + batch._glUniform1f(locations[SCALE_OVER_SCALE_DEPTH_LOCATION], (1.0f / (data.getAtmosphereOuterRadius() - data.getAtmosphereInnerRadius())) / 0.25f); - program->setUniformValue(locations[G_LOCATION], -0.990f); - program->setUniformValue(locations[G2_LOCATION], -0.990f * -0.990f); + batch._glUniform1f(locations[G_LOCATION], -0.990f); + batch._glUniform1f(locations[G2_LOCATION], -0.990f * -0.990f); - glDepthMask(GL_FALSE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); - glEnable(GL_BLEND); - DependencyManager::get()->renderSphere(1.0f, 100, 50, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); //Draw a unit sphere - glDepthMask(GL_TRUE); - - program->release(); - - glPopMatrix(); + DependencyManager::get()->renderSphere(batch,1.0f, 100, 50, glm::vec4(1.0f, 0.0f, 0.0f, 0.5f)); //Draw a unit sphere } diff --git a/interface/src/Environment.h b/libraries/render-utils/src/Environment.h similarity index 81% rename from interface/src/Environment.h rename to libraries/render-utils/src/Environment.h index c1b4171947..65e0df4b36 100644 --- a/interface/src/Environment.h +++ b/libraries/render-utils/src/Environment.h @@ -16,8 +16,9 @@ #include #include +#include -#include "EnvironmentData.h" +#include class ViewFrustum; class ProgramObject; @@ -29,7 +30,7 @@ public: void init(); void resetToDefault(); - void renderAtmospheres(ViewFrustum& camera); + void renderAtmospheres(gpu::Batch& batch, ViewFrustum& camera); void override(const EnvironmentData& overrideData) { _overrideData = overrideData; _environmentIsOverridden = true; } void endOverride() { _environmentIsOverridden = false; } @@ -44,14 +45,10 @@ private: bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration); // NOTE: Deprecated - ProgramObject* createSkyProgram(const char* from, int* locations); - - void renderAtmosphere(ViewFrustum& camera, const EnvironmentData& data); + void renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data); bool _initialized; - ProgramObject* _skyFromAtmosphereProgram; - ProgramObject* _skyFromSpaceProgram; - + enum { CAMERA_POS_LOCATION, LIGHT_POS_LOCATION, @@ -72,6 +69,11 @@ private: LOCATION_COUNT }; + void setupAtmosphereProgram(const char* vertSource, const char* fragSource, gpu::PipelinePointer& pipelineProgram, int* locations); + + + gpu::PipelinePointer _skyFromAtmosphereProgram; + gpu::PipelinePointer _skyFromSpaceProgram; int _skyFromAtmosphereUniformLocations[LOCATION_COUNT]; int _skyFromSpaceUniformLocations[LOCATION_COUNT]; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 8d234cdef5..803673a06e 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -82,6 +82,7 @@ Model::Model(QObject* parent) : _isVisible(true), _blendNumber(0), _appliedBlendNumber(0), + _calculatedMeshPartOffsetValid(false), _calculatedMeshPartBoxesValid(false), _calculatedMeshBoxesValid(false), _calculatedMeshTrianglesValid(false), @@ -544,6 +545,10 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g // we can use the AABox's ray intersection by mapping our origin and direction into the model frame // and testing intersection there. if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face)) { + + if (!_calculatedMeshBoxesValid) { + recalculateMeshBoxes(pickAgainstTriangles); + } float bestDistance = std::numeric_limits::max(); @@ -660,6 +665,25 @@ bool Model::convexHullContains(glm::vec3 point) { return false; } +void Model::recalculateMeshPartOffsets() { + if (!_calculatedMeshPartOffsetValid) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + int numberOfMeshes = geometry.meshes.size(); + _calculatedMeshPartOffset.clear(); + for (int i = 0; i < numberOfMeshes; i++) { + const FBXMesh& mesh = geometry.meshes.at(i); + qint64 partOffset = 0; + for (int j = 0; j < mesh.parts.size(); j++) { + const FBXMeshPart& part = mesh.parts.at(j); + _calculatedMeshPartOffset[QPair(i, j)] = partOffset; + partOffset += part.quadIndices.size() * sizeof(int); + partOffset += part.triangleIndices.size() * sizeof(int); + + } + } + _calculatedMeshPartOffsetValid = true; + } +} // TODO: we seem to call this too often when things haven't actually changed... look into optimizing this // Any script might trigger findRayIntersectionAgainstSubMeshes (and maybe convexHullContains), so these // can occur multiple times. In addition, rendering does it's own ray picking in order to decide which @@ -675,7 +699,8 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { _calculatedMeshTriangles.clear(); _calculatedMeshTriangles.resize(numberOfMeshes); _calculatedMeshPartBoxes.clear(); - _calculatedMeshPartOffet.clear(); + _calculatedMeshPartOffset.clear(); + _calculatedMeshPartOffsetValid = false; for (int i = 0; i < numberOfMeshes; i++) { const FBXMesh& mesh = geometry.meshes.at(i); Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents); @@ -770,7 +795,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { } } _calculatedMeshPartBoxes[QPair(i, j)] = thisPartBounds; - _calculatedMeshPartOffet[QPair(i, j)] = partOffset; + _calculatedMeshPartOffset[QPair(i, j)] = partOffset; partOffset += part.quadIndices.size() * sizeof(int); partOffset += part.triangleIndices.size() * sizeof(int); @@ -778,6 +803,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { } _calculatedMeshTriangles[i] = thisMeshTriangles; _calculatedMeshPartBoxesValid = true; + _calculatedMeshPartOffsetValid = true; } } _calculatedMeshBoxesValid = true; @@ -786,16 +812,6 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { } void Model::renderSetup(RenderArgs* args) { - // if we don't have valid mesh boxes, calculate them now, this only matters in cases - // where our caller has passed RenderArgs which will include a view frustum we can cull - // against. We cache the results of these calculations so long as the model hasn't been - // simulated and the mesh hasn't changed. - if (args && !_calculatedMeshBoxesValid) { - _mutex.lock(); - recalculateMeshBoxes(); - _mutex.unlock(); - } - // set up dilated textures on first render after load/simulate const FBXGeometry& geometry = _geometry->getFBXGeometry(); if (_dilatedTextures.isEmpty()) { @@ -1342,18 +1358,15 @@ void Model::snapToRegistrationPoint() { } void Model::simulate(float deltaTime, bool fullUpdate) { - /* - qDebug() << "Model::simulate()"; - qDebug() << " _translation:" << _translation; - qDebug() << " _rotation:" << _rotation; - */ - fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint); if (isActive() && fullUpdate) { - // NOTE: this seems problematic... need to review - _calculatedMeshBoxesValid = false; // if we have to simulate, we need to assume our mesh boxes are all invalid + // NOTE: This is overly aggressive and we are invalidating the MeshBoxes when in fact they may not be invalid + // they really only become invalid if something about the transform to world space has changed. This is + // not too bad at this point, because it doesn't impact rendering. However it does slow down ray picking + // because ray picking needs valid boxes to work + _calculatedMeshBoxesValid = false; _calculatedMeshTrianglesValid = false; // check for scale to fit @@ -1761,11 +1774,31 @@ void Model::setupBatchTransform(gpu::Batch& batch, RenderArgs* args) { } AABox Model::getPartBounds(int meshIndex, int partIndex) { - if (!_calculatedMeshPartBoxesValid) { - recalculateMeshBoxes(true); + if (meshIndex < _meshStates.size()) { + const MeshState& state = _meshStates.at(meshIndex); + bool isSkinned = state.clusterMatrices.size() > 1; + if (isSkinned) { + // if we're skinned return the entire mesh extents because we can't know for sure our clusters don't move us + return calculateScaledOffsetAABox(_geometry->getFBXGeometry().meshExtents); + } } - if (_calculatedMeshPartBoxesValid && _calculatedMeshPartBoxes.contains(QPair(meshIndex, partIndex))) { - return calculateScaledOffsetAABox(_calculatedMeshPartBoxes[QPair(meshIndex, partIndex)]); + + if (_geometry->getFBXGeometry().meshes.size() > meshIndex) { + + // FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding + // box of the mesh part fails. It seems to create boxes that are not consistent with where the + // geometry actually renders. If instead we make all the parts share the bounds of the entire subMesh + // things will render properly. + // + // return calculateScaledOffsetAABox(_calculatedMeshPartBoxes[QPair(meshIndex, partIndex)]); + // + // NOTE: we also don't want to use the _calculatedMeshBoxes[] because they don't handle avatar moving correctly + // without recalculating them... + // return _calculatedMeshBoxes[meshIndex]; + // + // If we not skinned use the bounds of the subMesh for all it's parts + const FBXMesh& mesh = _geometry->getFBXGeometry().meshes.at(meshIndex); + return calculateScaledOffsetExtents(mesh.meshExtents); } return AABox(); } @@ -1775,29 +1808,15 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran return; // bail asap } - // we always need these properly calculated before we can render, this will likely already have been done - // since the engine will call our getPartBounds() before rendering us. - if (!_calculatedMeshPartBoxesValid) { - recalculateMeshBoxes(true); + // We need to make sure we have valid offsets calculated before we can render + if (!_calculatedMeshPartOffsetValid) { + recalculateMeshPartOffsets(); } auto textureCache = DependencyManager::get(); gpu::Batch& batch = *(args->_batch); auto mode = args->_renderMode; - // render the part bounding box - #ifdef DEBUG_BOUNDING_PARTS - { - glm::vec4 cubeColor(1.0f,0.0f,0.0f,1.0f); - AABox partBounds = getPartBounds(meshIndex, partIndex); - - glm::mat4 translation = glm::translate(partBounds.calcCenter()); - glm::mat4 scale = glm::scale(partBounds.getDimensions()); - glm::mat4 modelToWorldMatrix = translation * scale; - batch.setModelTransform(modelToWorldMatrix); - DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); - } - #endif //def DEBUG_BOUNDING_PARTS // Capture the view matrix once for the rendering of this model if (_transforms.empty()) { @@ -1824,6 +1843,29 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran bool hasLightmap = mesh.hasEmissiveTexture(); bool isSkinned = state.clusterMatrices.size() > 1; bool wireframe = isWireframe(); + + // render the part bounding box + #ifdef DEBUG_BOUNDING_PARTS + { + AABox partBounds = getPartBounds(meshIndex, partIndex); + bool inView = args->_viewFrustum->boxInFrustum(partBounds) != ViewFrustum::OUTSIDE; + + glm::vec4 cubeColor; + if (isSkinned) { + cubeColor = glm::vec4(0.0f, 1.0f, 1.0f, 1.0f); + } else if (inView) { + cubeColor = glm::vec4(1.0f, 0.0f, 1.0f, 1.0f); + } else { + cubeColor = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f); + } + + Transform transform; + transform.setTranslation(partBounds.calcCenter()); + transform.setScale(partBounds.getDimensions()); + batch.setModelTransform(transform); + DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); + } + #endif //def DEBUG_BOUNDING_PARTS if (wireframe) { translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; @@ -1976,8 +2018,8 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } } - qint64 offset = _calculatedMeshPartOffet[QPair(meshIndex, partIndex)]; - + qint64 offset = _calculatedMeshPartOffset[QPair(meshIndex, partIndex)]; + if (part.quadIndices.size() > 0) { batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset); offset += part.quadIndices.size() * sizeof(int); @@ -2047,6 +2089,9 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f Locations*& locations) { RenderKey key(mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); + if (mode == RenderArgs::MIRROR_RENDER_MODE) { + key = RenderKey(key.getRaw() | RenderKey::IS_MIRROR); + } auto pipeline = _renderPipelineLib.find(key.getRaw()); if (pipeline == _renderPipelineLib.end()) { qDebug() << "No good, couldn't find a pipeline from the key ?" << key.getRaw(); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 7c1572418e..6dfe223581 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -369,7 +369,8 @@ private: }; QHash, AABox> _calculatedMeshPartBoxes; // world coordinate AABoxes for all sub mesh part boxes - QHash, qint64> _calculatedMeshPartOffet; + QHash, qint64> _calculatedMeshPartOffset; + bool _calculatedMeshPartOffsetValid; bool _calculatedMeshPartBoxesValid; @@ -381,6 +382,7 @@ private: QMutex _mutex; void recalculateMeshBoxes(bool pickAgainstTriangles = false); + void recalculateMeshPartOffsets(); void segregateMeshGroups(); // used to calculate our list of translucent vs opaque meshes diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 3fd7d05666..ce3c6769ca 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -15,9 +15,12 @@ #include "DeferredLightingEffect.h" #include "ViewFrustum.h" #include "RenderArgs.h" +#include "TextureCache.h" #include +#include "overlay3D_vert.h" +#include "overlay3D_frag.h" using namespace render; @@ -50,7 +53,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.push_back(Job(RenderDeferred())); _jobs.push_back(Job(ResolveDeferred())); _jobs.push_back(Job(DrawTransparentDeferred())); - _jobs.push_back(Job(DrawPostLayered())); + _jobs.push_back(Job(DrawOverlay3D())); _jobs.push_back(Job(ResetGLState())); } @@ -136,10 +139,12 @@ template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContex Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; { GLenum buffers[3]; int bufferCount = 0; @@ -204,11 +209,12 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; - const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f; { @@ -222,10 +228,76 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC renderItems(sceneContext, renderContext, renderedItems, renderContext->_maxDrawnTransparentItems); + // Before rendering the batch make sure we re in sync with gl state + args->_context->syncCache(); args->_context->render((*args->_batch)); args->_batch = nullptr; // reset blend function to standard... - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + // glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); } } + +const gpu::PipelinePointer& DrawOverlay3D::getOpaquePipeline() const { + if (!_opaquePipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(overlay3D_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(overlay3D_frag))); + + auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + + auto state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + + _opaquePipeline.reset(gpu::Pipeline::create(program, state)); + } + return _opaquePipeline; +} + +template <> void render::jobRun(const DrawOverlay3D& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("DrawOverlay3D"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + // render backgrounds + auto& scene = sceneContext->_scene; + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withLayered()); + + + ItemIDsBounds inItems; + inItems.reserve(items.size()); + for (auto id : items) { + auto& item = scene->getItem(id); + if (item.getKey().isVisible() && (item.getLayer() == 1)) { + inItems.emplace_back(id); + } + } + + RenderArgs* args = renderContext->args; + gpu::Batch batch; + args->_batch = &batch; + args->_whiteTexture = DependencyManager::get()->getWhiteTexture(); + + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setPipeline(job.getOpaquePipeline()); + batch.setUniformTexture(0, args->_whiteTexture); + + if (!inItems.empty()) { + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0); + renderItems(sceneContext, renderContext, inItems); + } + + // Before rendering the batch make sure we re in sync with gl state + args->_context->syncCache(); + args->_context->render((*args->_batch)); + args->_batch = nullptr; + args->_whiteTexture.reset(); +} diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index e2cac53c0d..3b0ffdfc9b 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -14,6 +14,8 @@ #include "render/DrawTask.h" +#include "gpu/Pipeline.h" + class PrepareDeferred { public: }; @@ -50,6 +52,15 @@ namespace render { template <> void jobRun(const DrawTransparentDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); } +class DrawOverlay3D { + mutable gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable +public: + const gpu::PipelinePointer& getOpaquePipeline() const; +}; +namespace render { +template <> void jobRun(const DrawOverlay3D& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); +} + class RenderDeferredTask : public render::Task { public: diff --git a/libraries/model/src/model/SkyFromAtmosphere.slf b/libraries/render-utils/src/SkyFromAtmosphere.slf similarity index 96% rename from libraries/model/src/model/SkyFromAtmosphere.slf rename to libraries/render-utils/src/SkyFromAtmosphere.slf index 02036d0d7c..1e1718677c 100755 --- a/libraries/model/src/model/SkyFromAtmosphere.slf +++ b/libraries/render-utils/src/SkyFromAtmosphere.slf @@ -53,12 +53,14 @@ uniform float g2; varying vec3 position; + float scale(float fCos) { float x = 1.0 - fCos; return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); } + void main (void) { // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) @@ -102,7 +104,8 @@ void main (void) float fCos = dot(v3LightPos, v3Direction) / length(v3Direction); float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5); - gl_FragColor.rgb = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb; - gl_FragColor.a = gl_FragColor.b; - gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1.0/2.2)); + + vec3 finalColor = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb; + gl_FragColor.a = finalColor.b; + gl_FragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); } diff --git a/libraries/model/src/model/SkyFromAtmosphere.slv b/libraries/render-utils/src/SkyFromAtmosphere.slv similarity index 90% rename from libraries/model/src/model/SkyFromAtmosphere.slv rename to libraries/render-utils/src/SkyFromAtmosphere.slv index 785f89bb48..29c907b94c 100755 --- a/libraries/model/src/model/SkyFromAtmosphere.slv +++ b/libraries/render-utils/src/SkyFromAtmosphere.slv @@ -33,6 +33,9 @@ // Copyright (c) 2004 Sean O'Neil // +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + uniform vec3 v3CameraPos; // The camera's current position uniform vec3 v3LightPos; // The direction vector to the light source uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels @@ -52,17 +55,14 @@ const float fSamples = 2.0; varying vec3 position; - -float scale(float fCos) -{ - float x = 1.0 - fCos; - return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); -} - void main(void) { // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) position = gl_Vertex.xyz * fOuterRadius; - gl_Position = gl_ModelViewProjectionMatrix * vec4(position, 1.0); + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + vec4 v4pos = vec4(position, 1.0); + <$transformModelToClipPos(cam, obj, v4pos, gl_Position)$> } diff --git a/libraries/model/src/model/SkyFromSpace.slf b/libraries/render-utils/src/SkyFromSpace.slf similarity index 96% rename from libraries/model/src/model/SkyFromSpace.slf rename to libraries/render-utils/src/SkyFromSpace.slf index 5f6ce80efa..2373511932 100755 --- a/libraries/model/src/model/SkyFromSpace.slf +++ b/libraries/render-utils/src/SkyFromSpace.slf @@ -108,7 +108,8 @@ void main (void) float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5); vec3 color = v3FrontColor * (v3InvWavelength * fKrESun); vec3 secondaryColor = v3FrontColor * fKmESun; - gl_FragColor.rgb = color + fMiePhase * secondaryColor; - gl_FragColor.a = gl_FragColor.b; - gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1.0/2.2)); + + vec3 finalColor = color + fMiePhase * secondaryColor; + gl_FragColor.a = finalColor.b; + gl_FragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); } diff --git a/libraries/model/src/model/SkyFromSpace.slv b/libraries/render-utils/src/SkyFromSpace.slv similarity index 80% rename from libraries/model/src/model/SkyFromSpace.slv rename to libraries/render-utils/src/SkyFromSpace.slv index 6740d1909e..6427af6715 100755 --- a/libraries/model/src/model/SkyFromSpace.slv +++ b/libraries/render-utils/src/SkyFromSpace.slv @@ -1,5 +1,6 @@ -#version 120 - +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> // // For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html: // @@ -32,12 +33,20 @@ // Copyright (c) 2004 Sean O'Neil // +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + uniform float fOuterRadius; // The outer (atmosphere) radius varying vec3 position; -void main(void) -{ + +void main(void) { position = gl_Vertex.xyz * fOuterRadius; - gl_Position = gl_ModelViewProjectionMatrix * vec4(position, 1.0); + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + vec4 v4pos = vec4(position, 1.0); + <$transformModelToClipPos(cam, obj, v4pos, gl_Position)$> } diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index d081c0480a..0eb560bf72 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -101,7 +101,7 @@ struct QuadBuilder { texMin); } QuadBuilder(const Glyph3D& glyph, const glm::vec2& offset) : - QuadBuilder(offset + glyph.offset - glm::vec2(0.0f, glyph.size.y), glyph.size, + QuadBuilder(offset + glm::vec2(glyph.offset.x, glyph.offset.y - glyph.size.y), glyph.size, glyph.texOffset, glyph.texSize) {} }; @@ -113,7 +113,7 @@ public: void read(QIODevice& path); glm::vec2 computeExtent(const QString& str) const; - float getRowHeight() const { return _rowHeight; } + float getFontSize() const { return _fontSize; } // Render string to batch void drawString(gpu::Batch& batch, float x, float y, const QString& str, @@ -139,7 +139,6 @@ private: // Font characteristics QString _family; float _fontSize = 0.0f; - float _rowHeight = 0.0f; float _leading = 0.0f; float _ascent = 0.0f; float _descent = 0.0f; @@ -251,13 +250,14 @@ glm::vec2 Font3D::computeTokenExtent(const QString& token) const { glm::vec2 Font3D::computeExtent(const QString& str) const { glm::vec2 extent = glm::vec2(0.0f, 0.0f); - QStringList tokens = splitLines(str); - foreach(const QString& token, tokens) { - glm::vec2 tokenExtent = computeTokenExtent(token); - extent.x = std::max(tokenExtent.x, extent.x); + QStringList lines{ splitLines(str) }; + if (!lines.empty()) { + for(const auto& line : lines) { + glm::vec2 tokenExtent = computeTokenExtent(line); + extent.x = std::max(tokenExtent.x, extent.x); + } + extent.y = lines.count() * _fontSize; } - extent.y = tokens.count() * _rowHeight; - return extent; } @@ -288,8 +288,7 @@ void Font3D::read(QIODevice& in) { readStream(in, _descent); readStream(in, _spaceWidth); _fontSize = _ascent + _descent; - _rowHeight = _fontSize + _leading; - + // Read character count uint16_t count; readStream(in, count); @@ -393,7 +392,7 @@ void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str, } if (isNewLine || forceNewLine) { // Character return, move the advance to a new line - advance = glm::vec2(x, advance.y - _rowHeight); + advance = glm::vec2(x, advance.y - _leading); if (isNewLine) { // No need to draw anything, go directly to next token @@ -413,7 +412,7 @@ void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str, for (auto c : token) { auto glyph = _glyphs[c]; - QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _fontSize)); + QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _ascent)); _verticesBuffer->append(sizeof(QuadBuilder), (const gpu::Byte*)&qd); _numVertices += 4; @@ -477,9 +476,9 @@ glm::vec2 TextRenderer3D::computeExtent(const QString& str) const { return glm::vec2(0.0f, 0.0f); } -float TextRenderer3D::getRowHeight() const { +float TextRenderer3D::getFontSize() const { if (_font) { - return _font->getRowHeight(); + return _font->getFontSize(); } return 0.0f; } diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h index 8f55d0c977..e61203b06f 100644 --- a/libraries/render-utils/src/TextRenderer3D.h +++ b/libraries/render-utils/src/TextRenderer3D.h @@ -50,7 +50,7 @@ public: ~TextRenderer3D(); glm::vec2 computeExtent(const QString& str) const; - float getRowHeight() const; + float getFontSize() const; void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color = glm::vec4(-1.0f), const glm::vec2& bounds = glm::vec2(-1.0f)); diff --git a/libraries/render-utils/src/overlay3D.slf b/libraries/render-utils/src/overlay3D.slf new file mode 100644 index 0000000000..69ea8c0e76 --- /dev/null +++ b/libraries/render-utils/src/overlay3D.slf @@ -0,0 +1,29 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// model.frag +// fragment shader +// +// Created by Sam Gateau on 6/16/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 +// + +uniform sampler2D diffuseMap; + +varying vec2 varTexcoord; + +varying vec3 varEyeNormal; + +varying vec4 varColor; + + +void main(void) { + vec4 diffuse = texture2D(diffuseMap, varTexcoord.st); + if (diffuse.a < 0.5) { + discard; + } + gl_FragColor = vec4(varColor * diffuse); +} diff --git a/libraries/render-utils/src/overlay3D.slv b/libraries/render-utils/src/overlay3D.slv new file mode 100644 index 0000000000..cdb11c1d08 --- /dev/null +++ b/libraries/render-utils/src/overlay3D.slv @@ -0,0 +1,40 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// overlay3D.slv +// +// Created by Sam Gateau on 6/16/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 gpu/Transform.slh@> + +<$declareStandardTransform()$> + +//attribute vec2 texcoord; + +varying vec2 varTexcoord; + +// interpolated eye position +varying vec4 varEyePosition; + +// the interpolated normal +varying vec3 varEyeNormal; + +varying vec4 varColor; + +void main(void) { + varTexcoord = gl_MultiTexCoord0.xy; + + // pass along the color + varColor = gl_Color; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, gl_Vertex, varEyePosition, gl_Position)$> + <$transformModelToEyeDir(cam, obj, gl_Normal, varEyeNormal.xyz)$> +} diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 53964ac1db..bb4ba00cee 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -263,7 +263,6 @@ template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer renderContext->_numDrawnOpaqueItems = renderedItems.size(); - ItemIDsBounds sortedItems; sortedItems.reserve(culledItems.size()); if (renderContext->_sortOpaque) { @@ -280,10 +279,12 @@ template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; { GLenum buffers[3]; int bufferCount = 0; @@ -347,11 +348,12 @@ template <> void render::jobRun(const DrawTransparent& job, const SceneContextPo Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; - const float MOSTLY_OPAQUE_THRESHOLD = 0.75f; const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f; @@ -434,6 +436,9 @@ template <> void render::jobRun(const DrawBackground& job, const SceneContextPoi Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); @@ -475,6 +480,9 @@ template <> void render::jobRun(const DrawPostLayered& job, const SceneContextPo Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index d74e1ed1e0..470d038196 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -39,6 +39,7 @@ signals: void mutedByMixer(); void environmentMuted(); void receivedFirstPacket(); + void disconnected(); private: AudioScriptingInterface(); diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index 84b3a202b4..9673623b13 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -13,6 +13,8 @@ #define hifi_RenderArgs_h #include +#include + class AABox; class OctreeRenderer; @@ -20,6 +22,7 @@ class ViewFrustum; namespace gpu { class Batch; class Context; +class Texture; } class RenderDetails { @@ -109,6 +112,8 @@ public: gpu::Batch* _batch = nullptr; ShoudRenderFunctor _shouldRender; + std::shared_ptr _whiteTexture; + RenderDetails _details; float _alphaThreshold = 0.5f; diff --git a/libraries/ui/src/CursorManager.cpp b/libraries/ui/src/CursorManager.cpp new file mode 100644 index 0000000000..38c746994d --- /dev/null +++ b/libraries/ui/src/CursorManager.cpp @@ -0,0 +1,92 @@ +// +// Created by Bradley Austin Davis on 2015/06/08 +// 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 "CursorManager.h" + +#include +#include +#include + +#include + +namespace Cursor { + + void Instance::setIcon(uint16_t icon) { + _icon = icon; + } + + uint16_t Instance::getIcon() const { + return _icon; + } + + + class MouseInstance : public Instance { + Source getType() const { + return Source::MOUSE; + } + + ivec2 getScreenPosition() const { + return toGlm(QCursor::pos()); + } + + ivec2 getWindowPosition(QWidget* widget) const { + return toGlm(widget->mapFromGlobal(QCursor::pos())); + } + + vec2 getRelativePosition(QWidget* widget) const { + vec2 pos = getWindowPosition(widget); + pos /= vec2(toGlm(widget->size())); + return pos; + } + }; + + static QMap ICONS; + static uint16_t _customIconId = Icon::USER_BASE; + + Manager::Manager() { + ICONS[Icon::DEFAULT] = PathUtils::resourcesPath() + "images/arrow.png"; + ICONS[Icon::LINK] = PathUtils::resourcesPath() + "images/reticleLink.png"; + } + + Manager& Manager::instance() { + static Manager instance; + return instance; + } + + uint8_t Manager::getCount() { + return 1; + } + + Instance* Manager::getCursor(uint8_t index) { + Q_ASSERT(index < getCount()); + static MouseInstance mouseInstance; + if (index == 0) { + return &mouseInstance; + } + return nullptr; + } + + uint16_t Manager::registerIcon(const QString& path) { + ICONS[_customIconId] = path; + return _customIconId++; + } + + const QString& Manager::getIconImage(uint16_t icon) { + Q_ASSERT(ICONS.count(icon)); + return ICONS[icon]; + } + + float Manager::getScale() { + return _scale; + } + + void Manager::setScale(float scale) { + _scale = scale; + } + +} \ No newline at end of file diff --git a/libraries/ui/src/CursorManager.h b/libraries/ui/src/CursorManager.h new file mode 100644 index 0000000000..c5810caf58 --- /dev/null +++ b/libraries/ui/src/CursorManager.h @@ -0,0 +1,61 @@ +// +// Created by Bradley Austin Davis on 2015/06/08 +// 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 +// + +#pragma once +#include + +#include + +namespace Cursor { + enum class Source { + MOUSE, + LEFT_HAND, + RIGHT_HAND, + UNKNOWN, + }; + + enum Icon { + DEFAULT, + LINK, + GRAB, + + // Add new system cursors here + + // User cursors will have ids over this value + USER_BASE = 0xFF, + }; + + class Instance { + public: + virtual Source getType() const = 0; + virtual ivec2 getWindowPosition(QWidget* widget) const = 0; + virtual vec2 getRelativePosition(QWidget* widget) const = 0; + virtual ivec2 getScreenPosition() const = 0; + virtual void setIcon(uint16_t icon); + virtual uint16_t getIcon() const; + private: + uint16_t _icon; + }; + + class Manager { + Manager(); + Manager(const Manager& other) = delete; + public: + static Manager& instance(); + uint8_t getCount(); + float getScale(); + void setScale(float scale); + Instance* getCursor(uint8_t index = 0); + uint16_t registerIcon(const QString& path); + const QString& getIconImage(uint16_t icon); + private: + float _scale{ 1.0f }; + }; +} + +