From 25266b986c07cfdcfa4e5d48315641932ae09d16 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 7 Aug 2015 21:43:14 +0200 Subject: [PATCH 001/103] added basic implementation of WebSocket class ( https://developer.mozilla.org/en-US/docs/Web/API/WebSocket ) using QWebSocket --- interface/CMakeLists.txt | 2 +- libraries/script-engine/CMakeLists.txt | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 4 + .../script-engine/src/WebSocketClass.cpp | 82 ++++++++++++ libraries/script-engine/src/WebSocketClass.h | 118 ++++++++++++++++++ 5 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 libraries/script-engine/src/WebSocketClass.cpp create mode 100644 libraries/script-engine/src/WebSocketClass.h diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index f1ef38ade9..f1bcbf4c47 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -60,7 +60,7 @@ else () list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP}) endif () -find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets) +find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets WebSockets) # grab the ui files in resources/ui file (GLOB_RECURSE QT_UI_FILES ui/*.ui) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 99d9149c3a..139b99e426 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME script-engine) # use setup_hifi_library macro to setup our project and link appropriate Qt modules -setup_hifi_library(Gui Network Script Widgets) +setup_hifi_library(Gui Network Script WebSockets Widgets) add_dependency_external_projects(glm) find_package(GLM REQUIRED) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index d5e727657c..afbf3eaf38 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -39,6 +39,7 @@ #include "ScriptEngine.h" #include "TypedArrays.h" #include "XMLHttpRequestClass.h" +#include "WebSocketClass.h" #include "SceneScriptingInterface.h" @@ -344,6 +345,9 @@ void ScriptEngine::init() { QScriptValue xmlHttpRequestConstructorValue = newFunction(XMLHttpRequestClass::constructor); globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue); + QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor); + globalObject().setProperty("WebSocket", webSocketConstructorValue); + QScriptValue printConstructorValue = newFunction(debugPrint); globalObject().setProperty("print", printConstructorValue); diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp new file mode 100644 index 0000000000..1b8d305b16 --- /dev/null +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -0,0 +1,82 @@ +// +// WebSocketClass.cpp +// libraries/script-engine/src/ +// +// Created by Thijs Wenker on 8/4/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// This class is an implementation of the WebSocket object for scripting use. It provides a near-complete implementation +// of the class described in the Mozilla docs: https://developer.mozilla.org/en-US/docs/Web/API/WebSocket +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ScriptEngine.h" +#include "WebSocketClass.h" + +WebSocketClass::WebSocketClass(QScriptEngine* engine, QString url) : + _engine(engine) +{ + connect(&_webSocket, &QWebSocket::disconnected, this, &WebSocketClass::handleOnClose); + connect(&_webSocket, SIGNAL(error()), this, SLOT(handleOnError())); + connect(&_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketClass::handleOnMessage); + connect(&_webSocket, &QWebSocket::connected, this, &WebSocketClass::handleOnOpen); + _webSocket.open(url); +} + +QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { + QString url; + QScriptValue callee = context->callee(); + if (context->argumentCount() == 1) { + url = context->argument(0).toString(); + } + return engine->newQObject(new WebSocketClass(engine, url)); +} + +WebSocketClass::~WebSocketClass() { + +} + +void WebSocketClass::send(QScriptValue message) { + _webSocket.sendTextMessage(message.toString()); +} + +void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode, QString reason) { + _webSocket.close(closeCode, reason); +} + +void WebSocketClass::handleOnClose() { + if (_onCloseEvent.isFunction()) { + //QScriptValueList args; + //args << ("received: " + message); + _onCloseEvent.call();//(QScriptValue(), args); + } +} + +void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) { + if (_onErrorEvent.isFunction()) { + // QScriptValueList args; + //args << ("received: " + message); + _onErrorEvent.call();/// QScriptValue(), args); + } +} + +void WebSocketClass::handleOnMessage(const QString& message) { + if (_onMessageEvent.isFunction()) { + + QScriptValueList args; + QScriptValue arg = _engine->newObject(); + arg.setProperty("data", message); + args << arg; + _onMessageEvent.call(QScriptValue(), args); + } +} + +void WebSocketClass::handleOnOpen() { + if (_onOpenEvent.isFunction()) { + //QScriptValueList args; + //args << ("received: " + message); + _onOpenEvent.call();// QScriptValue(), args); + } +} \ No newline at end of file diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h new file mode 100644 index 0000000000..6f0e3729c0 --- /dev/null +++ b/libraries/script-engine/src/WebSocketClass.h @@ -0,0 +1,118 @@ +// +// WebSocketClass.h +// libraries/script-engine/src/ +// +// Created by Thijs Wenker on 8/4/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// 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_WebSocketClass_h +#define hifi_WebSocketClass_h + +#include +#include +#include + +class WebSocketClass : public QObject { + Q_OBJECT + Q_PROPERTY(QString binaryType READ getBinaryType WRITE setBinaryType) + Q_PROPERTY(ulong bufferedAmount READ getBufferedAmount) + Q_PROPERTY(QString extensions READ getExtensions) + + Q_PROPERTY(QScriptValue onclose READ getOnClose WRITE setOnClose) + Q_PROPERTY(QScriptValue onerror READ getOnError WRITE setOnError) + Q_PROPERTY(QScriptValue onmessage READ getOnMessage WRITE setOnMessage) + Q_PROPERTY(QScriptValue onopen READ getOnOpen WRITE setOnOpen) + + Q_PROPERTY(QString protocol READ getProtocol) + Q_PROPERTY(uint readyState READ getReadyState) + Q_PROPERTY(QString url READ getURL) + + Q_PROPERTY(int CONNECTING READ getConnecting CONSTANT) + Q_PROPERTY(int OPEN READ getOpen CONSTANT) + Q_PROPERTY(int CLOSING READ getClosing CONSTANT) + Q_PROPERTY(int CLOSED READ getClosed CONSTANT) + +public: + WebSocketClass(QScriptEngine* engine, QString url); + ~WebSocketClass(); + + static QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine); + + enum ReadyState { + CONNECTING = 0, + OPEN, + CLOSING, + CLOSED + }; + + int getConnecting() const { return CONNECTING; }; + int getOpen() const { return OPEN; }; + int getClosing() const { return CLOSING; }; + int getClosed() const { return CLOSED; }; + + void setBinaryType(QString binaryType) { _binaryType = binaryType; } + QString getBinaryType() { return _binaryType; } + + // extensions is a empty string until supported in QT + QString getExtensions() { return QString(); } + + // protocol is a empty string until supported in QT + QString getProtocol() { return QString(); } + + //TODO: find buffered amount + ulong getBufferedAmount() { return 0; } + + QString getURL() { return _webSocket.requestUrl().toDisplayString(); } + + uint getReadyState() { + switch (_webSocket.state()) { + case QAbstractSocket::SocketState::HostLookupState: + case QAbstractSocket::SocketState::ConnectingState: + return CONNECTING; + case QAbstractSocket::SocketState::ConnectedState: + case QAbstractSocket::SocketState::BoundState: + case QAbstractSocket::SocketState::ListeningState: + return OPEN; + case QAbstractSocket::SocketState::ClosingState: + return CLOSING; + } + return CLOSED; + } + + void setOnClose(QScriptValue eventFunction) { _onCloseEvent = eventFunction; } + QScriptValue getOnClose() { return _onCloseEvent; } + void setOnError(QScriptValue eventFunction) { _onErrorEvent = eventFunction; } + QScriptValue getOnError() { return _onErrorEvent; } + void setOnMessage(QScriptValue eventFunction) { _onMessageEvent = eventFunction; } + QScriptValue getOnMessage() { return _onMessageEvent; } + void setOnOpen(QScriptValue eventFunction) { _onOpenEvent = eventFunction; } + QScriptValue getOnOpen() { return _onOpenEvent; } + +public slots: + void send(QScriptValue message); + void close(QWebSocketProtocol::CloseCode closeCode, QString reason); + +private: + QWebSocket _webSocket; + QScriptEngine* _engine; + + QScriptValue _onCloseEvent; + QScriptValue _onErrorEvent; + QScriptValue _onMessageEvent; + QScriptValue _onOpenEvent; + + QString _binaryType; + +private slots: + void handleOnClose(); + void handleOnError(QAbstractSocket::SocketError error); + void handleOnMessage(const QString& message); + void handleOnOpen(); + +}; + +#endif // hifi_WebSocketClass_h From 881cfb86dd9364f387ec453967003252df5f3b94 Mon Sep 17 00:00:00 2001 From: samcake Date: Sat, 8 Aug 2015 16:43:55 -0700 Subject: [PATCH 002/103] Updating the render engine dashboard --- examples/utilities/tools/cookies.js | 26 ++++ examples/utilities/tools/renderEngineDebug.js | 118 +++++++----------- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/examples/utilities/tools/cookies.js b/examples/utilities/tools/cookies.js index 751008fd99..b9d634d462 100644 --- a/examples/utilities/tools/cookies.js +++ b/examples/utilities/tools/cookies.js @@ -183,6 +183,24 @@ var CHECK_MARK_COLOR = { this.onValueChanged(resetValue); }; + + Slider.prototype.setMinValue = function(minValue) { + var currentValue = this.getValue(); + this.minValue = minValue; + this.setValue(currentValue); + }; + Slider.prototype.getMinValue = function() { + return this.minValue; + }; + Slider.prototype.setMaxValue = function(maxValue) { + var currentValue = this.getValue(); + this.maxValue = maxValue; + this.setValue(currentValue); + }; + Slider.prototype.getMaxValue = function() { + return this.maxValue; + }; + Slider.prototype.onValueChanged = function(value) {}; Slider.prototype.getHeight = function() { @@ -1396,6 +1414,14 @@ var CHECK_MARK_COLOR = { return null; }; + Panel.prototype.getWidget = function(name) { + var item = this.items[name]; + if (item != null) { + return item.widget; + } + return null; + }; + Panel.prototype.update = function(name) { var item = this.items[name]; if (item != null) { diff --git a/examples/utilities/tools/renderEngineDebug.js b/examples/utilities/tools/renderEngineDebug.js index d50a9c545c..49ac923436 100755 --- a/examples/utilities/tools/renderEngineDebug.js +++ b/examples/utilities/tools/renderEngineDebug.js @@ -12,59 +12,55 @@ Script.include("cookies.js"); var panel = new Panel(10, 100); -panel.newSlider("Num Feed Opaques", 0, 1000, - function(value) { }, - function() { return Scene.getEngineNumFeedOpaqueItems(); }, - function(value) { return (value); } +function CounterWidget(parentPanel, name, feedGetter, drawGetter, capSetter, capGetter) { + this.subPanel = panel.newSubPanel(name); + + this.subPanel.newSlider("Num Feed", 0, 1, + function(value) { }, + feedGetter, + function(value) { return (value); }); + this.subPanel.newSlider("Num Drawn", 0, 1, + function(value) { }, + drawGetter, + function(value) { return (value); }); + this.subPanel.newSlider("Max Drawn", -1, 1, + capSetter, + capGetter, + function(value) { return (value); }); + + this.update = function () { + var numFeed = this.subPanel.get("Num Feed"); + this.subPanel.set("Num Feed", numFeed); + this.subPanel.set("Num Drawn", this.subPanel.get("Num Drawn")); + + var numMax = Math.max(numFeed, 1); + this.subPanel.getWidget("Num Feed").setMaxValue(numMax); + this.subPanel.getWidget("Num Drawn").setMaxValue(numMax); + this.subPanel.getWidget("Max Drawn").setMaxValue(numMax); + }; +}; + +var opaquesCounter = new CounterWidget(panel, "Opaques", + function () { return Scene.getEngineNumFeedOpaqueItems(); }, + function () { return Scene.getEngineNumDrawnOpaqueItems(); }, + function(value) { Scene.setEngineMaxDrawnOpaqueItems(value); }, + function () { return Scene.getEngineMaxDrawnOpaqueItems(); } ); -panel.newSlider("Num Drawn Opaques", 0, 1000, - function(value) { }, - function() { return Scene.getEngineNumDrawnOpaqueItems(); }, - function(value) { return (value); } +var transparentsCounter = new CounterWidget(panel, "Transparents", + function () { return Scene.getEngineNumFeedTransparentItems(); }, + function () { return Scene.getEngineNumDrawnTransparentItems(); }, + function(value) { Scene.setEngineMaxDrawnTransparentItems(value); }, + function () { return Scene.getEngineMaxDrawnTransparentItems(); } ); -panel.newSlider("Max Drawn Opaques", -1, 1000, - function(value) { Scene.setEngineMaxDrawnOpaqueItems(value); }, - function() { return Scene.getEngineMaxDrawnOpaqueItems(); }, - function(value) { return (value); } +var overlaysCounter = new CounterWidget(panel, "Overlays", + function () { return Scene.getEngineNumFeedOverlay3DItems(); }, + function () { return Scene.getEngineNumDrawnOverlay3DItems(); }, + function(value) { Scene.setEngineMaxDrawnOverlay3DItems(value); }, + function () { return Scene.getEngineMaxDrawnOverlay3DItems(); } ); -panel.newSlider("Num Feed Transparents", 0, 100, - function(value) { }, - function() { return Scene.getEngineNumFeedTransparentItems(); }, - function(value) { return (value); } -); - -panel.newSlider("Num Drawn Transparents", 0, 100, - function(value) { }, - function() { return Scene.getEngineNumDrawnTransparentItems(); }, - function(value) { return (value); } -); - -panel.newSlider("Max Drawn Transparents", -1, 100, - function(value) { Scene.setEngineMaxDrawnTransparentItems(value); }, - function() { return Scene.getEngineMaxDrawnTransparentItems(); }, - function(value) { return (value); } -); - -panel.newSlider("Num Feed Overlay3Ds", 0, 100, - function(value) { }, - function() { return Scene.getEngineNumFeedOverlay3DItems(); }, - function(value) { return (value); } -); - -panel.newSlider("Num Drawn Overlay3Ds", 0, 100, - function(value) { }, - function() { return Scene.getEngineNumDrawnOverlay3DItems(); }, - function(value) { return (value); } -); - -panel.newSlider("Max Drawn Overlay3Ds", -1, 100, - function(value) { Scene.setEngineMaxDrawnOverlay3DItems(value); }, - function() { return Scene.getEngineMaxDrawnOverlay3DItems(); }, - function(value) { return (value); } -); panel.newCheckbox("Display status", function(value) { Scene.setEngineDisplayItemStatus(value); }, @@ -75,31 +71,9 @@ panel.newCheckbox("Display status", var tickTackPeriod = 500; function updateCounters() { - var numFeedOpaques = panel.get("Num Feed Opaques"); - var numFeedTransparents = panel.get("Num Feed Transparents"); - var numFeedOverlay3Ds = panel.get("Num Feed Overlay3Ds"); - - panel.set("Num Feed Opaques", numFeedOpaques); - panel.set("Num Drawn Opaques", panel.get("Num Drawn Opaques")); - panel.set("Num Feed Transparents", numFeedTransparents); - panel.set("Num Drawn Transparents", panel.get("Num Drawn Transparents")); - panel.set("Num Feed Overlay3Ds", numFeedOverlay3Ds); - panel.set("Num Drawn Overlay3Ds", panel.get("Num Drawn Overlay3Ds")); - - var numMax = Math.max(numFeedOpaques * 1.2, 1); - panel.getWidget("Num Feed Opaques").setMaxValue(numMax); - panel.getWidget("Num Drawn Opaques").setMaxValue(numMax); - panel.getWidget("Max Drawn Opaques").setMaxValue(numMax); - - numMax = Math.max(numFeedTransparents * 1.2, 1); - panel.getWidget("Num Feed Transparents").setMaxValue(numMax); - panel.getWidget("Num Drawn Transparents").setMaxValue(numMax); - panel.getWidget("Max Drawn Transparents").setMaxValue(numMax); - - numMax = Math.max(numFeedOverlay3Ds * 1.2, 1); - panel.getWidget("Num Feed Overlay3Ds").setMaxValue(numMax); - panel.getWidget("Num Drawn Overlay3Ds").setMaxValue(numMax); - panel.getWidget("Max Drawn Overlay3Ds").setMaxValue(numMax); + opaquesCounter.update(); + transparentsCounter.update(); + overlaysCounter.update(); } Script.setInterval(updateCounters, tickTackPeriod); From 4fe8f49d4ccd548641cf9c7a3fca61b254fe7378 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Aug 2015 23:47:40 -0700 Subject: [PATCH 003/103] move origin to center of domain --- examples/edit.js | 8 ++++++-- interface/src/Util.cpp | 2 ++ libraries/entities/src/AddEntityOperator.cpp | 2 ++ libraries/entities/src/EntityItem.cpp | 13 +++++++++++-- libraries/entities/src/EntityItem.h | 1 + libraries/entities/src/EntityItemProperties.h | 2 +- libraries/entities/src/EntitySimulation.cpp | 6 ++++-- libraries/entities/src/EntityTreeElement.cpp | 8 ++++---- libraries/entities/src/MovingEntitiesOperator.cpp | 2 +- libraries/entities/src/UpdateEntityOperator.cpp | 4 ++-- libraries/networking/src/udt/PacketHeaders.h | 1 + libraries/octree/src/OctreeConstants.h | 3 ++- libraries/octree/src/OctreeElement.cpp | 5 +++-- 13 files changed, 40 insertions(+), 17 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index 0f9c9fc4dd..4cc38a0c3d 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1019,9 +1019,13 @@ function getPositionToCreateEntity() { var placementPosition = Vec3.sum(Camera.position, offset); var cameraPosition = Camera.position; + + var HALF_TREE_SCALE = 16384 / 2; - var cameraOutOfBounds = cameraPosition.x < 0 || cameraPosition.y < 0 || cameraPosition.z < 0; - var placementOutOfBounds = placementPosition.x < 0 || placementPosition.y < 0 || placementPosition.z < 0; + var cameraOutOfBounds = cameraPosition.x < -HALF_TREE_SCALE || cameraPosition.y < -HALF_TREE_SCALE || + cameraPosition.z < -HALF_TREE_SCALE; + var placementOutOfBounds = placementPosition.x < -HALF_TREE_SCALE || placementPosition.y < -HALF_TREE_SCALE || + placementPosition.z < -HALF_TREE_SCALE; if (cameraOutOfBounds && placementOutOfBounds) { return null; diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 3a01367fc7..f62a6b8fc5 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -43,6 +43,8 @@ void renderWorldBox(gpu::Batch& batch) { auto transform = Transform{}; batch.setModelTransform(transform); + + // FIXME - new origin tweaks need to be done to this geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(TREE_SCALE, 0.0f, 0.0f), red); geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, TREE_SCALE, 0.0f), green); geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, TREE_SCALE), blue); diff --git a/libraries/entities/src/AddEntityOperator.cpp b/libraries/entities/src/AddEntityOperator.cpp index b85d12c2da..db9a18a3e9 100644 --- a/libraries/entities/src/AddEntityOperator.cpp +++ b/libraries/entities/src/AddEntityOperator.cpp @@ -25,6 +25,8 @@ AddEntityOperator::AddEntityOperator(EntityTree* tree, { // caller must have verified existence of newEntity assert(_newEntity); + + // FIXME - how does this change for new origin??? _newEntityBox = _newEntity->getMaximumAACube().clamp(0.0f, (float)TREE_SCALE); } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 205d1c339e..97704985aa 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -634,7 +634,11 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // but since we're using macros below we have to temporarily modify overwriteLocalData. bool oldOverwrite = overwriteLocalData; overwriteLocalData = overwriteLocalData && !weOwnSimulation; - READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition); + if (args.bitstreamVersion >= VERSION_ENTITIES_CENTER_ORIGIN) { + READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition); + } else { + READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePositionOldOrigin); + } READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation); READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity); READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity); @@ -656,7 +660,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef } else { // legacy order of packing here // TODO: purge this logic in a few months from now (2015.07) - READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition); + READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePositionOldOrigin); READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions); READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation); READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity); @@ -1307,6 +1311,11 @@ void EntityItem::updatePosition(const glm::vec3& value) { } } +void EntityItem::updatePositionOldOrigin(const glm::vec3& value) { + glm::vec3 newValue = value - glm::vec3(HALF_TREE_SCALE); + updatePosition(newValue); +} + void EntityItem::updateDimensions(const glm::vec3& value) { auto delta = glm::distance(getDimensions(), value); if (delta > IGNORE_DIMENSIONS_DELTA) { diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 57f8883cea..12e6c1b3e7 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -361,6 +361,7 @@ public: // updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags void updatePosition(const glm::vec3& value); + void updatePositionOldOrigin(const glm::vec3& value); void updateDimensions(const glm::vec3& value); void updateRotation(const glm::quat& rotation); void updateDensity(float value); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 8085c24d90..1e8cf51ef3 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -253,7 +253,7 @@ void EntityItemPropertiesFromScriptValueHonorReadOnly(const QScriptValue &object // define these inline here so the macros work inline void EntityItemProperties::setPosition(const glm::vec3& value) - { _position = glm::clamp(value, 0.0f, (float)TREE_SCALE); _positionChanged = true; } + { _position = glm::clamp(value, (float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); _positionChanged = true; } inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { debug << "EntityItemProperties[" << "\n"; diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index f2bd1e873e..c614b62c91 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -113,7 +113,8 @@ void EntitySimulation::sortEntitiesThatMoved() { // External changes to entity position/shape are expected to be sorted outside of the EntitySimulation. PerformanceTimer perfTimer("sortingEntities"); MovingEntitiesOperator moveOperator(_entityTree); - AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), (float)TREE_SCALE); + AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE, (float)-HALF_TREE_SCALE, (float)-HALF_TREE_SCALE), + (float)TREE_SCALE); SetOfEntities::iterator itemItr = _entitiesToSort.begin(); while (itemItr != _entitiesToSort.end()) { EntityItemPointer entity = *itemItr; @@ -195,7 +196,8 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) { bool wasRemoved = false; uint32_t dirtyFlags = entity->getDirtyFlags(); if (dirtyFlags & EntityItem::DIRTY_POSITION) { - AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), (float)TREE_SCALE); + AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE, (float)-HALF_TREE_SCALE, (float)-HALF_TREE_SCALE), + (float)TREE_SCALE); AACube newCube = entity->getMaximumAACube(); if (!domainBounds.touches(newCube)) { qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds."; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 856550f297..a023f46c5e 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -444,14 +444,14 @@ bool EntityTreeElement::bestFitBounds(const AABox& bounds) const { } bool EntityTreeElement::containsBounds(const glm::vec3& minPoint, const glm::vec3& maxPoint) const { - glm::vec3 clampedMin = glm::clamp(minPoint, 0.0f, (float)TREE_SCALE); - glm::vec3 clampedMax = glm::clamp(maxPoint, 0.0f, (float)TREE_SCALE); + glm::vec3 clampedMin = glm::clamp(minPoint, (float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); + glm::vec3 clampedMax = glm::clamp(maxPoint, (float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); return _cube.contains(clampedMin) && _cube.contains(clampedMax); } bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3& maxPoint) const { - glm::vec3 clampedMin = glm::clamp(minPoint, 0.0f, (float)TREE_SCALE); - glm::vec3 clampedMax = glm::clamp(maxPoint, 0.0f, (float)TREE_SCALE); + glm::vec3 clampedMin = glm::clamp(minPoint, (float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); + glm::vec3 clampedMax = glm::clamp(maxPoint, (float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); if (_cube.contains(clampedMin) && _cube.contains(clampedMax)) { diff --git a/libraries/entities/src/MovingEntitiesOperator.cpp b/libraries/entities/src/MovingEntitiesOperator.cpp index ddfea13d07..7dd1ab849c 100644 --- a/libraries/entities/src/MovingEntitiesOperator.cpp +++ b/libraries/entities/src/MovingEntitiesOperator.cpp @@ -52,7 +52,7 @@ MovingEntitiesOperator::~MovingEntitiesOperator() { void MovingEntitiesOperator::addEntityToMoveList(EntityItemPointer entity, const AACube& newCube) { EntityTreeElement* oldContainingElement = _tree->getContainingElement(entity->getEntityItemID()); - AABox newCubeClamped = newCube.clamp(0.0f, (float)TREE_SCALE); + AABox newCubeClamped = newCube.clamp((float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); if (_wantDebug) { qCDebug(entities) << "MovingEntitiesOperator::addEntityToMoveList() -----------------------------"; diff --git a/libraries/entities/src/UpdateEntityOperator.cpp b/libraries/entities/src/UpdateEntityOperator.cpp index 6720839da0..991d725f97 100644 --- a/libraries/entities/src/UpdateEntityOperator.cpp +++ b/libraries/entities/src/UpdateEntityOperator.cpp @@ -47,7 +47,7 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree, // which can handle all potential rotations? // the getMaximumAACube is the relaxed form. _oldEntityCube = _existingEntity->getMaximumAACube(); - _oldEntityBox = _oldEntityCube.clamp(0.0f, (float)TREE_SCALE); // clamp to domain bounds + _oldEntityBox = _oldEntityCube.clamp((float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); // clamp to domain bounds // If the old properties doesn't contain the properties required to calculate a bounding box, // get them from the existing entity. Registration point is required to correctly calculate @@ -123,7 +123,7 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree, } } - _newEntityBox = _newEntityCube.clamp(0.0f, (float)TREE_SCALE); // clamp to domain bounds + _newEntityBox = _newEntityCube.clamp((float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); // clamp to domain bounds if (_wantDebug) { diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 1aeadb1af9..613ea4913d 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -142,5 +142,6 @@ const PacketVersion VERSION_ENTITIES_HAVE_SIMULATION_OWNER_AND_ACTIONS_OVER_WIRE const PacketVersion VERSION_ENTITIES_NEW_PROTOCOL_LAYER = 35; const PacketVersion VERSION_POLYVOX_TEXTURES = 36; const PacketVersion VERSION_ENTITIES_POLYLINE = 37; +const PacketVersion VERSION_ENTITIES_CENTER_ORIGIN = 38; #endif // hifi_PacketHeaders_h \ No newline at end of file diff --git a/libraries/octree/src/OctreeConstants.h b/libraries/octree/src/OctreeConstants.h index 4a1baea2d5..acfa1d14c6 100644 --- a/libraries/octree/src/OctreeConstants.h +++ b/libraries/octree/src/OctreeConstants.h @@ -17,7 +17,8 @@ const quint64 CHANGE_FUDGE = 1000 * 200; // useconds of fudge in determining if we want to resend changed voxels -const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe +const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe +const int HALF_TREE_SCALE = TREE_SCALE / 2; // This controls the LOD. Larger number will make smaller voxels visible at greater distance. const float DEFAULT_OCTREE_SIZE_SCALE = TREE_SCALE * 400.0f; diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 85ea0caef0..2bbacccf95 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -191,6 +191,7 @@ void OctreeElement::calculateAACube() { // this tells you the "size" of the voxel float voxelScale = (float)TREE_SCALE / powf(2.0f, numberOfThreeBitSectionsInCode(getOctalCode())); corner *= (float)TREE_SCALE; + corner -= (float)HALF_TREE_SCALE; _cube.setBox(corner, voxelScale); } @@ -717,8 +718,8 @@ int OctreeElement::getMyChildContaining(const AACube& cube) const { } // Determine which of our children the minimum and maximum corners of the cube live in... - glm::vec3 cubeCornerMinimum = glm::clamp(cube.getCorner(), 0.0f, (float)TREE_SCALE); - glm::vec3 cubeCornerMaximum = glm::clamp(cube.calcTopFarLeft(), 0.0f, (float)TREE_SCALE); + glm::vec3 cubeCornerMinimum = glm::clamp(cube.getCorner(), (float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); + glm::vec3 cubeCornerMaximum = glm::clamp(cube.calcTopFarLeft(), (float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); if (_cube.contains(cubeCornerMinimum) && _cube.contains(cubeCornerMaximum)) { int childIndexCubeMinimum = getMyChildContainingPoint(cubeCornerMinimum); From 85603dab29a622d208b4030858ed1e8df36eb4f5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Aug 2015 23:51:07 -0700 Subject: [PATCH 004/103] bump entity packet version --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 0146abdb7c..d2b116a9d5 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -67,7 +67,7 @@ PacketVersion versionForPacketType(PacketType::Value packetType) { case EntityAdd: case EntityEdit: case EntityData: - return VERSION_ENTITIES_POLYLINE; + return VERSION_ENTITIES_CENTER_ORIGIN; default: return 11; } From 7c77e0e941f3fe9b3ceda0a079a31b8d63ace6fa Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 13 Aug 2015 18:21:50 +0200 Subject: [PATCH 005/103] initial WebSocketServer --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++ .../script-engine/src/WebSocketClass.cpp | 1 - .../src/WebSocketServerClass.cpp | 44 +++++++++++++++++++ .../script-engine/src/WebSocketServerClass.h | 39 ++++++++++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 libraries/script-engine/src/WebSocketServerClass.cpp create mode 100644 libraries/script-engine/src/WebSocketServerClass.h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 861034a6dc..bde0643c51 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -39,6 +39,7 @@ #include "TypedArrays.h" #include "XMLHttpRequestClass.h" #include "WebSocketClass.h" +#include "WebSocketServerClass.h" #include "SceneScriptingInterface.h" @@ -347,6 +348,9 @@ void ScriptEngine::init() { QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor); globalObject().setProperty("WebSocket", webSocketConstructorValue); + QScriptValue webSocketServerConstructorValue = newFunction(WebSocketServerClass::constructor); + globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); + QScriptValue printConstructorValue = newFunction(debugPrint); globalObject().setProperty("print", printConstructorValue); diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 1b8d305b16..f116c3a0e4 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -64,7 +64,6 @@ void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) { void WebSocketClass::handleOnMessage(const QString& message) { if (_onMessageEvent.isFunction()) { - QScriptValueList args; QScriptValue arg = _engine->newObject(); arg.setProperty("data", message); diff --git a/libraries/script-engine/src/WebSocketServerClass.cpp b/libraries/script-engine/src/WebSocketServerClass.cpp new file mode 100644 index 0000000000..86f6506375 --- /dev/null +++ b/libraries/script-engine/src/WebSocketServerClass.cpp @@ -0,0 +1,44 @@ +// +// WebSocketServerClass.cpp +// libraries/script-engine/src/ +// +// Created by Thijs Wenker on 8/10/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// Making WebSocketServer accessible through scripting. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ScriptEngine.h" +#include "WebSocketServerClass.h" + +WebSocketServerClass::WebSocketServerClass(QScriptEngine* engine, const QString& serverName, quint16 port) : + _engine(engine), + _webSocketServer(serverName, QWebSocketServer::SslMode::NonSecureMode) +{ + _webSocketServer.listen(QHostAddress::Any, port); +} + +QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptEngine* engine) { + // the serverName is used in handshakes + QString serverName = QStringLiteral("HighFidelity - Scripted WebSocket Listener"); + // port 0 will auto-assign a free port + quint16 port = 0; + QScriptValue callee = context->callee(); + if (context->argumentCount() > 0) { + QScriptValue options = context->argument(0); + QScriptValue portOption = options.property(QStringLiteral("port")); + if (portOption.isValid() && portOption.isNumber()) { + port = portOption.toNumber(); + } + } + return engine->newQObject(new WebSocketServerClass(engine, serverName, port)); +} + +WebSocketServerClass::~WebSocketServerClass() { + if (_webSocketServer.isListening()) { + _webSocketServer.close(); + } +} diff --git a/libraries/script-engine/src/WebSocketServerClass.h b/libraries/script-engine/src/WebSocketServerClass.h new file mode 100644 index 0000000000..18fcdc693e --- /dev/null +++ b/libraries/script-engine/src/WebSocketServerClass.h @@ -0,0 +1,39 @@ +// +// WebSocketServerClass.h +// libraries/script-engine/src/ +// +// Created by Thijs Wenker on 8/10/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// 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_WebSocketServerClass_h +#define hifi_WebSocketServerClass_h + +#include +#include +#include + +class WebSocketServerClass : public QObject { + Q_OBJECT + +public: + WebSocketServerClass(QScriptEngine* engine, const QString& serverName, quint16 port); + ~WebSocketServerClass(); + + static QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptEngine* engine); + +private: + QWebSocketServer _webSocketServer; + QScriptEngine* _engine; + +signals: + void newConnection(); + + + +}; + +#endif // hifi_WebSocketServerClass_h From cda5b29fd3f2c0340f5d891bdd897aad182b4b07 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 13 Aug 2015 14:19:13 -0600 Subject: [PATCH 006/103] Another take at versioning details exchange - checkpoint --- assignment-client/CMakeLists.txt | 1 + assignment-client/src/AssignmentClient.cpp | 3 +-- assignment-client/src/AssignmentClientApp.cpp | 5 ++--- .../macros/ApplicationVersion.h.in | 10 ++++----- cmake/macros/IncludeApplicationVersion.cmake | 22 +++++++++++++++++++ domain-server/CMakeLists.txt | 1 + domain-server/src/DomainServer.cpp | 8 ++----- interface/CMakeLists.txt | 12 ++-------- interface/src/Application.cpp | 2 +- libraries/networking/CMakeLists.txt | 3 ++- libraries/networking/src/Assignment.cpp | 7 ++++++ libraries/networking/src/Assignment.h | 1 + libraries/networking/src/NodeList.cpp | 10 ++++++--- 13 files changed, 53 insertions(+), 32 deletions(-) rename interface/InterfaceVersion.h.in => cmake/macros/ApplicationVersion.h.in (54%) create mode 100644 cmake/macros/IncludeApplicationVersion.cmake diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 2561a1502d..edd68e12bf 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -17,4 +17,5 @@ if (UNIX) target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS}) endif (UNIX) +include_application_version() copy_dlls_beside_windows_executable() \ No newline at end of file diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 40aef1c707..ec0e5b9e5a 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -129,6 +129,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri packetReceiver.registerListener(PacketType::CreateAssignment, this, "handleCreateAssignmentPacket"); packetReceiver.registerListener(PacketType::StopNode, this, "handleStopNodePacket"); } + void AssignmentClient::stopAssignmentClient() { qDebug() << "Forced stop of assignment-client."; @@ -172,7 +173,6 @@ void AssignmentClient::aboutToQuit() { qInstallMessageHandler(0); } - void AssignmentClient::setUpStatusToMonitor() { // send a stats packet every 1 seconds connect(&_statsTimerACM, &QTimer::timeout, this, &AssignmentClient::sendStatusPacketToACM); @@ -217,7 +217,6 @@ void AssignmentClient::sendAssignmentRequest() { qDebug() << "Failed to read local assignment server port from shared memory" << "- will send assignment request to previous assignment server socket."; } - } nodeList->sendAssignment(_requestAssignment); diff --git a/assignment-client/src/AssignmentClientApp.cpp b/assignment-client/src/AssignmentClientApp.cpp index 7268f9281b..2edae340c3 100644 --- a/assignment-client/src/AssignmentClientApp.cpp +++ b/assignment-client/src/AssignmentClientApp.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -40,6 +41,7 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : setOrganizationName("High Fidelity"); setOrganizationDomain("highfidelity.io"); setApplicationName("assignment-client"); + setApplicationName(BUILD_VERSION); // use the verbose message handler in Logging qInstallMessageHandler(LogHandler::verboseMessageHandler); @@ -93,10 +95,8 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : Q_UNREACHABLE(); } - const QVariantMap argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); - unsigned int numForks = 0; if (parser.isSet(numChildsOption)) { numForks = parser.value(numChildsOption).toInt(); @@ -139,7 +139,6 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : assignmentPool = parser.value(poolOption); } - QUuid walletUUID; if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) { walletUUID = argumentVariantMap.value(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION).toString(); diff --git a/interface/InterfaceVersion.h.in b/cmake/macros/ApplicationVersion.h.in similarity index 54% rename from interface/InterfaceVersion.h.in rename to cmake/macros/ApplicationVersion.h.in index 2f902de41c..736d00726c 100644 --- a/interface/InterfaceVersion.h.in +++ b/cmake/macros/ApplicationVersion.h.in @@ -1,11 +1,9 @@ // -// InterfaceVersion.h -// interface/src +// ApplicationVersion.h.in +// cmake/macros // -// Created by Leonardo Murillo on 12/16/13. -// Copyright 2013 High Fidelity, Inc. -// -// Declaration of version and build data +// Created by Leonardo Murillo on 8/13/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 diff --git a/cmake/macros/IncludeApplicationVersion.cmake b/cmake/macros/IncludeApplicationVersion.cmake new file mode 100644 index 0000000000..96137294a7 --- /dev/null +++ b/cmake/macros/IncludeApplicationVersion.cmake @@ -0,0 +1,22 @@ +# +# IncludeApplicationVersion.cmake +# cmake/macros +# +# Created by Leonardo Murillo on 07/14/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 +# + +macro(INCLUDE_APPLICATION_VERSION) + if (DEFINED ENV{JOB_ID}) + set (BUILD_SEQ $ENV{JOB_ID}) + elseif (DEFINED ENV{ghprbPullId}) + set (BUILD_SEQ "PR: $ENV{ghprbPullId} - Commit: $ENV{ghprbActualCommit}") + else () + set(BUILD_SEQ "dev") + endif () + configure_file("${MACRO_DIR}/ApplicationVersion.h.in" "${PROJECT_BINARY_DIR}/includes/ApplicationVersion.h") + include_directories("${PROJECT_BINARY_DIR}/includes") +endmacro(INCLUDE_APPLICATION_VERSION) diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index 41f2d15486..e4fa1d874d 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -31,4 +31,5 @@ include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") # append OpenSSL to our list of libraries to link target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES}) +include_application_version() copy_dlls_beside_windows_executable() diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 4e5b563fc6..abfd7efe89 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -75,6 +76,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : setOrganizationName("High Fidelity"); setOrganizationDomain("highfidelity.io"); setApplicationName("domain-server"); + setApplicationVersion(BUILD_VERSION); QSettings::setDefaultFormat(QSettings::IniFormat); // make sure we have a fresh AccountManager instance @@ -1478,7 +1480,6 @@ const char JSON_KEY_POOL[] = "pool"; const char JSON_KEY_PENDING_CREDITS[] = "pending_credits"; const char JSON_KEY_WAKE_TIMESTAMP[] = "wake_timestamp"; const char JSON_KEY_USERNAME[] = "username"; - QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { QJsonObject nodeJson; @@ -1527,7 +1528,6 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { } const char ASSIGNMENT_SCRIPT_HOST_LOCATION[] = "resources/web/assignment"; - QString pathForAssignmentScript(const QUuid& assignmentUUID) { QString newPath(ASSIGNMENT_SCRIPT_HOST_LOCATION); newPath += "/scripts/"; @@ -1537,7 +1537,6 @@ QString pathForAssignmentScript(const QUuid& assignmentUUID) { } const QString URI_OAUTH = "/oauth"; - bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) { const QString JSON_MIME_TYPE = "application/json"; @@ -2024,8 +2023,6 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl } const QString OAUTH_JSON_ACCESS_TOKEN_KEY = "access_token"; - - QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenReply) { // pull the access token from the returned JSON and store it with the matching session UUID QJsonDocument returnedJSON = QJsonDocument::fromJson(tokenReply->readAll()); @@ -2042,7 +2039,6 @@ QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenR } const QString DS_SETTINGS_SESSIONS_GROUP = "web-sessions"; - Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileReply) { Headers cookieHeaders; diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 9ad250bdcf..acafafa006 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -14,20 +14,12 @@ endforeach() find_package(Qt5LinguistTools REQUIRED) find_package(Qt5LinguistToolsMacros) -if (DEFINED ENV{JOB_ID}) - set(BUILD_SEQ $ENV{JOB_ID}) -elseif (DEFINED ENV{ghprbPullId}) - set(BUILD_SEQ "PR: $ENV{ghprbPullId} - Commit: $ENV{ghprbActualCommit}") -else () - set(BUILD_SEQ "dev") -endif () - if (WIN32) add_definitions(-D_USE_MATH_DEFINES) # apparently needed to get M_PI and other defines from cmath/math.h add_definitions(-DWINDOWS_LEAN_AND_MEAN) # needed to make sure windows doesn't go to crazy with its defines endif() -configure_file(InterfaceVersion.h.in "${PROJECT_BINARY_DIR}/includes/InterfaceVersion.h") +include_application_version() # grab the implementation and header files from src dirs file(GLOB_RECURSE INTERFACE_SRCS "src/*.cpp" "src/*.h") @@ -174,7 +166,7 @@ if (RTMIDI_FOUND AND NOT DISABLE_RTMIDI AND APPLE) endif () # include headers for interface and InterfaceConfig. -include_directories("${PROJECT_SOURCE_DIR}/src" "${PROJECT_BINARY_DIR}/includes") +include_directories("${PROJECT_SOURCE_DIR}/src") target_link_libraries( ${TARGET_NAME} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d8dad73d82..fe361f8b8f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -52,6 +52,7 @@ #include #include +#include #include #include #include @@ -100,7 +101,6 @@ #include "AudioClient.h" #include "DiscoverabilityManager.h" #include "GLCanvas.h" -#include "InterfaceVersion.h" #include "LODManager.h" #include "Menu.h" #include "ModelPackager.h" diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index 6b386ace92..d79e6bde58 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -28,4 +28,5 @@ include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES} ${TBB_LIBRARIES}) # append tbb includes to our list of includes to bubble -target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${TBB_INCLUDE_DIRS}) \ No newline at end of file +target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${TBB_INCLUDE_DIRS}) +include_application_version() \ No newline at end of file diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp index 293d86475f..a7a6fe03ea 100644 --- a/libraries/networking/src/Assignment.cpp +++ b/libraries/networking/src/Assignment.cpp @@ -66,7 +66,9 @@ Assignment::Assignment(NLPacket& packet) : _payload(), _walletUUID() { + qDebug() << "LEOTEST: We are building an Assignment from a packet"; if (packet.getType() == PacketType::RequestAssignment) { + qDebug() << "LEOTEST: This is a request assignment packet"; _command = Assignment::RequestCommand; } else if (packet.getType() == PacketType::CreateAssignment) { _command = Assignment::CreateCommand; @@ -151,11 +153,16 @@ QDataStream& operator<<(QDataStream &out, const Assignment& assignment) { QDataStream& operator>>(QDataStream &in, Assignment& assignment) { quint8 packedType; in >> packedType; + if (assignment._command == Assignment::RequestCommand) { + qDebug() << "We are extracting the version"; + in >> assignment._nodeVersion; + } assignment._type = (Assignment::Type) packedType; in >> assignment._uuid >> assignment._pool >> assignment._payload; if (assignment._command == Assignment::RequestCommand) { + qDebug() << "LEOTEST: Operator for >> in case of RequestCommand"; in >> assignment._walletUUID; } diff --git a/libraries/networking/src/Assignment.h b/libraries/networking/src/Assignment.h index 67f861f850..f1b6cc7fba 100644 --- a/libraries/networking/src/Assignment.h +++ b/libraries/networking/src/Assignment.h @@ -98,6 +98,7 @@ protected: QByteArray _payload; /// an optional payload attached to this assignment, a maximum for 1024 bytes will be packed bool _isStatic; /// defines if this assignment needs to be re-queued in the domain-server if it stops being fulfilled QUuid _walletUUID; /// the UUID for the wallet that should be paid for this assignment + QString _nodeVersion; }; #endif // hifi_Assignment_h diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 44b9c0b829..9ecf31edab 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include "AccountManager.h" @@ -367,7 +368,6 @@ void NodeList::sendDSPathQuery(const QString& newPath) { } } - void NodeList::processDomainServerPathResponse(QSharedPointer packet) { // This is a response to a path query we theoretically made. // In the future we may want to check that this was actually from our DS and for a query we actually made. @@ -457,7 +457,6 @@ void NodeList::pingPunchForDomainServer() { } } - void NodeList::processDomainServerConnectionTokenPacket(QSharedPointer packet) { if (_domainHandler.getSockAddr().isNull()) { // refuse to process this packet if we aren't currently connected to the DS @@ -547,9 +546,14 @@ void NodeList::sendAssignment(Assignment& assignment) { ? PacketType::CreateAssignment : PacketType::RequestAssignment; + qDebug() << "LEOTEST: Packet type name " << nameForPacketType(assignmentPacketType); auto assignmentPacket = NLPacket::create(assignmentPacketType); - + QDataStream packetStream(assignmentPacket.get()); + if (assignmentPacketType == PacketType::RequestAssignment) { + qDebug() << "LEOTEST: This is an assignment request, lets send the node version here " << BUILD_VERSION; + packetStream << BUILD_VERSION; + } packetStream << assignment; // TODO: should this be a non sourced packet? From 7c4813f1cf278a58d4fae85ad6bfbc1b1368b189 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 13 Aug 2015 16:35:55 -0600 Subject: [PATCH 007/103] Another versioning checkpoint + cleanup --- domain-server/src/DomainServer.cpp | 6 ++++- domain-server/src/DomainServerNodeData.h | 5 ++++ domain-server/src/PendingAssignedNodeData.cpp | 5 ++-- domain-server/src/PendingAssignedNodeData.h | 6 ++++- libraries/networking/src/Assignment.cpp | 26 ++++++++++++------- libraries/networking/src/Assignment.h | 2 ++ libraries/networking/src/NodeList.cpp | 5 ---- 7 files changed, 37 insertions(+), 18 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index abfd7efe89..071626ef1e 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -740,6 +740,7 @@ void DomainServer::processConnectRequestPacket(QSharedPointer packet) if (isAssignment) { nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID()); nodeData->setWalletUUID(pendingAssigneeData->getWalletUUID()); + nodeData->setNodeVersion(pendingAssigneeData->getNodeVersion()); // always allow assignment clients to create and destroy entities newNode->setCanAdjustLocks(true); @@ -1170,7 +1171,8 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointer packe // add the information for that deployed assignment to the hash of pending assigned nodes PendingAssignedNodeData* pendingNodeData = new PendingAssignedNodeData(assignmentToDeploy->getUUID(), - requestAssignment.getWalletUUID()); + requestAssignment.getWalletUUID(), + requestAssignment.getNodeVersion()); _pendingAssignedNodes.insert(uniqueAssignment.getUUID(), pendingNodeData); } else { if (requestAssignment.getType() != Assignment::AgentType @@ -1480,6 +1482,7 @@ const char JSON_KEY_POOL[] = "pool"; const char JSON_KEY_PENDING_CREDITS[] = "pending_credits"; const char JSON_KEY_WAKE_TIMESTAMP[] = "wake_timestamp"; const char JSON_KEY_USERNAME[] = "username"; +const char JSON_KEY_VERSION[] = "version"; QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { QJsonObject nodeJson; @@ -1506,6 +1509,7 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { // add the node username, if it exists nodeJson[JSON_KEY_USERNAME] = nodeData->getUsername(); + nodeJson[JSON_KEY_VERSION] = nodeData->getNodeVersion(); SharedAssignmentPointer matchingAssignment = _allAssignments.value(nodeData->getAssignmentUUID()); if (matchingAssignment) { diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h index 57ed96acab..2c57368653 100644 --- a/domain-server/src/DomainServerNodeData.h +++ b/domain-server/src/DomainServerNodeData.h @@ -50,6 +50,10 @@ public: const NodeSet& getNodeInterestSet() const { return _nodeInterestSet; } void setNodeInterestSet(const NodeSet& nodeInterestSet) { _nodeInterestSet = nodeInterestSet; } + + void setNodeVersion(const QString& nodeVersion) { _nodeVersion = nodeVersion; } + const QString& getNodeVersion() { return _nodeVersion; } + private: QJsonObject mergeJSONStatsFromNewObject(const QJsonObject& newObject, QJsonObject destinationObject); @@ -62,6 +66,7 @@ private: HifiSockAddr _sendingSockAddr; bool _isAuthenticated; NodeSet _nodeInterestSet; + QString _nodeVersion; }; #endif // hifi_DomainServerNodeData_h diff --git a/domain-server/src/PendingAssignedNodeData.cpp b/domain-server/src/PendingAssignedNodeData.cpp index 21b3aa4ca4..30310ac01f 100644 --- a/domain-server/src/PendingAssignedNodeData.cpp +++ b/domain-server/src/PendingAssignedNodeData.cpp @@ -11,9 +11,10 @@ #include "PendingAssignedNodeData.h" -PendingAssignedNodeData::PendingAssignedNodeData(const QUuid& assignmentUUID, const QUuid& walletUUID) : +PendingAssignedNodeData::PendingAssignedNodeData(const QUuid& assignmentUUID, const QUuid& walletUUID, const QString& nodeVersion) : _assignmentUUID(assignmentUUID), - _walletUUID(walletUUID) + _walletUUID(walletUUID), + _nodeVersion(nodeVersion) { } \ No newline at end of file diff --git a/domain-server/src/PendingAssignedNodeData.h b/domain-server/src/PendingAssignedNodeData.h index 93d99a6f8f..2d546f573f 100644 --- a/domain-server/src/PendingAssignedNodeData.h +++ b/domain-server/src/PendingAssignedNodeData.h @@ -18,16 +18,20 @@ class PendingAssignedNodeData : public QObject { Q_OBJECT public: - PendingAssignedNodeData(const QUuid& assignmentUUID, const QUuid& walletUUID); + PendingAssignedNodeData(const QUuid& assignmentUUID, const QUuid& walletUUID, const QString& nodeVersion); void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; } const QUuid& getAssignmentUUID() const { return _assignmentUUID; } void setWalletUUID(const QUuid& walletUUID) { _walletUUID = walletUUID; } const QUuid& getWalletUUID() const { return _walletUUID; } + + const QString& getNodeVersion() const { return _nodeVersion; } + private: QUuid _assignmentUUID; QUuid _walletUUID; + QString _nodeVersion; }; #endif // hifi_PendingAssignedNodeData_h \ No newline at end of file diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp index a7a6fe03ea..8a97b7cf8c 100644 --- a/libraries/networking/src/Assignment.cpp +++ b/libraries/networking/src/Assignment.cpp @@ -13,8 +13,10 @@ #include "SharedUtil.h" #include "UUID.h" +#include #include +#include #include "Assignment.h" Assignment::Type Assignment::typeForNodeType(NodeType_t nodeType) { @@ -52,11 +54,14 @@ Assignment::Assignment(Assignment::Command command, Assignment::Type type, const _location(location), _payload(), _isStatic(false), - _walletUUID() + _walletUUID(), + _nodeVersion() { if (_command == Assignment::CreateCommand) { // this is a newly created assignment, generate a random UUID _uuid = QUuid::createUuid(); + } else if (_command == Assignment::RequestCommand) { + _nodeVersion = BUILD_VERSION; } } @@ -64,11 +69,10 @@ Assignment::Assignment(NLPacket& packet) : _pool(), _location(GlobalLocation), _payload(), - _walletUUID() + _walletUUID(), + _nodeVersion() { - qDebug() << "LEOTEST: We are building an Assignment from a packet"; if (packet.getType() == PacketType::RequestAssignment) { - qDebug() << "LEOTEST: This is a request assignment packet"; _command = Assignment::RequestCommand; } else if (packet.getType() == PacketType::CreateAssignment) { _command = Assignment::CreateCommand; @@ -85,15 +89,14 @@ Assignment::Assignment(NLPacket& packet) : Assignment::Assignment(const Assignment& otherAssignment) { - _uuid = otherAssignment._uuid; - _command = otherAssignment._command; _type = otherAssignment._type; _location = otherAssignment._location; _pool = otherAssignment._pool; _payload = otherAssignment._payload; _walletUUID = otherAssignment._walletUUID; + _nodeVersion = otherAssignment._nodeVersion; } Assignment& Assignment::operator=(const Assignment& rhsAssignment) { @@ -112,6 +115,7 @@ void Assignment::swap(Assignment& otherAssignment) { swap(_pool, otherAssignment._pool); swap(_payload, otherAssignment._payload); swap(_walletUUID, otherAssignment._walletUUID); + swap(_nodeVersion, otherAssignment._nodeVersion); } const char* Assignment::getTypeName() const { @@ -141,7 +145,13 @@ QDebug operator<<(QDebug debug, const Assignment &assignment) { } QDataStream& operator<<(QDataStream &out, const Assignment& assignment) { - out << (quint8) assignment._type << assignment._uuid << assignment._pool << assignment._payload; + out << (quint8) assignment._type; + + if (assignment._command == Assignment::RequestCommand) { + out << assignment._nodeVersion; + } + + out << assignment._uuid << assignment._pool << assignment._payload; if (assignment._command == Assignment::RequestCommand) { out << assignment._walletUUID; @@ -154,7 +164,6 @@ QDataStream& operator>>(QDataStream &in, Assignment& assignment) { quint8 packedType; in >> packedType; if (assignment._command == Assignment::RequestCommand) { - qDebug() << "We are extracting the version"; in >> assignment._nodeVersion; } assignment._type = (Assignment::Type) packedType; @@ -162,7 +171,6 @@ QDataStream& operator>>(QDataStream &in, Assignment& assignment) { in >> assignment._uuid >> assignment._pool >> assignment._payload; if (assignment._command == Assignment::RequestCommand) { - qDebug() << "LEOTEST: Operator for >> in case of RequestCommand"; in >> assignment._walletUUID; } diff --git a/libraries/networking/src/Assignment.h b/libraries/networking/src/Assignment.h index f1b6cc7fba..6950223a9d 100644 --- a/libraries/networking/src/Assignment.h +++ b/libraries/networking/src/Assignment.h @@ -83,6 +83,8 @@ public: void setWalletUUID(const QUuid& walletUUID) { _walletUUID = walletUUID; } const QUuid& getWalletUUID() const { return _walletUUID; } + const QString& getNodeVersion() const { return _nodeVersion; } + const char* getTypeName() const; friend QDebug operator<<(QDebug debug, const Assignment& assignment); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 9ecf31edab..563b7d7080 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -546,14 +546,9 @@ void NodeList::sendAssignment(Assignment& assignment) { ? PacketType::CreateAssignment : PacketType::RequestAssignment; - qDebug() << "LEOTEST: Packet type name " << nameForPacketType(assignmentPacketType); auto assignmentPacket = NLPacket::create(assignmentPacketType); QDataStream packetStream(assignmentPacket.get()); - if (assignmentPacketType == PacketType::RequestAssignment) { - qDebug() << "LEOTEST: This is an assignment request, lets send the node version here " << BUILD_VERSION; - packetStream << BUILD_VERSION; - } packetStream << assignment; // TODO: should this be a non sourced packet? From b345d75e4d897f3b20f43de2705f6586aa04d9c4 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 13 Aug 2015 16:44:28 -0600 Subject: [PATCH 008/103] More cleanup and showing version in ds web gui --- domain-server/resources/web/index.shtml | 4 +++- libraries/networking/src/Assignment.cpp | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/web/index.shtml b/domain-server/resources/web/index.shtml index b159d624a4..0f720ebe79 100644 --- a/domain-server/resources/web/index.shtml +++ b/domain-server/resources/web/index.shtml @@ -9,6 +9,7 @@ Type + Version UUID Pool Username @@ -24,6 +25,7 @@ <% _.each(nodes, function(node, node_index){ %> <%- node.type %> + <%- node.version %> <%- node.uuid %> <%- node.pool %> <%- node.username %> @@ -75,4 +77,4 @@ - \ No newline at end of file + diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp index 8a97b7cf8c..b63373858e 100644 --- a/libraries/networking/src/Assignment.cpp +++ b/libraries/networking/src/Assignment.cpp @@ -13,7 +13,6 @@ #include "SharedUtil.h" #include "UUID.h" -#include #include #include From daeb90c1e0644c30d9f4338972a7e36bb7dd1a86 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 14 Aug 2015 09:35:39 -0700 Subject: [PATCH 009/103] fix clamp in AddEntityOperator --- libraries/entities/src/AddEntityOperator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/entities/src/AddEntityOperator.cpp b/libraries/entities/src/AddEntityOperator.cpp index db9a18a3e9..51015c1ff4 100644 --- a/libraries/entities/src/AddEntityOperator.cpp +++ b/libraries/entities/src/AddEntityOperator.cpp @@ -26,8 +26,7 @@ AddEntityOperator::AddEntityOperator(EntityTree* tree, // caller must have verified existence of newEntity assert(_newEntity); - // FIXME - how does this change for new origin??? - _newEntityBox = _newEntity->getMaximumAACube().clamp(0.0f, (float)TREE_SCALE); + _newEntityBox = _newEntity->getMaximumAACube().clamp((float)(-HALF_TREE_SCALE), (float)HALF_TREE_SCALE); } bool AddEntityOperator::preRecursion(OctreeElement* element) { From bdc78e9666a6fe95f9b931320f0571cd8d7896d6 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 14 Aug 2015 10:16:48 -0700 Subject: [PATCH 010/103] some CR cleanup --- libraries/entities/src/EntitySimulation.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index c614b62c91..06f5023193 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -113,8 +113,7 @@ void EntitySimulation::sortEntitiesThatMoved() { // External changes to entity position/shape are expected to be sorted outside of the EntitySimulation. PerformanceTimer perfTimer("sortingEntities"); MovingEntitiesOperator moveOperator(_entityTree); - AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE, (float)-HALF_TREE_SCALE, (float)-HALF_TREE_SCALE), - (float)TREE_SCALE); + AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE), (float)TREE_SCALE); SetOfEntities::iterator itemItr = _entitiesToSort.begin(); while (itemItr != _entitiesToSort.end()) { EntityItemPointer entity = *itemItr; @@ -196,8 +195,7 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) { bool wasRemoved = false; uint32_t dirtyFlags = entity->getDirtyFlags(); if (dirtyFlags & EntityItem::DIRTY_POSITION) { - AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE, (float)-HALF_TREE_SCALE, (float)-HALF_TREE_SCALE), - (float)TREE_SCALE); + AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE), (float)TREE_SCALE); AACube newCube = entity->getMaximumAACube(); if (!domainBounds.touches(newCube)) { qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds."; From 7c6a9b131d581644400879658d280435a11cfb22 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 14 Aug 2015 10:17:48 -0700 Subject: [PATCH 011/103] some CR cleanup --- interface/src/Util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index f62a6b8fc5..5a9ecfee67 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -44,7 +44,7 @@ void renderWorldBox(gpu::Batch& batch) { auto transform = Transform{}; batch.setModelTransform(transform); - // FIXME - new origin tweaks need to be done to this + // TODO - consider alternate rendering for negative build-able space in the domain geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(TREE_SCALE, 0.0f, 0.0f), red); geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, TREE_SCALE, 0.0f), green); geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, TREE_SCALE), blue); From 44fcf71f075bfc3136c0e2c99f6beba523555227 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 14 Aug 2015 15:26:49 -0700 Subject: [PATCH 012/103] change domain to be 32k and still have origin centered at 0 --- examples/edit.js | 2 +- libraries/entities/src/EntityItem.cpp | 13 ++----------- libraries/entities/src/EntityItem.h | 1 - libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 - libraries/octree/src/OctreeConstants.h | 2 +- 6 files changed, 5 insertions(+), 16 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index 4cc38a0c3d..aea08640aa 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1020,7 +1020,7 @@ function getPositionToCreateEntity() { var cameraPosition = Camera.position; - var HALF_TREE_SCALE = 16384 / 2; + var HALF_TREE_SCALE = 16384; var cameraOutOfBounds = cameraPosition.x < -HALF_TREE_SCALE || cameraPosition.y < -HALF_TREE_SCALE || cameraPosition.z < -HALF_TREE_SCALE; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 922170707b..0ffcc00ead 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -634,11 +634,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // but since we're using macros below we have to temporarily modify overwriteLocalData. bool oldOverwrite = overwriteLocalData; overwriteLocalData = overwriteLocalData && !weOwnSimulation; - if (args.bitstreamVersion >= VERSION_ENTITIES_CENTER_ORIGIN) { - READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition); - } else { - READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePositionOldOrigin); - } + READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition); READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation); READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity); READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity); @@ -660,7 +656,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef } else { // legacy order of packing here // TODO: purge this logic in a few months from now (2015.07) - READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePositionOldOrigin); + READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition); READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions); READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation); READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity); @@ -1321,11 +1317,6 @@ void EntityItem::updatePosition(const glm::vec3& value) { } } -void EntityItem::updatePositionOldOrigin(const glm::vec3& value) { - glm::vec3 newValue = value - glm::vec3(HALF_TREE_SCALE); - updatePosition(newValue); -} - void EntityItem::updateDimensions(const glm::vec3& value) { auto delta = glm::distance(getDimensions(), value); if (delta > IGNORE_DIMENSIONS_DELTA) { diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 936b2e4d21..e420d08709 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -363,7 +363,6 @@ public: // updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags void updatePosition(const glm::vec3& value); - void updatePositionOldOrigin(const glm::vec3& value); void updateDimensions(const glm::vec3& value); void updateRotation(const glm::quat& rotation); void updateDensity(float value); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index a1af3c58c5..5562490344 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -67,7 +67,7 @@ PacketVersion versionForPacketType(PacketType::Value packetType) { case EntityAdd: case EntityEdit: case EntityData: - return VERSION_ENTITIES_CENTER_ORIGIN; + return VERSION_ENTITIES_POLYLINE; case AvatarData: return 12; default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 613ea4913d..1aeadb1af9 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -142,6 +142,5 @@ const PacketVersion VERSION_ENTITIES_HAVE_SIMULATION_OWNER_AND_ACTIONS_OVER_WIRE const PacketVersion VERSION_ENTITIES_NEW_PROTOCOL_LAYER = 35; const PacketVersion VERSION_POLYVOX_TEXTURES = 36; const PacketVersion VERSION_ENTITIES_POLYLINE = 37; -const PacketVersion VERSION_ENTITIES_CENTER_ORIGIN = 38; #endif // hifi_PacketHeaders_h \ No newline at end of file diff --git a/libraries/octree/src/OctreeConstants.h b/libraries/octree/src/OctreeConstants.h index acfa1d14c6..a98f042006 100644 --- a/libraries/octree/src/OctreeConstants.h +++ b/libraries/octree/src/OctreeConstants.h @@ -17,7 +17,7 @@ const quint64 CHANGE_FUDGE = 1000 * 200; // useconds of fudge in determining if we want to resend changed voxels -const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe +const int TREE_SCALE = 32768; // ~20 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe const int HALF_TREE_SCALE = TREE_SCALE / 2; // This controls the LOD. Larger number will make smaller voxels visible at greater distance. From 65864b1876e4f1843c853f3e5ae28bbb5ce3bb48 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 14 Aug 2015 15:30:16 -0700 Subject: [PATCH 013/103] only draw axis for HALF_TREE_SCALE --- interface/src/Util.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 5a9ecfee67..8f687a93d6 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -45,11 +45,11 @@ void renderWorldBox(gpu::Batch& batch) { batch.setModelTransform(transform); // TODO - consider alternate rendering for negative build-able space in the domain - geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(TREE_SCALE, 0.0f, 0.0f), red); - geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, TREE_SCALE, 0.0f), green); - geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, TREE_SCALE), blue); - geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, TREE_SCALE), glm::vec3(TREE_SCALE, 0.0f, TREE_SCALE), grey); - geometryCache->renderLine(batch, glm::vec3(TREE_SCALE, 0.0f, TREE_SCALE), glm::vec3(TREE_SCALE, 0.0f, 0.0f), grey); + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(HALF_TREE_SCALE, 0.0f, 0.0f), red); + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, HALF_TREE_SCALE, 0.0f), green); + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, HALF_TREE_SCALE), blue); + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), grey); + geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, 0.0f), grey); // Draw meter markers along the 3 axis to help with measuring things const float MARKER_DISTANCE = 1.0f; From 708190bfd6cefc0f746a3746c703fe756fc675ea Mon Sep 17 00:00:00 2001 From: Christopher Root Date: Sat, 15 Aug 2015 14:10:25 -0700 Subject: [PATCH 014/103] horiz amb occl performance upgrades --- .../src/AmbientOcclusionEffect.cpp | 53 ++++++-- .../render-utils/src/AmbientOcclusionEffect.h | 11 +- .../render-utils/src/ambient_occlusion.slf | 126 +++++++++++++----- 3 files changed, 142 insertions(+), 48 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 5f0afd37d1..b94bd09538 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -51,8 +51,13 @@ const gpu::PipelinePointer& AmbientOcclusion::getOcclusionPipeline() { _gBiasLoc = program->getUniforms().findLocation("g_bias"); _gSampleRadiusLoc = program->getUniforms().findLocation("g_sample_rad"); _gIntensityLoc = program->getUniforms().findLocation("g_intensity"); - _bufferWidthLoc = program->getUniforms().findLocation("bufferWidth"); - _bufferHeightLoc = program->getUniforms().findLocation("bufferHeight"); + + _nearLoc = program->getUniforms().findLocation("near"); + _depthScaleLoc = program->getUniforms().findLocation("depthScale"); + _depthTexCoordOffsetLoc = program->getUniforms().findLocation("depthTexCoordOffset"); + _depthTexCoordScaleLoc = program->getUniforms().findLocation("depthTexCoordScale"); + _renderTargetResLoc = program->getUniforms().findLocation("renderTargetRes"); + _renderTargetResInvLoc = program->getUniforms().findLocation("renderTargetResInv"); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); @@ -172,9 +177,19 @@ const gpu::PipelinePointer& AmbientOcclusion::getBlendPipeline() { void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { assert(renderContext->args); assert(renderContext->args->_viewFrustum); - RenderArgs* args = renderContext->args; gpu::Batch batch; + RenderArgs* args = renderContext->args; + + auto framebufferCache = DependencyManager::get(); + QSize framebufferSize = framebufferCache->getFrameBufferSize(); + float fbWidth = framebufferSize.width(); + float fbHeight = framebufferSize.height(); + float sMin = args->_viewport.x / fbWidth; + float sWidth = args->_viewport.z / fbWidth; + float tMin = args->_viewport.y / fbHeight; + float tHeight = args->_viewport.w / fbHeight; + glm::mat4 projMat; Transform viewMat; @@ -186,8 +201,8 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons // Occlusion step getOcclusionPipeline(); - batch.setResourceTexture(0, DependencyManager::get()->getPrimaryDepthTexture()); - batch.setResourceTexture(1, DependencyManager::get()->getPrimaryNormalTexture()); + batch.setResourceTexture(0, framebufferCache->getPrimaryDepthTexture()); + batch.setResourceTexture(1, framebufferCache->getPrimaryNormalTexture()); _occlusionBuffer->setRenderBuffer(0, _occlusionTexture); batch.setFramebuffer(_occlusionBuffer); @@ -203,8 +218,28 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons batch._glUniform1f(_gBiasLoc, g_bias); batch._glUniform1f(_gSampleRadiusLoc, g_sample_rad); batch._glUniform1f(_gIntensityLoc, g_intensity); - batch._glUniform1f(_bufferWidthLoc, DependencyManager::get()->getFrameBufferSize().width()); - batch._glUniform1f(_bufferHeightLoc, DependencyManager::get()->getFrameBufferSize().height()); + + // setup uniforms for extracting depth from the depth buffer and + // converting that depth to a camera-space position, same as DeferredLightingEffect.cpp + float left, right, bottom, top, nearVal, farVal; + glm::vec4 nearClipPlane, farClipPlane; + args->_viewFrustum->computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); + + batch._glUniform1f(_nearLoc, nearVal); + + float depthScale = (farVal - nearVal) / farVal; + batch._glUniform1f(_depthScaleLoc, depthScale); + + float nearScale = -1.0f / nearVal; + float depthTexCoordScaleS = (right - left) * nearScale / sWidth; + float depthTexCoordScaleT = (top - bottom) * nearScale / tHeight; + float depthTexCoordOffsetS = left * nearScale - sMin * depthTexCoordScaleS; + float depthTexCoordOffsetT = bottom * nearScale - tMin * depthTexCoordScaleT; + batch._glUniform2f(_depthTexCoordOffsetLoc, depthTexCoordOffsetS, depthTexCoordOffsetT); + batch._glUniform2f(_depthTexCoordScaleLoc, depthTexCoordScaleS, depthTexCoordScaleT); + + batch._glUniform2f(_renderTargetResLoc, fbWidth, fbHeight); + batch._glUniform2f(_renderTargetResInvLoc, 1.0/fbWidth, 1.0/fbHeight); glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f); glm::vec2 bottomLeft(-1.0f, -1.0f); @@ -238,13 +273,13 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons // Blend step getBlendPipeline(); batch.setResourceTexture(0, _hBlurTexture); - batch.setFramebuffer(DependencyManager::get()->getPrimaryFramebuffer()); + batch.setFramebuffer(framebufferCache->getPrimaryFramebuffer()); // Bind the fourth gpu::Pipeline we need - for blending the primary color buffer with blurred occlusion texture batch.setPipeline(getBlendPipeline()); DependencyManager::get()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); - + // Ready to render args->_context->syncCache(); args->_context->render((batch)); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 0b695dd2ad..6153795ea6 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -36,8 +36,15 @@ private: gpu::int32 _gBiasLoc; gpu::int32 _gSampleRadiusLoc; gpu::int32 _gIntensityLoc; - gpu::int32 _bufferWidthLoc; - gpu::int32 _bufferHeightLoc; + + gpu::int32 _nearLoc; + gpu::int32 _depthScaleLoc; + gpu::int32 _depthTexCoordOffsetLoc; + gpu::int32 _depthTexCoordScaleLoc; + gpu::int32 _renderTargetResLoc; + gpu::int32 _renderTargetResInvLoc; + + float g_scale; float g_bias; float g_sample_rad; diff --git a/libraries/render-utils/src/ambient_occlusion.slf b/libraries/render-utils/src/ambient_occlusion.slf index 649fb16c56..73bed26a9c 100644 --- a/libraries/render-utils/src/ambient_occlusion.slf +++ b/libraries/render-utils/src/ambient_occlusion.slf @@ -30,25 +30,49 @@ uniform float g_scale; uniform float g_bias; uniform float g_sample_rad; uniform float g_intensity; -uniform float bufferWidth; -uniform float bufferHeight; + +// the distance to the near clip plane +uniform float near; + +// scale factor for depth: (far - near) / far +uniform float depthScale; + +// offset for depth texture coordinates +uniform vec2 depthTexCoordOffset; + +// scale for depth texture coordinates +uniform vec2 depthTexCoordScale; + +// the resolution of the occlusion buffer +// and its inverse +uniform vec2 renderTargetRes; +uniform vec2 renderTargetResInv; + + const float PI = 3.14159265; -const vec2 FocalLen = vec2(1.0, 1.0); +// const vec2 FocalLen = vec2(1.0, 1.0); +// const vec2 LinMAD = vec2(0.1-10.0, 0.1+10.0) / (2.0*0.1*10.0); -const vec2 LinMAD = vec2(0.1-10.0, 0.1+10.0) / (2.0*0.1*10.0); - -const vec2 AORes = vec2(1024.0, 768.0); -const vec2 InvAORes = vec2(1.0/1024.0, 1.0/768.0); -const vec2 NoiseScale = vec2(1024.0, 768.0) / 4.0; +// const vec2 AORes = vec2(1024.0, 768.0); +// const vec2 InvAORes = vec2(1.0/1024.0, 1.0/768.0); +// const vec2 NoiseScale = vec2(1024.0, 768.0) / 4.0; const float AOStrength = 1.9; -const float R = 0.3; -const float R2 = 0.3*0.3; -const float NegInvR2 = - 1.0 / (0.3*0.3); + +// const float R = 0.3; +// const float R2 = 0.3*0.3; +// const float NegInvR2 = - 1.0 / (0.3*0.3); + +const float R = 0.01; +const float R2 = 0.01*0.01; +const float NegInvR2 = - 1.0 / (0.01*0.01); + + + // can't use tan to initialize a const value -const float TanBias = 0.57735027; // tan(30.0 * PI / 180.0); +const float TanBias = 0.57735027; // tan(30.0 * PI / 180.0); const float MaxRadiusPixels = 50.0; const int NumDirections = 6; @@ -56,30 +80,43 @@ const int NumSamples = 4; out vec4 outFragColor; -float ViewSpaceZFromDepth(float d){ - // [0,1] -> [-1,1] clip space - d = d * 2.0 - 1.0; +// float ViewSpaceZFromDepth(float d){ +// // [0,1] -> [-1,1] clip space +// d = d * 2.0 - 1.0; - // Get view space Z - return -1.0 / (LinMAD.x * d + LinMAD.y); +// // Get view space Z +// return -1.0 / (LinMAD.x * d + LinMAD.y); +// } + +// vec3 UVToViewSpace(vec2 uv, float z){ +// //uv = UVToViewA * uv + UVToViewB; +// return vec3(uv * z, z); +// } + +// vec3 GetViewPos(vec2 uv){ +// float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r); +// return UVToViewSpace(uv, z); +// } + +vec3 GetViewNormalFromTexture(vec2 uv) { + // convert [0,1] -> [-1,1], note: since we're normalizing + // we don't need to do v*2 - 1.0, we can just do a v-0.5 + return normalize(texture(normalTexture, uv).xyz - 0.5); +} + +float ViewSpaceZFromDepth(float d){ + return near / (d * depthScale - 1.0); } vec3 UVToViewSpace(vec2 uv, float z){ - //uv = UVToViewA * uv + UVToViewB; - return vec3(uv * z, z); + return vec3((depthTexCoordOffset + varTexcoord * depthTexCoordScale) * z, z); } vec3 GetViewPos(vec2 uv){ - float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r); - return UVToViewSpace(uv, z); + float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r); + return UVToViewSpace(uv, z); } -vec3 GetViewPosPoint(ivec2 uv){ - vec2 coord = vec2(gl_FragCoord.xy) + uv; - //float z = texelFetch(texture0, coord, 0).r; - float z = texture(depthTexture, uv).r; - return UVToViewSpace(uv, z); -} float TanToSin(float x){ return x * inversesqrt(x*x + 1.0); @@ -112,7 +149,8 @@ vec3 MinDiff(vec3 P, vec3 Pr, vec3 Pl){ } vec2 SnapUVOffset(vec2 uv){ - return round(uv * AORes) * InvAORes; + // return round(uv * AORes) * InvAORes; + return round(uv * renderTargetRes) * renderTargetResInv; } float Falloff(float d2){ @@ -180,7 +218,8 @@ void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPi } // Step size in uv space - stepSizeUv = stepSizePix * InvAORes; + // stepSizeUv = stepSizePix * InvAORes; + stepSizeUv = stepSizePix * renderTargetResInv; } float getRandom(vec2 uv){ @@ -188,27 +227,39 @@ float getRandom(vec2 uv){ } void main(void){ + mat4 projMatrix = getTransformCamera()._projection; + float numDirections = NumDirections; vec3 P, Pr, Pl, Pt, Pb; P = GetViewPos(varTexcoord); // Sample neighboring pixels - Pr = GetViewPos(varTexcoord + vec2( InvAORes.x, 0)); - Pl = GetViewPos(varTexcoord + vec2(-InvAORes.x, 0)); - Pt = GetViewPos(varTexcoord + vec2( 0, InvAORes.y)); - Pb = GetViewPos(varTexcoord + vec2( 0,-InvAORes.y)); + // Pr = GetViewPos(varTexcoord + vec2( InvAORes.x, 0)); + // Pl = GetViewPos(varTexcoord + vec2(-InvAORes.x, 0)); + // Pt = GetViewPos(varTexcoord + vec2( 0, InvAORes.y)); + // Pb = GetViewPos(varTexcoord + vec2( 0,-InvAORes.y)); + Pr = GetViewPos(varTexcoord + vec2( renderTargetResInv.x, 0)); + Pl = GetViewPos(varTexcoord + vec2(-renderTargetResInv.x, 0)); + Pt = GetViewPos(varTexcoord + vec2( 0, renderTargetResInv.y)); + Pb = GetViewPos(varTexcoord + vec2( 0,-renderTargetResInv.y)); // Calculate tangent basis vectors using the minimum difference vec3 dPdu = MinDiff(P, Pr, Pl); - vec3 dPdv = MinDiff(P, Pt, Pb) * (AORes.y * InvAORes.x); + // vec3 dPdv = MinDiff(P, Pt, Pb) * (AORes.y * InvAORes.x); + vec3 dPdv = MinDiff(P, Pt, Pb) * (renderTargetRes.y * renderTargetResInv.x); // Get the random samples from the noise function vec3 random = vec3(getRandom(varTexcoord.xy), getRandom(varTexcoord.yx), getRandom(varTexcoord.xx)); // Calculate the projected size of the hemisphere - vec2 rayRadiusUV = 0.5 * R * FocalLen / -P.z; - float rayRadiusPix = rayRadiusUV.x * AORes.x; + // vec2 rayRadiusUV = 0.5 * R * FocalLen / -P.z; + // float rayRadiusPix = rayRadiusUV.x * AORes.x; + + // project the radius of the hemisphere into screen space + float w = P.z * projMatrix[2][3] + projMatrix[3][3]; + vec2 rayRadiusUV = (0.5 * R * vec2(projMatrix[0][0], projMatrix[1][1]) / w); // [-1,1] -> [0,1] uv + float rayRadiusPix = rayRadiusUV.x * renderTargetRes.x; float ao = 1.0; @@ -244,5 +295,6 @@ void main(void){ ao = 1.0 - ao / numDirections * AOStrength; } + outFragColor = vec4(vec3(ao), 1.0); -} \ No newline at end of file +} From 81ea8e88743bfd6f2ec6710da649dddf8018508a Mon Sep 17 00:00:00 2001 From: Christopher Root Date: Sat, 15 Aug 2015 14:27:08 -0700 Subject: [PATCH 015/103] Updating some comments and removing unnecessary code --- .../src/AmbientOcclusionEffect.cpp | 16 ++++--- .../render-utils/src/ambient_occlusion.slf | 47 +++++++------------ 2 files changed, 28 insertions(+), 35 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index b94bd09538..962b57a22c 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -219,22 +219,26 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons batch._glUniform1f(_gSampleRadiusLoc, g_sample_rad); batch._glUniform1f(_gIntensityLoc, g_intensity); - // setup uniforms for extracting depth from the depth buffer and - // converting that depth to a camera-space position, same as DeferredLightingEffect.cpp + // setup uniforms for unpacking a view-space position from the depth buffer + // This is code taken from DeferredLightEffect.render() method in DeferredLightingEffect.cpp. + // DeferredBuffer.slh shows how the unpacking is done and what variables are needed. + + // initialize the view-space unpacking uniforms using frustum data float left, right, bottom, top, nearVal, farVal; glm::vec4 nearClipPlane, farClipPlane; + args->_viewFrustum->computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); - batch._glUniform1f(_nearLoc, nearVal); - float depthScale = (farVal - nearVal) / farVal; - batch._glUniform1f(_depthScaleLoc, depthScale); - float nearScale = -1.0f / nearVal; float depthTexCoordScaleS = (right - left) * nearScale / sWidth; float depthTexCoordScaleT = (top - bottom) * nearScale / tHeight; float depthTexCoordOffsetS = left * nearScale - sMin * depthTexCoordScaleS; float depthTexCoordOffsetT = bottom * nearScale - tMin * depthTexCoordScaleT; + + // now set the position-unpacking unforms + batch._glUniform1f(_nearLoc, nearVal); + batch._glUniform1f(_depthScaleLoc, depthScale); batch._glUniform2f(_depthTexCoordOffsetLoc, depthTexCoordOffsetS, depthTexCoordOffsetT); batch._glUniform2f(_depthTexCoordScaleLoc, depthTexCoordScaleS, depthTexCoordScaleT); diff --git a/libraries/render-utils/src/ambient_occlusion.slf b/libraries/render-utils/src/ambient_occlusion.slf index 73bed26a9c..44ff79a179 100644 --- a/libraries/render-utils/src/ambient_occlusion.slf +++ b/libraries/render-utils/src/ambient_occlusion.slf @@ -52,19 +52,8 @@ uniform vec2 renderTargetResInv; const float PI = 3.14159265; -// const vec2 FocalLen = vec2(1.0, 1.0); -// const vec2 LinMAD = vec2(0.1-10.0, 0.1+10.0) / (2.0*0.1*10.0); - -// const vec2 AORes = vec2(1024.0, 768.0); -// const vec2 InvAORes = vec2(1.0/1024.0, 1.0/768.0); -// const vec2 NoiseScale = vec2(1024.0, 768.0) / 4.0; - const float AOStrength = 1.9; -// const float R = 0.3; -// const float R2 = 0.3*0.3; -// const float NegInvR2 = - 1.0 / (0.3*0.3); - const float R = 0.01; const float R2 = 0.01*0.01; const float NegInvR2 = - 1.0 / (0.01*0.01); @@ -80,38 +69,38 @@ const int NumSamples = 4; out vec4 outFragColor; -// float ViewSpaceZFromDepth(float d){ -// // [0,1] -> [-1,1] clip space -// d = d * 2.0 - 1.0; - -// // Get view space Z -// return -1.0 / (LinMAD.x * d + LinMAD.y); -// } - -// vec3 UVToViewSpace(vec2 uv, float z){ -// //uv = UVToViewA * uv + UVToViewB; -// return vec3(uv * z, z); -// } - -// vec3 GetViewPos(vec2 uv){ -// float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r); -// return UVToViewSpace(uv, z); -// } - +/** + * Gets the normal in view space from a normal texture. + * uv: the uv texture coordinates to look up in the texture at. + */ vec3 GetViewNormalFromTexture(vec2 uv) { // convert [0,1] -> [-1,1], note: since we're normalizing // we don't need to do v*2 - 1.0, we can just do a v-0.5 return normalize(texture(normalTexture, uv).xyz - 0.5); } +/** + * Gets the linearized depth in view space. + * d: the depth value [0-1], usually from a depth texture to convert. + */ float ViewSpaceZFromDepth(float d){ return near / (d * depthScale - 1.0); } +/** + * Converts a uv coordinate and depth value into a 3D view space coordinate. + * uv: the uv coordinates to convert + * z: the view space depth of the uv coordinate. + */ vec3 UVToViewSpace(vec2 uv, float z){ return vec3((depthTexCoordOffset + varTexcoord * depthTexCoordScale) * z, z); } +/** + * Converts a uv coordinate into a 3D view space coordinate. + * The depth of the uv coord is determined from the depth texture. + * uv: the uv coordinates to convert + */ vec3 GetViewPos(vec2 uv){ float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r); return UVToViewSpace(uv, z); From 8ea6048ec75d8c21c3f6d32090ad5fda8f736432 Mon Sep 17 00:00:00 2001 From: Christopher Root Date: Sat, 15 Aug 2015 14:31:03 -0700 Subject: [PATCH 016/103] Removing commented out code --- libraries/render-utils/src/ambient_occlusion.slf | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/libraries/render-utils/src/ambient_occlusion.slf b/libraries/render-utils/src/ambient_occlusion.slf index 44ff79a179..f304644604 100644 --- a/libraries/render-utils/src/ambient_occlusion.slf +++ b/libraries/render-utils/src/ambient_occlusion.slf @@ -54,6 +54,8 @@ const float PI = 3.14159265; const float AOStrength = 1.9; + +// TODO: R (radius) should be exposed as a uniform parameter const float R = 0.01; const float R2 = 0.01*0.01; const float NegInvR2 = - 1.0 / (0.01*0.01); @@ -224,10 +226,6 @@ void main(void){ P = GetViewPos(varTexcoord); // Sample neighboring pixels - // Pr = GetViewPos(varTexcoord + vec2( InvAORes.x, 0)); - // Pl = GetViewPos(varTexcoord + vec2(-InvAORes.x, 0)); - // Pt = GetViewPos(varTexcoord + vec2( 0, InvAORes.y)); - // Pb = GetViewPos(varTexcoord + vec2( 0,-InvAORes.y)); Pr = GetViewPos(varTexcoord + vec2( renderTargetResInv.x, 0)); Pl = GetViewPos(varTexcoord + vec2(-renderTargetResInv.x, 0)); Pt = GetViewPos(varTexcoord + vec2( 0, renderTargetResInv.y)); @@ -235,17 +233,12 @@ void main(void){ // Calculate tangent basis vectors using the minimum difference vec3 dPdu = MinDiff(P, Pr, Pl); - // vec3 dPdv = MinDiff(P, Pt, Pb) * (AORes.y * InvAORes.x); vec3 dPdv = MinDiff(P, Pt, Pb) * (renderTargetRes.y * renderTargetResInv.x); // Get the random samples from the noise function vec3 random = vec3(getRandom(varTexcoord.xy), getRandom(varTexcoord.yx), getRandom(varTexcoord.xx)); // Calculate the projected size of the hemisphere - // vec2 rayRadiusUV = 0.5 * R * FocalLen / -P.z; - // float rayRadiusPix = rayRadiusUV.x * AORes.x; - - // project the radius of the hemisphere into screen space float w = P.z * projMatrix[2][3] + projMatrix[3][3]; vec2 rayRadiusUV = (0.5 * R * vec2(projMatrix[0][0], projMatrix[1][1]) / w); // [-1,1] -> [0,1] uv float rayRadiusPix = rayRadiusUV.x * renderTargetRes.x; From 21dc58eb85fcf795f98fa8e7be7d24ccc1274e10 Mon Sep 17 00:00:00 2001 From: Christopher Root Date: Sat, 15 Aug 2015 14:40:34 -0700 Subject: [PATCH 017/103] Improving readability in shader --- .../render-utils/src/ambient_occlusion.slf | 167 +++++++++--------- 1 file changed, 82 insertions(+), 85 deletions(-) diff --git a/libraries/render-utils/src/ambient_occlusion.slf b/libraries/render-utils/src/ambient_occlusion.slf index f304644604..8ab78891b0 100644 --- a/libraries/render-utils/src/ambient_occlusion.slf +++ b/libraries/render-utils/src/ambient_occlusion.slf @@ -103,95 +103,94 @@ vec3 UVToViewSpace(vec2 uv, float z){ * The depth of the uv coord is determined from the depth texture. * uv: the uv coordinates to convert */ -vec3 GetViewPos(vec2 uv){ +vec3 GetViewPos(vec2 uv) { float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r); return UVToViewSpace(uv, z); } -float TanToSin(float x){ - return x * inversesqrt(x*x + 1.0); +float TanToSin(float x) { + return x * inversesqrt(x*x + 1.0); } -float InvLength(vec2 V){ - return inversesqrt(dot(V,V)); +float InvLength(vec2 V) { + return inversesqrt(dot(V, V)); } -float Tangent(vec3 V){ - return V.z * InvLength(V.xy); +float Tangent(vec3 V) { + return V.z * InvLength(V.xy); } -float BiasedTangent(vec3 V){ - return V.z * InvLength(V.xy) + TanBias; +float BiasedTangent(vec3 V) { + return V.z * InvLength(V.xy) + TanBias; } -float Tangent(vec3 P, vec3 S){ +float Tangent(vec3 P, vec3 S) { return -(P.z - S.z) * InvLength(S.xy - P.xy); } -float Length2(vec3 V){ - return dot(V,V); +float Length2(vec3 V) { + return dot(V, V); } -vec3 MinDiff(vec3 P, vec3 Pr, vec3 Pl){ +vec3 MinDiff(vec3 P, vec3 Pr, vec3 Pl) { vec3 V1 = Pr - P; vec3 V2 = P - Pl; return (Length2(V1) < Length2(V2)) ? V1 : V2; } -vec2 SnapUVOffset(vec2 uv){ - // return round(uv * AORes) * InvAORes; +vec2 SnapUVOffset(vec2 uv) { return round(uv * renderTargetRes) * renderTargetResInv; } -float Falloff(float d2){ - return d2 * NegInvR2 + 1.0f; +float Falloff(float d2) { + return d2 * NegInvR2 + 1.0f; } -float HorizonOcclusion( vec2 deltaUV, vec3 P, vec3 dPdu, vec3 dPdv, float randstep, float numSamples){ - float ao = 0; +float HorizonOcclusion(vec2 deltaUV, vec3 P, vec3 dPdu, vec3 dPdv, float randstep, float numSamples) { + float ao = 0; - // Offset the first coord with some noise - vec2 uv = varTexcoord + SnapUVOffset(randstep*deltaUV); - deltaUV = SnapUVOffset( deltaUV ); + // Offset the first coord with some noise + vec2 uv = varTexcoord + SnapUVOffset(randstep*deltaUV); + deltaUV = SnapUVOffset(deltaUV); - // Calculate the tangent vector - vec3 T = deltaUV.x * dPdu + deltaUV.y * dPdv; + // Calculate the tangent vector + vec3 T = deltaUV.x * dPdu + deltaUV.y * dPdv; - // Get the angle of the tangent vector from the viewspace axis - float tanH = BiasedTangent(T); - float sinH = TanToSin(tanH); + // Get the angle of the tangent vector from the viewspace axis + float tanH = BiasedTangent(T); + float sinH = TanToSin(tanH); - float tanS; - float d2; - vec3 S; + float tanS; + float d2; + vec3 S; - // Sample to find the maximum angle - for(float s = 1; s <= numSamples; ++s){ - uv += deltaUV; - S = GetViewPos(uv); - tanS = Tangent(P, S); - d2 = Length2(S - P); + // Sample to find the maximum angle + for (float s = 1; s <= numSamples; ++s) { + uv += deltaUV; + S = GetViewPos(uv); + tanS = Tangent(P, S); + d2 = Length2(S - P); - // Is the sample within the radius and the angle greater? - if(d2 < R2 && tanS > tanH) - { - float sinS = TanToSin(tanS); - // Apply falloff based on the distance - ao += Falloff(d2) * (sinS - sinH); + // Is the sample within the radius and the angle greater? + if (d2 < R2 && tanS > tanH) { + float sinS = TanToSin(tanS); + // Apply falloff based on the distance + ao += Falloff(d2) * (sinS - sinH); - tanH = tanS; - sinH = sinS; - } - } - return ao; + tanH = tanS; + sinH = sinS; + } + } + return ao; } -vec2 RotateDirections(vec2 Dir, vec2 CosSin){ - return vec2(Dir.x*CosSin.x - Dir.y*CosSin.y, Dir.x*CosSin.y + Dir.y*CosSin.x); +vec2 RotateDirections(vec2 Dir, vec2 CosSin) { + return vec2(Dir.x*CosSin.x - Dir.y*CosSin.y, + Dir.x*CosSin.y + Dir.y*CosSin.x); } -void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPix, float rand){ +void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPix, float rand) { // Avoid oversampling if numSteps is greater than the kernel radius in pixels numSteps = min(NumSamples, rayRadiusPix); @@ -200,8 +199,7 @@ void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPi // Clamp numSteps if it is greater than the max kernel footprint float maxNumSteps = MaxRadiusPixels / stepSizePix; - if (maxNumSteps < numSteps) - { + if (maxNumSteps < numSteps) { // Use dithering to avoid AO discontinuities numSteps = floor(maxNumSteps + rand); numSteps = max(numSteps, 1); @@ -209,23 +207,22 @@ void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPi } // Step size in uv space - // stepSizeUv = stepSizePix * InvAORes; stepSizeUv = stepSizePix * renderTargetResInv; } -float getRandom(vec2 uv){ +float getRandom(vec2 uv) { return fract(sin(dot(uv.xy ,vec2(12.9898,78.233))) * 43758.5453); } -void main(void){ +void main(void) { mat4 projMatrix = getTransformCamera()._projection; - float numDirections = NumDirections; + float numDirections = NumDirections; - vec3 P, Pr, Pl, Pt, Pb; - P = GetViewPos(varTexcoord); + vec3 P, Pr, Pl, Pt, Pb; + P = GetViewPos(varTexcoord); - // Sample neighboring pixels + // Sample neighboring pixels Pr = GetViewPos(varTexcoord + vec2( renderTargetResInv.x, 0)); Pl = GetViewPos(varTexcoord + vec2(-renderTargetResInv.x, 0)); Pt = GetViewPos(varTexcoord + vec2( 0, renderTargetResInv.y)); @@ -236,9 +233,9 @@ void main(void){ vec3 dPdv = MinDiff(P, Pt, Pb) * (renderTargetRes.y * renderTargetResInv.x); // Get the random samples from the noise function - vec3 random = vec3(getRandom(varTexcoord.xy), getRandom(varTexcoord.yx), getRandom(varTexcoord.xx)); + vec3 random = vec3(getRandom(varTexcoord.xy), getRandom(varTexcoord.yx), getRandom(varTexcoord.xx)); - // Calculate the projected size of the hemisphere + // Calculate the projected size of the hemisphere float w = P.z * projMatrix[2][3] + projMatrix[3][3]; vec2 rayRadiusUV = (0.5 * R * vec2(projMatrix[0][0], projMatrix[1][1]) / w); // [-1,1] -> [0,1] uv float rayRadiusPix = rayRadiusUV.x * renderTargetRes.x; @@ -246,36 +243,36 @@ void main(void){ float ao = 1.0; // Make sure the radius of the evaluated hemisphere is more than a pixel - if(rayRadiusPix > 1.0){ - ao = 0.0; - float numSteps; - vec2 stepSizeUV; + if(rayRadiusPix > 1.0) { + ao = 0.0; + float numSteps; + vec2 stepSizeUV; - // Compute the number of steps - ComputeSteps(stepSizeUV, numSteps, rayRadiusPix, random.z); + // Compute the number of steps + ComputeSteps(stepSizeUV, numSteps, rayRadiusPix, random.z); - float alpha = 2.0 * PI / numDirections; + float alpha = 2.0 * PI / numDirections; - // Calculate the horizon occlusion of each direction - for(float d = 0; d < numDirections; ++d){ - float theta = alpha * d; + // Calculate the horizon occlusion of each direction + for(float d = 0; d < numDirections; ++d) { + float theta = alpha * d; - // Apply noise to the direction - vec2 dir = RotateDirections(vec2(cos(theta), sin(theta)), random.xy); - vec2 deltaUV = dir * stepSizeUV; + // Apply noise to the direction + vec2 dir = RotateDirections(vec2(cos(theta), sin(theta)), random.xy); + vec2 deltaUV = dir * stepSizeUV; - // Sample the pixels along the direction - ao += HorizonOcclusion( deltaUV, - P, - dPdu, - dPdv, - random.z, - numSteps); - } + // Sample the pixels along the direction + ao += HorizonOcclusion( deltaUV, + P, + dPdu, + dPdv, + random.z, + numSteps); + } - // Average the results and produce the final AO - ao = 1.0 - ao / numDirections * AOStrength; - } + // Average the results and produce the final AO + ao = 1.0 - ao / numDirections * AOStrength; + } outFragColor = vec4(vec3(ao), 1.0); From e78d763f85d272d4af889cc08974a6c6ae8df3a5 Mon Sep 17 00:00:00 2001 From: lanac Date: Mon, 17 Aug 2015 12:53:57 +0200 Subject: [PATCH 018/103] shell for Danish translation of Interface --- interface/interface_da.ts | 300 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 interface/interface_da.ts diff --git a/interface/interface_da.ts b/interface/interface_da.ts new file mode 100644 index 0000000000..526eaf7200 --- /dev/null +++ b/interface/interface_da.ts @@ -0,0 +1,300 @@ + + + + + Application + + + Sparse Voxel Octree Files (*.svo) + + + + + Open Script + + + + + JavaScript Files (*.js) + + + + + ChatWindow + + + + Chat + + + + + + Connecting to XMPP... + + + + + + online now: + + + + + day + + %n dag + %n dage + + + + + hour + + %n time + %n timer + + + + + minute + + %n minut + %n minutter + + + + second + + %n second + %n seconds + + + + + %1 online now: + + + + + Dialog + + + + + + Update Required + + + + + + Download + + + + + + Skip Version + + + + + + Close + + + + + Menu + + + Open .ini config file + + + + + + Text files (*.ini) + + + + + Save .ini config file + + + + + PreferencesDialog + + + + Cancel + + + + + + Save all changes + + + + + + + + Avatar + + + + + + <html><head/><body><p>Avatar display name <span style=" color:#909090;">(optional)</span></p></body></html> + + + + + + Not showing a name + + + + + + Head + + + + + + Body + + + + + + Advanced Tuning + + + + + + It's not recomended that you play with these settings unless you've looked into exactly what they do. + + + + + + Vertical field of view + + + + + + Lean scale (applies to Faceshift users) + + + + + + Avatar scale <span style=" color:#909090;">(default is 1.0)</span> + + + + + + Pupil dillation + + + + + + Audio Jitter Buffer Samples (0 for automatic) + + + + + + Faceshift eye detection + + + + + + Octree + + + + + + Max packets sent each second + + + + + QObject + + + Loading ... + + + + + Cancel + + + + + RunningScriptsWidget + + + + Form + + + + + + <html><head/><body><p><span style=" font-size:18pt;">Running Scripts</span></p></body></html> + + + + + + <html><head/><body><p><span style=" font-weight:600;">Currently running</span></p></body></html> + + + + + + Reload all + Reload All + + + + + + Stop all + Stop All + + + + + + <html><head/><body><p><span style=" font-weight:600;">Recently loaded</span></p></body></html> + + + + + + (click a script or use the 1-9 keys to load and run it) + + + + + + There are no scripts currently running. + + + + From 014bf661fa92de5c1c9126783a8d3cb84ed654fd Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 17 Aug 2015 11:18:25 -0700 Subject: [PATCH 019/103] bump packet version --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 5562490344..db2a426696 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -67,7 +67,7 @@ PacketVersion versionForPacketType(PacketType::Value packetType) { case EntityAdd: case EntityEdit: case EntityData: - return VERSION_ENTITIES_POLYLINE; + return VERSION_OCTREE_CENTERED_ORIGIN; case AvatarData: return 12; default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 1aeadb1af9..75d8105dca 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -142,5 +142,6 @@ const PacketVersion VERSION_ENTITIES_HAVE_SIMULATION_OWNER_AND_ACTIONS_OVER_WIRE const PacketVersion VERSION_ENTITIES_NEW_PROTOCOL_LAYER = 35; const PacketVersion VERSION_POLYVOX_TEXTURES = 36; const PacketVersion VERSION_ENTITIES_POLYLINE = 37; +const PacketVersion VERSION_OCTREE_CENTERED_ORIGIN = 38; #endif // hifi_PacketHeaders_h \ No newline at end of file From e43dc3f9fed195c4ee99c1ccc3db81e92a9e8351 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 18 Aug 2015 03:21:48 +0200 Subject: [PATCH 020/103] Completed implementation of WebSocket and WebSocketServer --- libraries/script-engine/src/ScriptEngine.cpp | 2 + .../script-engine/src/WebSocketClass.cpp | 83 ++++++++++++++----- libraries/script-engine/src/WebSocketClass.h | 28 +++++-- .../src/WebSocketServerClass.cpp | 27 +++++- .../script-engine/src/WebSocketServerClass.h | 21 ++++- 5 files changed, 124 insertions(+), 37 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index bde0643c51..c629529382 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -361,6 +361,8 @@ void ScriptEngine::init() { qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue); qScriptRegisterMetaType(this, avatarDataToScriptValue, avatarDataFromScriptValue); qScriptRegisterMetaType(this, animationDetailsToScriptValue, animationDetailsFromScriptValue); + qScriptRegisterMetaType(this, webSocketToScriptValue, webSocketFromScriptValue); + qScriptRegisterMetaType(this, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue); registerGlobalObject("Script", this); registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index f116c3a0e4..654b4746a8 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -16,13 +16,25 @@ #include "WebSocketClass.h" WebSocketClass::WebSocketClass(QScriptEngine* engine, QString url) : - _engine(engine) + _engine(engine), + _webSocket(new QWebSocket()) { - connect(&_webSocket, &QWebSocket::disconnected, this, &WebSocketClass::handleOnClose); - connect(&_webSocket, SIGNAL(error()), this, SLOT(handleOnError())); - connect(&_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketClass::handleOnMessage); - connect(&_webSocket, &QWebSocket::connected, this, &WebSocketClass::handleOnOpen); - _webSocket.open(url); + initialize(); + _webSocket->open(url); +} + +WebSocketClass::WebSocketClass(QScriptEngine* engine, QWebSocket* qWebSocket) : + _engine(engine), + _webSocket(qWebSocket) +{ + initialize(); +} + +void WebSocketClass::initialize() { + connect(_webSocket, &QWebSocket::disconnected, this, &WebSocketClass::handleOnClose); + connect(_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketClass::handleOnMessage); + connect(_webSocket, &QWebSocket::connected, this, &WebSocketClass::handleOnOpen); + _binaryType = "blob"; } QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { @@ -39,26 +51,37 @@ WebSocketClass::~WebSocketClass() { } void WebSocketClass::send(QScriptValue message) { - _webSocket.sendTextMessage(message.toString()); + _webSocket->sendTextMessage(message.toString()); +} + +void WebSocketClass::close() { + this->close(QWebSocketProtocol::CloseCode::CloseCodeNormal); +} + +void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode) { + this->close(closeCode, QStringLiteral("")); } void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode, QString reason) { - _webSocket.close(closeCode, reason); + _webSocket->close(closeCode, reason); } void WebSocketClass::handleOnClose() { - if (_onCloseEvent.isFunction()) { - //QScriptValueList args; - //args << ("received: " + message); - _onCloseEvent.call();//(QScriptValue(), args); + bool hasError = false; + if (_webSocket->error() != QAbstractSocket::SocketError::UnknownSocketError) { + hasError = true; + if (_onErrorEvent.isFunction()) { + _onErrorEvent.call(); + } } -} - -void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) { - if (_onErrorEvent.isFunction()) { - // QScriptValueList args; - //args << ("received: " + message); - _onErrorEvent.call();/// QScriptValue(), args); + if (_onCloseEvent.isFunction()) { + QScriptValueList args; + QScriptValue arg = _engine->newObject(); + arg.setProperty("code", hasError ? QWebSocketProtocol::CloseCode::CloseCodeAbnormalDisconnection : _webSocket->closeCode()); + arg.setProperty("reason", _webSocket->closeReason()); + arg.setProperty("wasClean", !hasError); + args << arg; + _onCloseEvent.call(QScriptValue(), args); } } @@ -74,8 +97,22 @@ void WebSocketClass::handleOnMessage(const QString& message) { void WebSocketClass::handleOnOpen() { if (_onOpenEvent.isFunction()) { - //QScriptValueList args; - //args << ("received: " + message); - _onOpenEvent.call();// QScriptValue(), args); + _onOpenEvent.call(); } -} \ No newline at end of file +} + +QScriptValue qWSCloseCodeToScriptValue(QScriptEngine* engine, const QWebSocketProtocol::CloseCode &closeCode) { + return closeCode; +} + +void qWSCloseCodeFromScriptValue(const QScriptValue &object, QWebSocketProtocol::CloseCode &closeCode) { + closeCode = (QWebSocketProtocol::CloseCode)object.toUInt16(); +} + +QScriptValue webSocketToScriptValue(QScriptEngine* engine, WebSocketClass* const &in) { + return engine->newQObject(in); +} + +void webSocketFromScriptValue(const QScriptValue &object, WebSocketClass* &out) { + out = qobject_cast(object.toQObject()); +} diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index 6f0e3729c0..fc70a72883 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -38,6 +38,7 @@ class WebSocketClass : public QObject { public: WebSocketClass(QScriptEngine* engine, QString url); + WebSocketClass(QScriptEngine* engine, QWebSocket* qWebSocket); ~WebSocketClass(); static QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine); @@ -57,19 +58,18 @@ public: void setBinaryType(QString binaryType) { _binaryType = binaryType; } QString getBinaryType() { return _binaryType; } - // extensions is a empty string until supported in QT + // extensions is a empty string until supported in QT WebSockets QString getExtensions() { return QString(); } - // protocol is a empty string until supported in QT + // protocol is a empty string until supported in QT WebSockets QString getProtocol() { return QString(); } - //TODO: find buffered amount ulong getBufferedAmount() { return 0; } - QString getURL() { return _webSocket.requestUrl().toDisplayString(); } + QString getURL() { return _webSocket->requestUrl().toDisplayString(); } uint getReadyState() { - switch (_webSocket.state()) { + switch (_webSocket->state()) { case QAbstractSocket::SocketState::HostLookupState: case QAbstractSocket::SocketState::ConnectingState: return CONNECTING; @@ -85,19 +85,25 @@ public: void setOnClose(QScriptValue eventFunction) { _onCloseEvent = eventFunction; } QScriptValue getOnClose() { return _onCloseEvent; } + void setOnError(QScriptValue eventFunction) { _onErrorEvent = eventFunction; } QScriptValue getOnError() { return _onErrorEvent; } + void setOnMessage(QScriptValue eventFunction) { _onMessageEvent = eventFunction; } QScriptValue getOnMessage() { return _onMessageEvent; } + void setOnOpen(QScriptValue eventFunction) { _onOpenEvent = eventFunction; } QScriptValue getOnOpen() { return _onOpenEvent; } public slots: void send(QScriptValue message); + + void close(); + void close(QWebSocketProtocol::CloseCode closeCode); void close(QWebSocketProtocol::CloseCode closeCode, QString reason); private: - QWebSocket _webSocket; + QWebSocket* _webSocket; QScriptEngine* _engine; QScriptValue _onCloseEvent; @@ -107,12 +113,20 @@ private: QString _binaryType; + void initialize(); + private slots: void handleOnClose(); - void handleOnError(QAbstractSocket::SocketError error); void handleOnMessage(const QString& message); void handleOnOpen(); }; +Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode); +QScriptValue qWSCloseCodeToScriptValue(QScriptEngine* engine, const QWebSocketProtocol::CloseCode& closeCode); +void qWSCloseCodeFromScriptValue(const QScriptValue& object, QWebSocketProtocol::CloseCode& closeCode); + +QScriptValue webSocketToScriptValue(QScriptEngine* engine, WebSocketClass* const &in); +void webSocketFromScriptValue(const QScriptValue &object, WebSocketClass* &out); + #endif // hifi_WebSocketClass_h diff --git a/libraries/script-engine/src/WebSocketServerClass.cpp b/libraries/script-engine/src/WebSocketServerClass.cpp index 86f6506375..7981d67de3 100644 --- a/libraries/script-engine/src/WebSocketServerClass.cpp +++ b/libraries/script-engine/src/WebSocketServerClass.cpp @@ -14,11 +14,13 @@ #include "ScriptEngine.h" #include "WebSocketServerClass.h" -WebSocketServerClass::WebSocketServerClass(QScriptEngine* engine, const QString& serverName, quint16 port) : +WebSocketServerClass::WebSocketServerClass(QScriptEngine* engine, const QString& serverName, const quint16 port) : _engine(engine), _webSocketServer(serverName, QWebSocketServer::SslMode::NonSecureMode) { - _webSocketServer.listen(QHostAddress::Any, port); + if (_webSocketServer.listen(QHostAddress::Any, port)) { + connect(&_webSocketServer, &QWebSocketServer::newConnection, this, &WebSocketServerClass::onNewConnection); + } } QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptEngine* engine) { @@ -33,12 +35,31 @@ QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptE if (portOption.isValid() && portOption.isNumber()) { port = portOption.toNumber(); } + QScriptValue serverNameOption = options.property(QStringLiteral("serverName")); + if (serverNameOption.isValid() && serverNameOption.isString()) { + serverName = serverNameOption.toString(); + } } return engine->newQObject(new WebSocketServerClass(engine, serverName, port)); } WebSocketServerClass::~WebSocketServerClass() { if (_webSocketServer.isListening()) { - _webSocketServer.close(); + close(); } } + +void WebSocketServerClass::onNewConnection() { + WebSocketClass* newClient = new WebSocketClass(_engine, _webSocketServer.nextPendingConnection()); + _clients << newClient; + emit newConnection(newClient); +} + +void WebSocketServerClass::close() { + foreach(WebSocketClass* client, _clients) { + if (client->getReadyState() != WebSocketClass::ReadyState::CLOSED) { + client->close(QWebSocketProtocol::CloseCode::CloseCodeGoingAway, "Server closing."); + } + } + _webSocketServer.close(); +} diff --git a/libraries/script-engine/src/WebSocketServerClass.h b/libraries/script-engine/src/WebSocketServerClass.h index 18fcdc693e..cad993a508 100644 --- a/libraries/script-engine/src/WebSocketServerClass.h +++ b/libraries/script-engine/src/WebSocketServerClass.h @@ -15,24 +15,37 @@ #include #include #include +#include "WebSocketClass.h" class WebSocketServerClass : public QObject { Q_OBJECT + Q_PROPERTY(QString url READ getURL) + Q_PROPERTY(quint16 port READ getPort) + Q_PROPERTY(bool listening READ isListening) public: - WebSocketServerClass(QScriptEngine* engine, const QString& serverName, quint16 port); + WebSocketServerClass(QScriptEngine* engine, const QString& serverName, const quint16 port); ~WebSocketServerClass(); + QString getURL() { return _webSocketServer.serverUrl().toDisplayString(); } + quint16 getPort() { return _webSocketServer.serverPort(); } + bool isListening() { return _webSocketServer.isListening(); } + static QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptEngine* engine); +public slots: + void close(); + private: QWebSocketServer _webSocketServer; QScriptEngine* _engine; + QList _clients; + +private slots: + void onNewConnection(); signals: - void newConnection(); - - + void newConnection(WebSocketClass* client); }; From 29c857f1fa1d95913e7b3a0fc949c8e7fe5a21e6 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 18 Aug 2015 03:24:26 +0200 Subject: [PATCH 021/103] - unitTests for WebSockets + WebSocketServer (added some asynchronous functionality to the unitTest.js library , previous functionality intact) --- examples/libraries/unitTest.js | 107 +++++++++++++++--- .../utilities/diagnostics/testWebSocket.js | 100 ++++++++++++++++ 2 files changed, 192 insertions(+), 15 deletions(-) create mode 100644 examples/utilities/diagnostics/testWebSocket.js diff --git a/examples/libraries/unitTest.js b/examples/libraries/unitTest.js index beb3387898..d65cd780bd 100644 --- a/examples/libraries/unitTest.js +++ b/examples/libraries/unitTest.js @@ -11,17 +11,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var test = function(name, func) { +test = function(name, func, timeout) { print("Running test: " + name); - - var unitTest = new UnitTest(name, func); - - try { - unitTest.run(); + var unitTest = new UnitTest(name, func, timeout); + unitTest.run(function(unitTest) { print(" Success: " + unitTest.numAssertions + " assertions passed"); - } catch (error) { + }, function(unitTest, error) { print(" Failure: " + error.name + " " + error.message); - } + }); }; AssertionException = function(expected, actual, message) { @@ -36,13 +33,86 @@ UnthrownException = function(message) { this.name = 'UnthrownException'; }; -UnitTest = function(name, func) { - this.numAssertions = 0; - this.func = func; +TimeoutException = function() { + print("Creating exception"); + this.message = "UnitTest timed out\n"; + this.name = 'TimeoutException'; }; -UnitTest.prototype.run = function() { - this.func(); +SequentialUnitTester = function() { + this.tests = []; + this.testIndex = -1; +}; + +SequentialUnitTester.prototype.addTest = function(name, func, timeout) { + var _this = this; + this.tests.push(function() { + print("Running test: " + name); + var unitTest = new UnitTest(name, func, timeout); + unitTest.run(function(unitTest) { + print(" Success: " + unitTest.numAssertions + " assertions passed"); + _this._nextTest(); + }, function(unitTest, error) { + print(" Failure: " + error.name + " " + error.message); + _this._nextTest(); + }); + }); +}; + +SequentialUnitTester.prototype._nextTest = function() { + this.testIndex++; + if (this.testIndex < this.tests.length) { + this.tests[this.testIndex](); + return; + } + print("Completed all UnitTests"); +}; + +SequentialUnitTester.prototype.run = function() { + this._nextTest(); +}; + +UnitTest = function(name, func, timeout) { + this.numAssertions = 0; + this.func = func; + this.timeout = timeout; +}; + +UnitTest.prototype.run = function(successCallback, failureCallback) { + var _this = this; + this.successCallback = successCallback; + this.failureCallback = failureCallback; + if (this.timeout !== undefined) { + this.timeoutTimer = Script.setTimeout(function() { + _this.failureCallback(this, new TimeoutException()); + }, this.timeout); + } + try { + this.func(); + if (this.timeout === undefined) { + successCallback(this); + } + } catch (exception) { + this.handleException(exception); + } +}; + +UnitTest.prototype.registerCallbackFunction = function(func) { + var _this = this; + return function(one, two, three, four, five, six) { + try { + func(one, two, three, four, five, six); + } catch (exception) { + _this.handleException(exception); + } + }; +}; + +UnitTest.prototype.handleException = function(exception) { + if (this.timeout !== undefined) { + Script.clearTimeout(this.timeoutTimer); + } + this.failureCallback(this, exception); }; UnitTest.prototype.assertNotEquals = function(expected, actual, message) { @@ -83,7 +153,7 @@ UnitTest.prototype.assertNull = function(value, message) { UnitTest.prototype.arrayEqual = function(array1, array2, message) { this.numAssertions++; if (array1.length !== array2.length) { - throw new AssertionException(array1.length , array2.length , message); + throw new AssertionException(array1.length, array2.length , message); } for (var i = 0; i < array1.length; ++i) { if (array1[i] !== array2[i]) { @@ -101,4 +171,11 @@ UnitTest.prototype.raises = function(func, message) { } throw new UnthrownException(message); -} \ No newline at end of file +} + +UnitTest.prototype.ready = function() { + if (this.timeout !== undefined) { + Script.clearTimeout(this.timeoutTimer); + this.successCallback(this); + } +} diff --git a/examples/utilities/diagnostics/testWebSocket.js b/examples/utilities/diagnostics/testWebSocket.js new file mode 100644 index 0000000000..03b9b6b405 --- /dev/null +++ b/examples/utilities/diagnostics/testWebSocket.js @@ -0,0 +1,100 @@ +// +// testWebSocket.js +// examples +// +// Created by Thijs Wenker on 8/18/15 +// Copyright 2015 High Fidelity, Inc. +// +// WebSocket and WebSocketServer Tests +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +Script.include("../../libraries/unitTest.js"); + +// We set the unit testing timeout to 1000 milliseconds by default. Please increase if the test fails due to a slow connection. +const UNITTEST_TIMEOUT = 1000; +const WEBSOCKET_PING_URL = "ws://echo.websocket.org"; +// Please do not register the following domain + gTLD: +const WEBSOCKET_INVALID_URL = "ws://thisisnotavaliddomainname.invalid"; +const TEST_MESSAGE = "This is a test message."; + +var unitTests = new SequentialUnitTester(); + +unitTests.addTest("Test default WebSocket values", function(finished) { + var _this = this; + var webSocket = new WebSocket(WEBSOCKET_PING_URL); + + webSocket.onmessage = this.registerCallbackFunction(function(event) { + _this.assertEquals(TEST_MESSAGE, event.data, "event.data should be '" + TEST_MESSAGE + "'"); + webSocket.close(); + }); + webSocket.onopen = this.registerCallbackFunction(function(event) { + _this.assertEquals(webSocket.OPEN, webSocket.readyState, "readyState should be OPEN"); + webSocket.send(TEST_MESSAGE); + }); + webSocket.onclose = this.registerCallbackFunction(function(event) { + _this.assertEquals(webSocket.CLOSED, webSocket.readyState, "readyState should be CLOSED"); + _this.ready(); + }); + this.assertEquals(webSocket.CONNECTING, webSocket.readyState, "readyState should be CONNECTING"); + this.assertEquals("blob", webSocket.binaryType, "binaryType should be 'blob'"); + this.assertEquals(0, webSocket.bufferedAmount, "bufferedAmount should be 0"); + this.assertEquals("", webSocket.extensions, "extensions should be an empty string by default"); + this.assertEquals("", webSocket.protocol, "protocol should be an empty string by default"); + this.assertEquals(WEBSOCKET_PING_URL, webSocket.url, "url should be '" + WEBSOCKET_PING_URL + "'"); +}, UNITTEST_TIMEOUT); + +unitTests.addTest("Test WebSocket invalid URL", function(finished) { + var _this = this; + var webSocket = new WebSocket(WEBSOCKET_INVALID_URL); + var hadError = false; + webSocket.onerror = this.registerCallbackFunction(function() { + hadError = true; + }); + webSocket.onclose = this.registerCallbackFunction(function(event) { + _this.assertEquals(true, hadError, "hadError should be true"); + _this.assertEquals(webSocket.CLOSED, webSocket.readyState, "readyState should be CLOSED"); + _this.ready(); + }); + this.assertEquals(webSocket.CONNECTING, webSocket.readyState, "readyState should be CONNECTING"); + this.assertEquals(WEBSOCKET_INVALID_URL, webSocket.url, "url should be '" + WEBSOCKET_INVALID_URL + "'"); +}, UNITTEST_TIMEOUT); + + +unitTests.addTest("Test WebSocketServer with three clients", function(finished) { + var _this = this; + const NUMBER_OF_CLIENTS = 3; + var connectedClients = 0; + var respondedClients = 0; + var webSocketServer = new WebSocketServer(); + _this.assertEquals(true, webSocketServer.listening, "listening should be true"); + webSocketServer.newConnection.connect(this.registerCallbackFunction(function(newClient) { + connectedClients++; + newClient.onmessage = _this.registerCallbackFunction(function(event) { + var data = JSON.parse(event.data); + _this.assertEquals(TEST_MESSAGE, data.message, "data.message should be '" + TEST_MESSAGE + "'"); + respondedClients++; + if (respondedClients === NUMBER_OF_CLIENTS) { + webSocketServer.close(); + _this.assertEquals(false, webSocketServer.listening, "listening should be false"); + _this.ready(); + } + }); + newClient.send(JSON.stringify({message: TEST_MESSAGE, client: connectedClients})); + })); + var newSocket1 = new WebSocket(webSocketServer.url); + newSocket1.onmessage = this.registerCallbackFunction(function(event) { + newSocket1.send(event.data); + }); + var newSocket2 = new WebSocket(webSocketServer.url); + newSocket2.onmessage = this.registerCallbackFunction(function(event) { + newSocket2.send(event.data); + }); + var newSocket3 = new WebSocket(webSocketServer.url); + newSocket3.onmessage = this.registerCallbackFunction(function(event) { + newSocket3.send(event.data); + }); +}, UNITTEST_TIMEOUT); + +unitTests.run(); From 1b6538cc59fa154fa9302247cb5770b003a71072 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 19 Aug 2015 00:31:40 +0200 Subject: [PATCH 022/103] fixes extra qualification errors Linux --- libraries/script-engine/src/WebSocketClass.cpp | 2 +- libraries/script-engine/src/WebSocketServerClass.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 654b4746a8..7283f2c47f 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -37,7 +37,7 @@ void WebSocketClass::initialize() { _binaryType = "blob"; } -QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { +QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) { QString url; QScriptValue callee = context->callee(); if (context->argumentCount() == 1) { diff --git a/libraries/script-engine/src/WebSocketServerClass.h b/libraries/script-engine/src/WebSocketServerClass.h index cad993a508..972bf9c032 100644 --- a/libraries/script-engine/src/WebSocketServerClass.h +++ b/libraries/script-engine/src/WebSocketServerClass.h @@ -31,7 +31,7 @@ public: quint16 getPort() { return _webSocketServer.serverPort(); } bool isListening() { return _webSocketServer.isListening(); } - static QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptEngine* engine); + static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); public slots: void close(); From 7f8506acc0c4851a090dc74986d994c79f3415d1 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 19 Aug 2015 00:54:00 +0200 Subject: [PATCH 023/103] fix. switchup. --- libraries/script-engine/src/WebSocketClass.cpp | 2 +- libraries/script-engine/src/WebSocketClass.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 7283f2c47f..654b4746a8 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -37,7 +37,7 @@ void WebSocketClass::initialize() { _binaryType = "blob"; } -QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) { +QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { QString url; QScriptValue callee = context->callee(); if (context->argumentCount() == 1) { diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index fc70a72883..e053257e3d 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -41,7 +41,7 @@ public: WebSocketClass(QScriptEngine* engine, QWebSocket* qWebSocket); ~WebSocketClass(); - static QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine); + static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); enum ReadyState { CONNECTING = 0, From 07ae00f20715b8a5c0ab814c31c50c9b49d5edca Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 19 Aug 2015 19:33:48 +0200 Subject: [PATCH 024/103] Close WebSocket properly after ScriptEngine finished running --- libraries/script-engine/src/WebSocketServerClass.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/WebSocketServerClass.cpp b/libraries/script-engine/src/WebSocketServerClass.cpp index 7981d67de3..9fc060e61c 100644 --- a/libraries/script-engine/src/WebSocketServerClass.cpp +++ b/libraries/script-engine/src/WebSocketServerClass.cpp @@ -40,7 +40,9 @@ QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptE serverName = serverNameOption.toString(); } } - return engine->newQObject(new WebSocketServerClass(engine, serverName, port)); + auto webSocketServerClass = new WebSocketServerClass(engine, serverName, port); + connect(engine, SIGNAL(finished(const QString&)), webSocketServerClass, SLOT(deleteLater())); + return engine->newQObject(webSocketServerClass); } WebSocketServerClass::~WebSocketServerClass() { From ecd368fac35172a5de2c52259366c4934e4bdde5 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 19 Aug 2015 20:00:06 +0200 Subject: [PATCH 025/103] new style qt connectors --- libraries/script-engine/src/WebSocketServerClass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/WebSocketServerClass.cpp b/libraries/script-engine/src/WebSocketServerClass.cpp index 9fc060e61c..dbe92510e4 100644 --- a/libraries/script-engine/src/WebSocketServerClass.cpp +++ b/libraries/script-engine/src/WebSocketServerClass.cpp @@ -41,7 +41,7 @@ QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptE } } auto webSocketServerClass = new WebSocketServerClass(engine, serverName, port); - connect(engine, SIGNAL(finished(const QString&)), webSocketServerClass, SLOT(deleteLater())); + connect(static_cast(engine), &ScriptEngine::finished, webSocketServerClass, &QObject::deleteLater); return engine->newQObject(webSocketServerClass); } From 92ae3e758c9a849c60e13132a18566ea062732e4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 19 Aug 2015 12:28:07 -0700 Subject: [PATCH 026/103] restore DEFAULT_FAR_CLIP to 16km --- libraries/octree/src/ViewFrustum.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index 3e41b58bb6..fe40cfd53c 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -31,7 +31,7 @@ const float DEFAULT_KEYHOLE_RADIUS = 3.0f; const float DEFAULT_FIELD_OF_VIEW_DEGREES = 45.0f; const float DEFAULT_ASPECT_RATIO = 16.0f/9.0f; const float DEFAULT_NEAR_CLIP = 0.08f; -const float DEFAULT_FAR_CLIP = (float)TREE_SCALE; +const float DEFAULT_FAR_CLIP = (float)HALF_TREE_SCALE; class ViewFrustum { public: From 9605294ea8b1b8fd2fd8062f427b2d9455de3955 Mon Sep 17 00:00:00 2001 From: Chris Collins Date: Wed, 19 Aug 2015 12:55:52 -0700 Subject: [PATCH 027/103] Removed HMDcontrol.js from default scripts Removed HMDcontrol.js from default scripts. Will put back when HMDcontrol is fixed --- examples/defaultScripts.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index 922047b90c..c06ecd393d 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -17,5 +17,4 @@ Script.load("users.js"); Script.load("grab.js"); Script.load("directory.js"); Script.load("mouseLook.js"); -Script.load("hmdControls.js"); Script.load("dialTone.js"); From 846ca3b86b59b84190a3a72ee747eb3c3ce67910 Mon Sep 17 00:00:00 2001 From: Chris Collins Date: Wed, 19 Aug 2015 12:58:26 -0700 Subject: [PATCH 028/103] removed mouselook removed mouselook --- examples/defaultScripts.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index c06ecd393d..c602c36382 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -16,5 +16,4 @@ Script.load("notifications.js"); Script.load("users.js"); Script.load("grab.js"); Script.load("directory.js"); -Script.load("mouseLook.js"); Script.load("dialTone.js"); From 6860f3d126e4c7688016ca3536072034111dd82d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Aug 2015 14:32:26 -0700 Subject: [PATCH 029/103] add readWithoutCopy for clarity in Packet read --- assignment-client/src/AssignmentClientMonitor.cpp | 2 +- assignment-client/src/audio/AudioMixerClientData.cpp | 2 +- .../src/avatars/AvatarMixerClientData.cpp | 2 +- libraries/audio/src/InboundAudioStream.cpp | 4 ++-- libraries/avatars/src/AvatarHashMap.cpp | 10 +++++----- libraries/entities/src/EntityTree.cpp | 2 +- libraries/networking/src/LimitedNodeList.cpp | 2 +- libraries/networking/src/NodeList.cpp | 2 +- libraries/networking/src/udt/Packet.cpp | 7 +++++++ libraries/networking/src/udt/Packet.h | 3 ++- 10 files changed, 22 insertions(+), 14 deletions(-) diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 72765dd0df..ddea6cc702 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -203,7 +203,7 @@ void AssignmentClientMonitor::checkSpares() { void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer packet) { // read out the sender ID - QUuid senderID = QUuid::fromRfc4122(packet->read(NUM_BYTES_RFC4122_UUID)); + QUuid senderID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); auto nodeList = DependencyManager::get(); diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 10a5992fd8..7e822951d8 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -90,7 +90,7 @@ int AudioMixerClientData::parseData(NLPacket& packet) { // grab the stream identifier for this injected audio packet.seek(sizeof(quint16)); - QUuid streamIdentifier = QUuid::fromRfc4122(packet.read(NUM_BYTES_RFC4122_UUID)); + QUuid streamIdentifier = QUuid::fromRfc4122(packet.readWithoutCopy(NUM_BYTES_RFC4122_UUID)); bool isStereo; packet.readPrimitive(&isStereo); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index fec51f76cb..65660b178e 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -15,7 +15,7 @@ int AvatarMixerClientData::parseData(NLPacket& packet) { // compute the offset to the data payload - return _avatar.parseDataFromBuffer(packet.read(packet.bytesLeftToRead())); + return _avatar.parseDataFromBuffer(packet.readWithoutCopy(packet.bytesLeftToRead())); } bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() { diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index 8888c9bae5..d552db1735 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -112,7 +112,7 @@ int InboundAudioStream::parseData(NLPacket& packet) { // parse the info after the seq number and before the audio data (the stream properties) int prePropertyPosition = packet.pos(); - int propertyBytes = parseStreamProperties(packet.getType(), packet.read(packet.bytesLeftToRead()), networkSamples); + int propertyBytes = parseStreamProperties(packet.getType(), packet.readWithoutCopy(packet.bytesLeftToRead()), networkSamples); packet.seek(prePropertyPosition + propertyBytes); // handle this packet based on its arrival status. @@ -131,7 +131,7 @@ int InboundAudioStream::parseData(NLPacket& packet) { if (packet.getType() == PacketType::SilentAudioFrame) { writeDroppableSilentSamples(networkSamples); } else { - parseAudioData(packet.getType(), packet.read(packet.bytesLeftToRead()), networkSamples); + parseAudioData(packet.getType(), packet.readWithoutCopy(packet.bytesLeftToRead()), networkSamples); } break; } diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 5c65f89990..d2ec5d68f0 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -53,11 +53,11 @@ void AvatarHashMap::processAvatarDataPacket(QSharedPointer packet, Sha // enumerate over all of the avatars in this packet // only add them if mixerWeakPointer points to something (meaning that mixer is still around) while (packet->bytesLeftToRead()) { - QUuid sessionUUID = QUuid::fromRfc4122(packet->read(NUM_BYTES_RFC4122_UUID)); + QUuid sessionUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); int positionBeforeRead = packet->pos(); - QByteArray byteArray = packet->read(packet->bytesLeftToRead()); + QByteArray byteArray = packet->readWithoutCopy(packet->bytesLeftToRead()); if (sessionUUID != _lastOwnerSessionUUID) { AvatarSharedPointer avatar = _avatarHash.value(sessionUUID); @@ -114,14 +114,14 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer packet, } void AvatarHashMap::processAvatarBillboardPacket(QSharedPointer packet, SharedNodePointer sendingNode) { - QUuid sessionUUID = QUuid::fromRfc4122(packet->read(NUM_BYTES_RFC4122_UUID)); + QUuid sessionUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); AvatarSharedPointer avatar = _avatarHash.value(sessionUUID); if (!avatar) { avatar = addAvatar(sessionUUID, sendingNode); } - QByteArray billboard = packet->read(packet->bytesLeftToRead()); + QByteArray billboard = packet->readWithoutCopy(packet->bytesLeftToRead()); if (avatar->getBillboard() != billboard) { avatar->setBillboard(billboard); } @@ -129,7 +129,7 @@ void AvatarHashMap::processAvatarBillboardPacket(QSharedPointer packet void AvatarHashMap::processKillAvatar(QSharedPointer packet, SharedNodePointer sendingNode) { // read the node id - QUuid sessionUUID = QUuid::fromRfc4122(packet->read(NUM_BYTES_RFC4122_UUID)); + QUuid sessionUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); removeAvatar(sessionUUID); } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index ba2691aa3b..ba0e3f495f 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -873,7 +873,7 @@ int EntityTree::processEraseMessage(NLPacket& packet, const SharedNodePointer& s break; // bail to prevent buffer overflow } - QUuid entityID = QUuid::fromRfc4122(packet.read(NUM_BYTES_RFC4122_UUID)); + QUuid entityID = QUuid::fromRfc4122(packet.readWithoutCopy(NUM_BYTES_RFC4122_UUID)); EntityItemID entityItemID(entityID); entityItemIDsToDelete << entityItemID; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index fdb3461c1f..48febf39bd 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -397,7 +397,7 @@ void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) { void LimitedNodeList::processKillNode(NLPacket& packet) { // read the node id - QUuid nodeUUID = QUuid::fromRfc4122(packet.read(NUM_BYTES_RFC4122_UUID)); + QUuid nodeUUID = QUuid::fromRfc4122(packet.readWithoutCopy(NUM_BYTES_RFC4122_UUID)); // kill the node with this UUID, if it exists killNodeWithUUID(nodeUUID); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 563b7d7080..6616707579 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -463,7 +463,7 @@ void NodeList::processDomainServerConnectionTokenPacket(QSharedPointer return; } // read in the connection token from the packet, then send domain-server checkin - _domainHandler.setConnectionToken(QUuid::fromRfc4122(packet->read(NUM_BYTES_RFC4122_UUID))); + _domainHandler.setConnectionToken(QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID))); sendDomainServerCheckIn(); } diff --git a/libraries/networking/src/udt/Packet.cpp b/libraries/networking/src/udt/Packet.cpp index 13f8a39e26..81747749be 100644 --- a/libraries/networking/src/udt/Packet.cpp +++ b/libraries/networking/src/udt/Packet.cpp @@ -213,6 +213,13 @@ void Packet::writeSequenceNumber(SequenceNumber seqNum) { } QByteArray Packet::read(qint64 maxSize) { + qint64 sizeToRead = std::min(size() - pos(), maxSize); + QByteArray data { getPayload() + pos(), (int) sizeToRead }; + seek(pos() + sizeToRead); + return data; +} + +QByteArray Packet::readWithoutCopy(qint64 maxSize) { qint64 sizeToRead = std::min(size() - pos(), maxSize); QByteArray data { QByteArray::fromRawData(getPayload() + pos(), sizeToRead) }; seek(pos() + sizeToRead); diff --git a/libraries/networking/src/udt/Packet.h b/libraries/networking/src/udt/Packet.h index 91b5974e09..569ff4b9a4 100644 --- a/libraries/networking/src/udt/Packet.h +++ b/libraries/networking/src/udt/Packet.h @@ -81,7 +81,8 @@ public: using QIODevice::read; QByteArray read(qint64 maxSize); - + QByteArray readWithoutCopy(qint64 maxSize); // this can only be used if packet will stay in scope + template qint64 peekPrimitive(T* data); template qint64 readPrimitive(T* data); template qint64 writePrimitive(const T& data); From c8fbb64bbf11ff53ba3172e60aa47d3edd23c2a1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Aug 2015 14:36:16 -0700 Subject: [PATCH 030/103] fix AvatarHashMap read due to COW --- libraries/avatars/src/AvatarHashMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index d2ec5d68f0..4378e818ec 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -121,7 +121,7 @@ void AvatarHashMap::processAvatarBillboardPacket(QSharedPointer packet avatar = addAvatar(sessionUUID, sendingNode); } - QByteArray billboard = packet->readWithoutCopy(packet->bytesLeftToRead()); + QByteArray billboard = packet->read(packet->bytesLeftToRead()); if (avatar->getBillboard() != billboard) { avatar->setBillboard(billboard); } From 010a3d8220a11783f155028a41ce9fc8b46321ca Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 19 Aug 2015 17:12:03 -0700 Subject: [PATCH 031/103] fix create in negative space bug --- examples/edit.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index f0d782cd4a..deda035d5e 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1036,6 +1036,7 @@ function handeMenuEvent(menuItem) { // This function tries to find a reasonable position to place a new entity based on the camera // position. If a reasonable position within the world bounds can't be found, `null` will // be returned. The returned position will also take into account grid snapping settings. +// FIXME - technically we should guard against very large positions too function getPositionToCreateEntity() { var distance = cameraManager.enabled ? cameraManager.zoomDistance : DEFAULT_ENTITY_DRAG_DROP_DISTANCE; var direction = Quat.getFront(Camera.orientation); @@ -1055,9 +1056,9 @@ function getPositionToCreateEntity() { return null; } - placementPosition.x = Math.max(0, placementPosition.x); - placementPosition.y = Math.max(0, placementPosition.y); - placementPosition.z = Math.max(0, placementPosition.z); + placementPosition.x = Math.max(-HALF_TREE_SCALE, placementPosition.x); + placementPosition.y = Math.max(-HALF_TREE_SCALE, placementPosition.y); + placementPosition.z = Math.max(-HALF_TREE_SCALE, placementPosition.z); return placementPosition; } From 2056f588e3575e0dae35b66c288f0147fe7bbe39 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 19 Aug 2015 18:03:16 -0700 Subject: [PATCH 032/103] if environment variable HIFI_MEMORY_DEBUGGING is defined when cmake is run, enable -fsanitize=address on linux --- assignment-client/CMakeLists.txt | 10 +++++++++- domain-server/CMakeLists.txt | 8 ++++++++ interface/CMakeLists.txt | 8 ++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index edd68e12bf..7e3b2e6af9 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -18,4 +18,12 @@ if (UNIX) endif (UNIX) include_application_version() -copy_dlls_beside_windows_executable() \ No newline at end of file +copy_dlls_beside_windows_executable() + +if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) + if (UNIX) + MESSAGE("-- assignment-client memory debugging is enabled.") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") + endif (UNIX) +endif () diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index e4fa1d874d..0d56e34cf2 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -33,3 +33,11 @@ target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES}) include_application_version() copy_dlls_beside_windows_executable() + +if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) + if (UNIX) + MESSAGE("-- domain-server memory debugging is enabled.") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") + endif (UNIX) +endif () diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index acafafa006..16d8bacd23 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -206,3 +206,11 @@ else (APPLE) endif (APPLE) copy_dlls_beside_windows_executable() + +if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) + if (UNIX) + MESSAGE("-- interface memory debugging is enabled.") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") + endif (UNIX) +endif () From 2738f65c10b624a7e49d3edc7d935b8df1ba8d96 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Aug 2015 18:23:11 -0700 Subject: [PATCH 033/103] have domain-server report non-skewed uptime --- domain-server/resources/web/index.shtml | 2 +- domain-server/resources/web/js/tables.js | 4 ++-- domain-server/src/DomainServer.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/domain-server/resources/web/index.shtml b/domain-server/resources/web/index.shtml index 0f720ebe79..ea941a73fd 100644 --- a/domain-server/resources/web/index.shtml +++ b/domain-server/resources/web/index.shtml @@ -31,7 +31,7 @@ <%- node.username %> <%- node.public.ip %>:<%- node.public.port %> <%- node.local.ip %>:<%- node.local.port %> - <%- ((Date.now() - node.wake_timestamp) / 1000).toLocaleString() %> + <%- node.uptime %> <%- (typeof node.pending_credits == 'number' ? node.pending_credits.toLocaleString() : 'N/A') %> diff --git a/domain-server/resources/web/js/tables.js b/domain-server/resources/web/js/tables.js index 0b29d4e6c9..b2e0679d8b 100644 --- a/domain-server/resources/web/js/tables.js +++ b/domain-server/resources/web/js/tables.js @@ -9,9 +9,9 @@ $(document).ready(function(){ json.nodes.sort(function(a, b){ if (a.type === b.type) { - if (a.wake_timestamp < b.wake_timestamp) { + if (a.uptime > b.uptime) { return 1; - } else if (a.wake_timestamp > b.wake_timestamp) { + } else if (a.uptime < b.uptime) { return -1; } else { return 0; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 071626ef1e..369cb3b761 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1480,7 +1480,7 @@ const char JSON_KEY_PUBLIC_SOCKET[] = "public"; const char JSON_KEY_LOCAL_SOCKET[] = "local"; const char JSON_KEY_POOL[] = "pool"; const char JSON_KEY_PENDING_CREDITS[] = "pending_credits"; -const char JSON_KEY_WAKE_TIMESTAMP[] = "wake_timestamp"; +const char JSON_KEY_UPTIME[] = "uptime"; const char JSON_KEY_USERNAME[] = "username"; const char JSON_KEY_VERSION[] = "version"; QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { @@ -1502,7 +1502,7 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { nodeJson[JSON_KEY_LOCAL_SOCKET] = jsonForSocket(node->getLocalSocket()); // add the node uptime in our list - nodeJson[JSON_KEY_WAKE_TIMESTAMP] = QString::number(node->getWakeTimestamp()); + nodeJson[JSON_KEY_UPTIME] = QString::number((QDateTime::currentMSecsSinceEpoch() - node->getWakeTimestamp()) / 1000.0); // if the node has pool information, add it DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); From b386a95d0852a4284d414e26ac6bbd56d95fed44 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Aug 2015 18:31:42 -0700 Subject: [PATCH 034/103] fix sort for node uptime --- domain-server/resources/web/js/tables.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/web/js/tables.js b/domain-server/resources/web/js/tables.js index b2e0679d8b..09f85a7047 100644 --- a/domain-server/resources/web/js/tables.js +++ b/domain-server/resources/web/js/tables.js @@ -9,9 +9,9 @@ $(document).ready(function(){ json.nodes.sort(function(a, b){ if (a.type === b.type) { - if (a.uptime > b.uptime) { + if (a.uptime < b.uptime) { return 1; - } else if (a.uptime < b.uptime) { + } else if (a.uptime > b.uptime) { return -1; } else { return 0; From 6a7f367e5fecc2e37000818e1965be57789323f1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 19 Aug 2015 21:11:48 -0700 Subject: [PATCH 035/103] fix throttled rendering for the 2D display plugin --- interface/src/Application.cpp | 27 ++++++++- interface/src/Application.h | 3 + interface/src/GLCanvas.cpp | 59 ++----------------- interface/src/GLCanvas.h | 10 ---- interface/src/Menu.h | 2 +- .../Basic2DWindowOpenGLDisplayPlugin.cpp | 21 +++++++ .../Basic2DWindowOpenGLDisplayPlugin.h | 6 ++ .../src/display-plugins/OpenGLDisplayPlugin.h | 4 +- .../plugins/src/plugins/PluginContainer.h | 1 + 9 files changed, 64 insertions(+), 69 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4dd61b7982..15f4a48405 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -175,8 +175,6 @@ public: using namespace std; // Starfield information -static uint8_t THROTTLED_IDLE_TIMER_DELAY = 10; - const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES; // 10GB static QTimer* locationUpdateTimer = NULL; @@ -737,6 +735,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _keyboardFocusHighlight->setVisible(false); } }); + + connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); } void Application::aboutToQuit() { @@ -2108,8 +2108,11 @@ void Application::idle() { // Once rendering is off on another thread we should be able to have Application::idle run at start(0) in // perpetuity and not expect events to get backed up. + bool isThrottled = getActiveDisplayPlugin()->isThrottled(); + static const int THROTTLED_IDLE_TIMER_DELAY = MSECS_PER_SECOND / 15; static const int IDLE_TIMER_DELAY_MS = 2; - int desiredInterval = getActiveDisplayPlugin()->isThrottled() ? THROTTLED_IDLE_TIMER_DELAY : IDLE_TIMER_DELAY_MS; + int desiredInterval = isThrottled ? THROTTLED_IDLE_TIMER_DELAY : IDLE_TIMER_DELAY_MS; + //qDebug() << "isThrottled:" << isThrottled << "desiredInterval:" << desiredInterval; if (idleTimer->interval() != desiredInterval) { idleTimer->start(desiredInterval); @@ -4612,6 +4615,24 @@ void Application::checkSkeleton() { } } +bool Application::isForeground() { + return _isForeground && !getWindow()->isMinimized(); +} + +void Application::activeChanged(Qt::ApplicationState state) { + switch (state) { + case Qt::ApplicationActive: + _isForeground = true; + break; + + case Qt::ApplicationSuspended: + case Qt::ApplicationHidden: + case Qt::ApplicationInactive: + default: + _isForeground = false; + break; + } +} void Application::showFriendsWindow() { const QString FRIENDS_WINDOW_TITLE = "Add/Remove Friends"; const QString FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends"; diff --git a/interface/src/Application.h b/interface/src/Application.h index 4819bd08a4..a1765109ce 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -291,6 +291,7 @@ public: virtual void unsetFullscreen(const QScreen* avoid) override; virtual void showDisplayPluginsTools() override; virtual QGLWidget* getPrimarySurface() override; + virtual bool isForeground() override; void setActiveDisplayPlugin(const QString& pluginName); @@ -476,6 +477,7 @@ private slots: void faceTrackerMuteToggled(); void setCursorVisible(bool visible); + void activeChanged(Qt::ApplicationState state); private: void resetCameras(Camera& camera, const glm::uvec2& size); @@ -688,6 +690,7 @@ private: SimpleMovingAverage _simsPerSecond{10}; int _simsPerSecondReport = 0; quint64 _lastSimsPerSecondUpdate = 0; + bool _isForeground = true; // starts out assumed to be in foreground }; #endif // hifi_Application_h diff --git a/interface/src/GLCanvas.cpp b/interface/src/GLCanvas.cpp index 0d0d335b67..bfa2dacf3a 100644 --- a/interface/src/GLCanvas.cpp +++ b/interface/src/GLCanvas.cpp @@ -18,8 +18,6 @@ #include "MainWindow.h" -const int MSECS_PER_FRAME_WHEN_THROTTLED = 66; - static QGLFormat& getDesiredGLFormat() { // Specify an OpenGL 3.3 format using the Core profile. // That is, no old-school fixed pipeline functionality @@ -35,10 +33,7 @@ static QGLFormat& getDesiredGLFormat() { return glFormat; } -GLCanvas::GLCanvas() : QGLWidget(getDesiredGLFormat()), - _throttleRendering(false), - _idleRenderInterval(MSECS_PER_FRAME_WHEN_THROTTLED) -{ +GLCanvas::GLCanvas() : QGLWidget(getDesiredGLFormat()) { #ifdef Q_OS_LINUX // Cause GLCanvas::eventFilter to be called. // It wouldn't hurt to do this on Mac and PC too; but apparently it's only needed on linux. @@ -46,15 +41,6 @@ GLCanvas::GLCanvas() : QGLWidget(getDesiredGLFormat()), #endif } -void GLCanvas::stopFrameTimer() { - _frameTimer.stop(); -} - -bool GLCanvas::isThrottleRendering() const { - return (_throttleRendering - || (Application::getInstance()->getWindow()->isMinimized() && Application::getInstance()->isThrottleFPSEnabled())); -} - int GLCanvas::getDeviceWidth() const { return width() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f); } @@ -66,17 +52,17 @@ int GLCanvas::getDeviceHeight() const { void GLCanvas::initializeGL() { setAttribute(Qt::WA_AcceptTouchEvents); setAcceptDrops(true); - connect(Application::getInstance(), SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(activeChanged(Qt::ApplicationState))); - connect(&_frameTimer, SIGNAL(timeout()), this, SLOT(throttleRender())); - // Note, we *DO NOT* want Qt to automatically swap buffers for us. This results in the "ringing" bug mentioned in WL#19514 when we're throttling the framerate. setAutoBufferSwap(false); } void GLCanvas::paintGL() { PROFILE_RANGE(__FUNCTION__); - if (!_throttleRendering && - (!Application::getInstance()->getWindow()->isMinimized() || !Application::getInstance()->isThrottleFPSEnabled())) { + + // FIXME - I'm not sure why this still remains, it appears as if this GLCanvas gets a single paintGL call near + // the beginning of the application starting up. I'm not sure if we really need to call Application::paintGL() + // in this case, since the display plugins eventually handle all the painting + if ((!Application::getInstance()->getWindow()->isMinimized() || !Application::getInstance()->isThrottleFPSEnabled())) { Application::getInstance()->paintGL(); } } @@ -85,39 +71,6 @@ void GLCanvas::resizeGL(int width, int height) { Application::getInstance()->resizeGL(); } -void GLCanvas::activeChanged(Qt::ApplicationState state) { - switch (state) { - case Qt::ApplicationActive: - // If we're active, stop the frame timer and the throttle. - _frameTimer.stop(); - _throttleRendering = false; - break; - - case Qt::ApplicationSuspended: - case Qt::ApplicationHidden: - // If we're hidden or are about to suspend, don't render anything. - _throttleRendering = false; - _frameTimer.stop(); - break; - - default: - // Otherwise, throttle. - if (!_throttleRendering && !Application::getInstance()->isAboutToQuit() - && Application::getInstance()->isThrottleFPSEnabled()) { - _frameTimer.start(_idleRenderInterval); - _throttleRendering = true; - } - break; - } -} - -void GLCanvas::throttleRender() { - _frameTimer.start(_idleRenderInterval); - if (!Application::getInstance()->getWindow()->isMinimized()) { - Application::getInstance()->paintGL(); - } -} - int updateTime = 0; bool GLCanvas::event(QEvent* event) { switch (event->type()) { diff --git a/interface/src/GLCanvas.h b/interface/src/GLCanvas.h index bcaa9577d1..73c5b5e8bf 100644 --- a/interface/src/GLCanvas.h +++ b/interface/src/GLCanvas.h @@ -23,28 +23,18 @@ class GLCanvas : public QGLWidget { public: GLCanvas(); - void stopFrameTimer(); - - bool isThrottleRendering() const; - int getDeviceWidth() const; int getDeviceHeight() const; QSize getDeviceSize() const { return QSize(getDeviceWidth(), getDeviceHeight()); } protected: - QTimer _frameTimer; - bool _throttleRendering; - int _idleRenderInterval; - virtual void initializeGL(); virtual void paintGL(); virtual void resizeGL(int width, int height); virtual bool event(QEvent* event); private slots: - void activeChanged(Qt::ApplicationState state); - void throttleRender(); bool eventFilter(QObject*, QEvent* event); }; diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b0a78fdd83..ca46b80f92 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -283,7 +283,7 @@ namespace MenuOption { const QString TestPing = "Test Ping"; const QString ThirdPerson = "Third Person"; const QString ThreePointCalibration = "3 Point Calibration"; - const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; + const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp const QString ToolWindow = "Tool Window"; const QString TransmitterDrive = "Transmitter Drive"; const QString TurnWithHead = "Turn using Head"; diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index ce6467cd55..e478223281 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -34,3 +34,24 @@ void Basic2DWindowOpenGLDisplayPlugin::deactivate() { // container->removeMenu(MENU_PATH); MainWindowOpenGLDisplayPlugin::deactivate(); } + +int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval(bool isThrottled) const { + static const int THROTTLED_PAINT_TIMER_DELAY = MSECS_PER_SECOND / 15; + static const int PAINT_TIMER_DELAY_MS = 1; + + return isThrottled ? THROTTLED_PAINT_TIMER_DELAY : PAINT_TIMER_DELAY_MS; +} + +bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const { + static const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Menu.h + + bool shouldThrottle = (!CONTAINER->isForeground() && CONTAINER->isOptionChecked(ThrottleFPSIfNotFocus)); + + if (_isThrottled != shouldThrottle) { + int desiredInterval = getDesiredInterval(shouldThrottle); + _timer.start(desiredInterval); + _isThrottled = shouldThrottle; + } + + return shouldThrottle; +} \ No newline at end of file diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index d19326f007..612d15377a 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -18,6 +18,12 @@ public: virtual const QString & getName() const override; + virtual bool isThrottled() const override; + +protected: + int getDesiredInterval(bool isThrottled) const; + mutable bool _isThrottled = false; + private: static const QString NAME; }; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 9986610a50..9b6b92d8d4 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -37,8 +37,8 @@ protected: virtual void doneCurrent() = 0; virtual void swapBuffers() = 0; - QTimer _timer; - ProgramPtr _program; + mutable QTimer _timer; + ProgramPtr _program; ShapeWrapperPtr _plane; }; diff --git a/libraries/plugins/src/plugins/PluginContainer.h b/libraries/plugins/src/plugins/PluginContainer.h index 85c5c814de..7f6346a181 100644 --- a/libraries/plugins/src/plugins/PluginContainer.h +++ b/libraries/plugins/src/plugins/PluginContainer.h @@ -26,4 +26,5 @@ public: virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) = 0; virtual void showDisplayPluginsTools() = 0; virtual QGLWidget* getPrimarySurface() = 0; + virtual bool isForeground() = 0; }; From 002aea333352b58bf6c107363066a26e486ddb40 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 20 Aug 2015 00:30:44 -0700 Subject: [PATCH 036/103] Add fullscreen menu item to 2D display plugin --- .../Basic2DWindowOpenGLDisplayPlugin.cpp | 27 ++++++++++++------- .../Basic2DWindowOpenGLDisplayPlugin.h | 3 +++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index e478223281..7667fa1a29 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -9,12 +9,11 @@ #include #include +#include const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display"); -const QString MENU_PARENT = "View"; -const QString MENU_NAME = "Display Options"; -const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; +const QString MENU_PATH = "Display"; const QString FULLSCREEN = "Fullscreen"; const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const { @@ -22,16 +21,19 @@ const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const { } void Basic2DWindowOpenGLDisplayPlugin::activate() { -// container->addMenu(MENU_PATH); -// container->addMenuItem(MENU_PATH, FULLSCREEN, -// [this] (bool clicked) { this->setFullscreen(clicked); }, -// true, false); + CONTAINER->addMenu(MENU_PATH); + CONTAINER->addMenuItem(MENU_PATH, FULLSCREEN, + [this](bool clicked) { + if (clicked) { + CONTAINER->setFullscreen(getFullscreenTarget()); + } else { + CONTAINER->unsetFullscreen(); + } + }, true, false); MainWindowOpenGLDisplayPlugin::activate(); } void Basic2DWindowOpenGLDisplayPlugin::deactivate() { -// container->removeMenuItem(MENU_NAME, FULLSCREEN); -// container->removeMenu(MENU_PATH); MainWindowOpenGLDisplayPlugin::deactivate(); } @@ -54,4 +56,9 @@ bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const { } return shouldThrottle; -} \ No newline at end of file +} + +// FIXME target the screen the window is currently on +QScreen* Basic2DWindowOpenGLDisplayPlugin::getFullscreenTarget() { + return qApp->primaryScreen(); +} diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index 612d15377a..477e214f4e 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -9,6 +9,7 @@ #include "MainWindowOpenGLDisplayPlugin.h" +class QScreen; class Basic2DWindowOpenGLDisplayPlugin : public MainWindowOpenGLDisplayPlugin { Q_OBJECT @@ -26,4 +27,6 @@ protected: private: static const QString NAME; + QScreen* getFullscreenTarget(); + int _fullscreenTarget{ -1 }; }; From 9faf45219604941cf3ae9ee48f4107094d77b254 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 20 Aug 2015 00:16:50 -0700 Subject: [PATCH 037/103] Make sure that items added via plugins can be removed as well --- interface/src/Application.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 15f4a48405..5f8b17f178 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4746,6 +4746,7 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti } static QVector> _currentDisplayPluginActions; +static bool _activatingDisplayPlugin{false}; void Application::updateDisplayMode() { auto menu = Menu::getInstance(); @@ -4792,7 +4793,9 @@ void Application::updateDisplayMode() { if (newDisplayPlugin) { _offscreenContext->makeCurrent(); + _activatingDisplayPlugin = true; newDisplayPlugin->activate(); + _activatingDisplayPlugin = false; _offscreenContext->makeCurrent(); offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize())); _offscreenContext->makeCurrent(); @@ -4909,14 +4912,17 @@ void Application::removeMenu(const QString& menuName) { void Application::addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable, bool checked, const QString& groupName) { auto menu = Menu::getInstance(); MenuWrapper* parentItem = menu->getMenu(path); - QAction* action = parentItem->addAction(name); + QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name); connect(action, &QAction::triggered, [=] { onClicked(action->isChecked()); }); action->setCheckable(checkable); action->setChecked(checked); - _currentDisplayPluginActions.push_back({ path, name }); - _currentInputPluginActions.push_back({ path, name }); + if (_activatingDisplayPlugin) { + _currentDisplayPluginActions.push_back({ path, name }); + } else { + _currentInputPluginActions.push_back({ path, name }); + } } void Application::removeMenuItem(const QString& menuName, const QString& menuItem) { From 2b26f302fd0353c668230c632b950057ecdf741d Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 20 Aug 2015 08:01:33 -0600 Subject: [PATCH 038/103] Better handling of socket binding --- .../embedded-webserver/src/HTTPManager.cpp | 30 +++++++++++++++---- .../embedded-webserver/src/HTTPManager.h | 11 ++++++- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 19443e01da..0d91672904 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include #include @@ -19,16 +20,18 @@ #include "EmbeddedWebserverLogging.h" #include "HTTPManager.h" +const int SOCKET_ERROR_EXIT_CODE = 2; + HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : QTcpServer(parent), _documentRoot(documentRoot), - _requestHandler(requestHandler) + _requestHandler(requestHandler), + _port(port) { - // start listening on the passed port - if (!listen(QHostAddress("0.0.0.0"), port)) { - qCDebug(embeddedwebserver) << "Failed to open HTTP server socket:" << errorString(); - return; - } + bindSocket(); + _isListeningTimer = new QTimer(this); + connect(_isListeningTimer, &QTimer::timeout, this, &HTTPManager::isTcpServerListening); + _isListeningTimer->start(10000); } void HTTPManager::incomingConnection(qintptr socketDescriptor) { @@ -157,3 +160,18 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool HTTPManager::requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url) { return _requestHandler && _requestHandler->handleHTTPRequest(connection, url); } + +void HTTPManager::isTcpServerListening() { + if (!isListening()) { + qCWarning(embeddedwebserver) << "Socket on port " << QString::number(_port) << " is no longer listening"; + bindSocket(); + }} + +bool HTTPManager::bindSocket() { + qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); + if (!listen(QHostAddress::Any, _port)) { + qCWarning(embeddedwebserver) << "Failed to open HTTP server socket:" << errorString() << " can't continue"; + QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); + } + return true; +} \ No newline at end of file diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index 83c4103c15..6375b10205 100644 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -17,6 +17,7 @@ #define hifi_HTTPManager_h #include +#include class HTTPConnection; class HTTPSConnection; @@ -35,14 +36,22 @@ public: HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler = NULL, QObject* parent = 0); bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); + +private slots: + void isTcpServerListening(); + +private: + bool bindSocket(); protected: /// Accepts all pending connections virtual void incomingConnection(qintptr socketDescriptor); virtual bool requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url); -protected: + QString _documentRoot; HTTPRequestHandler* _requestHandler; + QTimer* _isListeningTimer; + const quint16 _port; }; #endif // hifi_HTTPManager_h From f87f3eb03319af5b6fedff39b09a2b4f00403866 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 20 Aug 2015 08:02:45 -0600 Subject: [PATCH 039/103] Typo --- libraries/embedded-webserver/src/HTTPManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 0d91672904..c0d49e8638 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -165,7 +165,8 @@ void HTTPManager::isTcpServerListening() { if (!isListening()) { qCWarning(embeddedwebserver) << "Socket on port " << QString::number(_port) << " is no longer listening"; bindSocket(); - }} + } +} bool HTTPManager::bindSocket() { qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); From 2c23dab2e74b5333603be7ba224445135e683833 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 20 Aug 2015 08:03:37 -0600 Subject: [PATCH 040/103] More specific logging --- libraries/embedded-webserver/src/HTTPManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index c0d49e8638..6e953598de 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -171,7 +171,7 @@ void HTTPManager::isTcpServerListening() { bool HTTPManager::bindSocket() { qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); if (!listen(QHostAddress::Any, _port)) { - qCWarning(embeddedwebserver) << "Failed to open HTTP server socket:" << errorString() << " can't continue"; + qCritical() << "Failed to open HTTP server socket:" << errorString() << " can't continue"; QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); } return true; From 129761c002781c0c48341dda89d78142af765279 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 20 Aug 2015 08:10:19 -0600 Subject: [PATCH 041/103] making qtimer interval a const --- libraries/embedded-webserver/src/HTTPManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 6e953598de..72436fc55e 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -21,6 +21,7 @@ #include "HTTPManager.h" const int SOCKET_ERROR_EXIT_CODE = 2; +const int SOCKET_CHECK_INTERVAL_IN_MS = 30000; HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : QTcpServer(parent), @@ -31,7 +32,7 @@ HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestH bindSocket(); _isListeningTimer = new QTimer(this); connect(_isListeningTimer, &QTimer::timeout, this, &HTTPManager::isTcpServerListening); - _isListeningTimer->start(10000); + _isListeningTimer->start(SOCKET_CHECK_INTERVAL_IN_MS); } void HTTPManager::incomingConnection(qintptr socketDescriptor) { From cd050b2e60ddd787058db61443a3ae7eca71a562 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Aug 2015 09:53:58 -0700 Subject: [PATCH 042/103] don't show hmd tools for direct mod HMDs --- interface/src/Application.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5f8b17f178..a28435e258 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4803,10 +4803,17 @@ void Application::updateDisplayMode() { oldDisplayPlugin = _displayPlugin; _displayPlugin = newDisplayPlugin; - + + // If the displayPlugin is a screen based HMD, then it will want the HMDTools displayed + // Direct Mode HMDs (like windows Oculus) will be isHmd() but will have a screen of -1 + bool newPluginWantsHMDTools = newDisplayPlugin ? + (newDisplayPlugin->isHmd() && (newDisplayPlugin->getHmdScreen() >= 0)) : false; + bool oldPluginWantedHMDTools = oldDisplayPlugin ? + (oldDisplayPlugin->isHmd() && (oldDisplayPlugin->getHmdScreen() >= 0)) : false; + // Only show the hmd tools after the correct plugin has // been activated so that it's UI is setup correctly - if (newDisplayPlugin->isHmd()) { + if (newPluginWantsHMDTools) { showDisplayPluginsTools(); } @@ -4815,7 +4822,7 @@ void Application::updateDisplayMode() { _offscreenContext->makeCurrent(); // if the old plugin was HMD and the new plugin is not HMD, then hide our hmdtools - if (oldDisplayPlugin->isHmd() && !newDisplayPlugin->isHmd()) { + if (oldPluginWantedHMDTools && !newPluginWantsHMDTools) { DependencyManager::get()->hmdTools(false); } } From c25082d86f45c5562750ddd81e6afb77f159b903 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 20 Aug 2015 10:14:16 -0700 Subject: [PATCH 043/103] use -fsanitize=address in all code rather than just the top-level links --- CMakeLists.txt | 6 ++++++ assignment-client/CMakeLists.txt | 11 +++-------- cmake/macros/MemoryDebugger.cmake | 21 +++++++++++++++++++++ domain-server/CMakeLists.txt | 10 ++-------- gvr-interface/CMakeLists.txt | 4 +++- ice-server/CMakeLists.txt | 4 +++- interface/CMakeLists.txt | 10 ++-------- libraries/animation/CMakeLists.txt | 4 +++- libraries/audio-client/CMakeLists.txt | 4 +++- libraries/audio/CMakeLists.txt | 2 ++ libraries/auto-updater/CMakeLists.txt | 3 +++ libraries/avatars/CMakeLists.txt | 2 ++ libraries/display-plugins/CMakeLists.txt | 4 +++- libraries/embedded-webserver/CMakeLists.txt | 4 +++- libraries/entities-renderer/CMakeLists.txt | 2 ++ libraries/entities/CMakeLists.txt | 2 ++ libraries/environment/CMakeLists.txt | 4 +++- libraries/fbx/CMakeLists.txt | 4 +++- libraries/gpu/CMakeLists.txt | 2 ++ libraries/input-plugins/CMakeLists.txt | 4 +++- libraries/model/CMakeLists.txt | 4 +++- libraries/networking/CMakeLists.txt | 4 +++- libraries/octree/CMakeLists.txt | 2 ++ libraries/physics/CMakeLists.txt | 2 ++ libraries/plugins/CMakeLists.txt | 5 +++-- libraries/render-utils/CMakeLists.txt | 2 ++ libraries/render/CMakeLists.txt | 4 +++- libraries/script-engine/CMakeLists.txt | 2 ++ libraries/shared/CMakeLists.txt | 2 ++ libraries/ui/CMakeLists.txt | 5 +++-- tests/CMakeLists.txt | 2 ++ tests/animation/CMakeLists.txt | 2 ++ tests/audio/CMakeLists.txt | 4 +++- tests/entities/CMakeLists.txt | 4 +++- tests/jitter/CMakeLists.txt | 4 +++- tests/networking/CMakeLists.txt | 4 +++- tests/octree/CMakeLists.txt | 4 +++- tests/physics/CMakeLists.txt | 2 ++ tests/render-utils/CMakeLists.txt | 3 +++ tests/shaders/CMakeLists.txt | 3 +++ tests/shared/CMakeLists.txt | 4 +++- tests/ui/CMakeLists.txt | 4 +++- tools/CMakeLists.txt | 3 ++- tools/mtc/CMakeLists.txt | 4 +++- tools/scribe/CMakeLists.txt | 5 ++++- tools/vhacd-util/CMakeLists.txt | 2 ++ 46 files changed, 143 insertions(+), 50 deletions(-) create mode 100644 cmake/macros/MemoryDebugger.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 38bcb42e26..bc4c938f08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,3 +213,9 @@ endif () if (ANDROID OR DESKTOP_GVR) add_subdirectory(gvr-interface) endif () + +if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) + if (UNIX) + MESSAGE("-- Memory debugging is enabled") + endif (UNIX) +endif () diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 7e3b2e6af9..315eeb6b83 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -18,12 +18,7 @@ if (UNIX) endif (UNIX) include_application_version() -copy_dlls_beside_windows_executable() -if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) - if (UNIX) - MESSAGE("-- assignment-client memory debugging is enabled.") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") - endif (UNIX) -endif () +setup_memory_debugger() + +copy_dlls_beside_windows_executable() diff --git a/cmake/macros/MemoryDebugger.cmake b/cmake/macros/MemoryDebugger.cmake new file mode 100644 index 0000000000..cb907efa96 --- /dev/null +++ b/cmake/macros/MemoryDebugger.cmake @@ -0,0 +1,21 @@ +# +# MemoryDebugger.cmake +# +# 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 +# + +macro(SETUP_MEMORY_DEBUGGER) +if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) + SET( HIFI_MEMORY_DEBUGGING true ) +endif () + +if (HIFI_MEMORY_DEBUGGING) + if (UNIX) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libasan -static-libstdc++ -fsanitize=address") + endif (UNIX) +endif () +endmacro(SETUP_MEMORY_DEBUGGER) diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index 0d56e34cf2..d2f30b6c25 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME domain-server) +setup_memory_debugger() + if (UPPER_CMAKE_BUILD_TYPE MATCHES DEBUG AND NOT WIN32) set(_SHOULD_SYMLINK_RESOURCES TRUE) else () @@ -33,11 +35,3 @@ target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES}) include_application_version() copy_dlls_beside_windows_executable() - -if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) - if (UNIX) - MESSAGE("-- domain-server memory debugging is enabled.") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") - endif (UNIX) -endif () diff --git a/gvr-interface/CMakeLists.txt b/gvr-interface/CMakeLists.txt index a986fcae0d..c4880a80b6 100644 --- a/gvr-interface/CMakeLists.txt +++ b/gvr-interface/CMakeLists.txt @@ -88,4 +88,6 @@ if (ANDROID) endif (ANDROID) -copy_dlls_beside_windows_executable() \ No newline at end of file +setup_memory_debugger() + +copy_dlls_beside_windows_executable() diff --git a/ice-server/CMakeLists.txt b/ice-server/CMakeLists.txt index 13d89fc4a2..d62192bcec 100644 --- a/ice-server/CMakeLists.txt +++ b/ice-server/CMakeLists.txt @@ -1,9 +1,11 @@ set(TARGET_NAME ice-server) +setup_memory_debugger() + # setup the project and link required Qt modules setup_hifi_project(Network) # link the shared hifi libraries link_hifi_libraries(embedded-webserver networking shared) -copy_dlls_beside_windows_executable() \ No newline at end of file +copy_dlls_beside_windows_executable() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 16d8bacd23..d858673774 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -205,12 +205,6 @@ else (APPLE) endif() endif (APPLE) -copy_dlls_beside_windows_executable() +setup_memory_debugger() -if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) - if (UNIX) - MESSAGE("-- interface memory debugging is enabled.") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") - endif (UNIX) -endif () +copy_dlls_beside_windows_executable() diff --git a/libraries/animation/CMakeLists.txt b/libraries/animation/CMakeLists.txt index 8c75d5620c..fc7fa23dcc 100644 --- a/libraries/animation/CMakeLists.txt +++ b/libraries/animation/CMakeLists.txt @@ -3,4 +3,6 @@ set(TARGET_NAME animation) # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network Script) -link_hifi_libraries(shared gpu model fbx) \ No newline at end of file +setup_memory_debugger() + +link_hifi_libraries(shared gpu model fbx) diff --git a/libraries/audio-client/CMakeLists.txt b/libraries/audio-client/CMakeLists.txt index 43a2016acf..c313aecbc0 100644 --- a/libraries/audio-client/CMakeLists.txt +++ b/libraries/audio-client/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME audio-client) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network Multimedia) @@ -25,4 +27,4 @@ if (APPLE) find_library(CoreAudio CoreAudio) find_library(CoreFoundation CoreFoundation) target_link_libraries(${TARGET_NAME} ${CoreAudio} ${CoreFoundation}) -endif () \ No newline at end of file +endif () diff --git a/libraries/audio/CMakeLists.txt b/libraries/audio/CMakeLists.txt index c03f588d94..a0d40b1a10 100644 --- a/libraries/audio/CMakeLists.txt +++ b/libraries/audio/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME audio) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network) diff --git a/libraries/auto-updater/CMakeLists.txt b/libraries/auto-updater/CMakeLists.txt index b3665af2cb..6960d8368d 100644 --- a/libraries/auto-updater/CMakeLists.txt +++ b/libraries/auto-updater/CMakeLists.txt @@ -1,3 +1,6 @@ set(TARGET_NAME auto-updater) + +setup_memory_debugger() + setup_hifi_library(Network) link_hifi_libraries(shared networking) diff --git a/libraries/avatars/CMakeLists.txt b/libraries/avatars/CMakeLists.txt index acc939b25c..b05c667c71 100644 --- a/libraries/avatars/CMakeLists.txt +++ b/libraries/avatars/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME avatars) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network Script) diff --git a/libraries/display-plugins/CMakeLists.txt b/libraries/display-plugins/CMakeLists.txt index 321b13f191..79b41fa957 100644 --- a/libraries/display-plugins/CMakeLists.txt +++ b/libraries/display-plugins/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME display-plugins) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(OpenGL) @@ -31,4 +33,4 @@ if (WIN32) find_package(OpenVR REQUIRED) target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) -endif() \ No newline at end of file +endif() diff --git a/libraries/embedded-webserver/CMakeLists.txt b/libraries/embedded-webserver/CMakeLists.txt index 955487e540..2d8915998b 100644 --- a/libraries/embedded-webserver/CMakeLists.txt +++ b/libraries/embedded-webserver/CMakeLists.txt @@ -1,4 +1,6 @@ set(TARGET_NAME embedded-webserver) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules -setup_hifi_library(Network) \ No newline at end of file +setup_hifi_library(Network) diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index c4dddb8971..3387715348 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -26,4 +26,6 @@ find_package(PolyVox REQUIRED) target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES}) +setup_memory_debugger() + link_hifi_libraries(shared gpu script-engine render render-utils) diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index f7936ff125..368257661e 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME entities) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network Script) diff --git a/libraries/environment/CMakeLists.txt b/libraries/environment/CMakeLists.txt index a2ee9e3f55..fbdc614d26 100644 --- a/libraries/environment/CMakeLists.txt +++ b/libraries/environment/CMakeLists.txt @@ -7,4 +7,6 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -link_hifi_libraries(shared networking) \ No newline at end of file +setup_memory_debugger() + +link_hifi_libraries(shared networking) diff --git a/libraries/fbx/CMakeLists.txt b/libraries/fbx/CMakeLists.txt index 1ce1c74922..c06bb0efc1 100644 --- a/libraries/fbx/CMakeLists.txt +++ b/libraries/fbx/CMakeLists.txt @@ -7,4 +7,6 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -link_hifi_libraries(shared gpu model networking octree) \ No newline at end of file +setup_memory_debugger() + +link_hifi_libraries(shared gpu model networking octree) diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index 7a88580f7f..84320297eb 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME gpu) +setup_memory_debugger() + AUTOSCRIBE_SHADER_LIB(gpu) # use setup_hifi_library macro to setup our project and link appropriate Qt modules diff --git a/libraries/input-plugins/CMakeLists.txt b/libraries/input-plugins/CMakeLists.txt index c3ded6c587..4428327deb 100644 --- a/libraries/input-plugins/CMakeLists.txt +++ b/libraries/input-plugins/CMakeLists.txt @@ -33,6 +33,8 @@ endif() #target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS}) #target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES}) +setup_memory_debugger() + # perform standard include and linking for found externals foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) @@ -69,4 +71,4 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) add_definitions(-DSIXENSE_LIB_FILENAME=\"${${${EXTERNAL}_UPPERCASE}_LIBRARY_RELEASE}\") endif () endif () -endforeach() \ No newline at end of file +endforeach() diff --git a/libraries/model/CMakeLists.txt b/libraries/model/CMakeLists.txt index 563f347952..2099f83fec 100755 --- a/libraries/model/CMakeLists.txt +++ b/libraries/model/CMakeLists.txt @@ -1,7 +1,9 @@ set(TARGET_NAME model) - + AUTOSCRIBE_SHADER_LIB(gpu model) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library() diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index d79e6bde58..d0e0b850c7 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME networking) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network) @@ -29,4 +31,4 @@ target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES} ${TBB_LIBRARIES}) # append tbb includes to our list of includes to bubble target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${TBB_INCLUDE_DIRS}) -include_application_version() \ No newline at end of file +include_application_version() diff --git a/libraries/octree/CMakeLists.txt b/libraries/octree/CMakeLists.txt index cc36aead15..8b9ff6bda2 100644 --- a/libraries/octree/CMakeLists.txt +++ b/libraries/octree/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME octree) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library() diff --git a/libraries/physics/CMakeLists.txt b/libraries/physics/CMakeLists.txt index b1f9fbb79c..802665b948 100644 --- a/libraries/physics/CMakeLists.txt +++ b/libraries/physics/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME physics) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library() diff --git a/libraries/plugins/CMakeLists.txt b/libraries/plugins/CMakeLists.txt index 28b136ccf4..98fd5fdc93 100644 --- a/libraries/plugins/CMakeLists.txt +++ b/libraries/plugins/CMakeLists.txt @@ -7,6 +7,7 @@ link_hifi_libraries(shared) add_dependency_external_projects(glm) find_package(GLM REQUIRED) + +setup_memory_debugger() + target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) - - diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 0ea71e54e3..ceb1a192ab 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -40,4 +40,6 @@ add_dependency_external_projects(oglplus) find_package(OGLPLUS REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${OGLPLUS_INCLUDE_DIRS}) +setup_memory_debugger() + link_hifi_libraries(animation fbx shared gpu model render environment) diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt index 4d2be949e6..1f73d93519 100644 --- a/libraries/render/CMakeLists.txt +++ b/libraries/render/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME render) +setup_memory_debugger() + AUTOSCRIBE_SHADER_LIB(gpu model) # use setup_hifi_library macro to setup our project and link appropriate Qt modules @@ -21,4 +23,4 @@ if (WIN32) target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}") endif () endif() -endif (WIN32) \ No newline at end of file +endif (WIN32) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 99d9149c3a..6bb53389af 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME script-engine) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Gui Network Script Widgets) diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 00a80619bc..a80f4194ef 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME shared) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules # TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp) setup_hifi_library(Gui Network Script Widgets) diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index 1aefc99c78..68caa940c1 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -7,6 +7,7 @@ link_hifi_libraries(render-utils shared) add_dependency_external_projects(glm) find_package(GLM REQUIRED) + +setup_memory_debugger() + target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) - - diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1593b649a0..c2dc30c4bb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -35,3 +35,5 @@ set_target_properties("all-tests" PROPERTIES FOLDER "hidden/test-targets") set_target_properties("all-tests" PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD TRUE EXCLUDE_FROM_ALL TRUE) + +setup_memory_debugger() diff --git a/tests/animation/CMakeLists.txt b/tests/animation/CMakeLists.txt index 2e9dbc9424..bc1e93a94f 100644 --- a/tests/animation/CMakeLists.txt +++ b/tests/animation/CMakeLists.txt @@ -6,4 +6,6 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro () +setup_memory_debugger() + setup_hifi_testcase() diff --git a/tests/audio/CMakeLists.txt b/tests/audio/CMakeLists.txt index 8e894e929e..c56ef049bd 100644 --- a/tests/audio/CMakeLists.txt +++ b/tests/audio/CMakeLists.txt @@ -6,4 +6,6 @@ macro (SETUP_TESTCASE_DEPENDENCIES) copy_dlls_beside_windows_executable() endmacro () -setup_hifi_testcase() \ No newline at end of file +setup_memory_debugger() + +setup_hifi_testcase() diff --git a/tests/entities/CMakeLists.txt b/tests/entities/CMakeLists.txt index 0077549100..f83efe7f64 100644 --- a/tests/entities/CMakeLists.txt +++ b/tests/entities/CMakeLists.txt @@ -9,4 +9,6 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries link_hifi_libraries(entities avatars shared octree gpu model fbx networking animation environment) -copy_dlls_beside_windows_executable() \ No newline at end of file +setup_memory_debugger() + +copy_dlls_beside_windows_executable() diff --git a/tests/jitter/CMakeLists.txt b/tests/jitter/CMakeLists.txt index 7b636aa87f..ba46582b02 100644 --- a/tests/jitter/CMakeLists.txt +++ b/tests/jitter/CMakeLists.txt @@ -7,4 +7,6 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro() -setup_hifi_testcase() \ No newline at end of file +setup_memory_debugger() + +setup_hifi_testcase() diff --git a/tests/networking/CMakeLists.txt b/tests/networking/CMakeLists.txt index 3be2fff027..fcf32d89c8 100644 --- a/tests/networking/CMakeLists.txt +++ b/tests/networking/CMakeLists.txt @@ -7,4 +7,6 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro () -setup_hifi_testcase() \ No newline at end of file +setup_memory_debugger() + +setup_hifi_testcase() diff --git a/tests/octree/CMakeLists.txt b/tests/octree/CMakeLists.txt index a605a4088b..77511c682a 100644 --- a/tests/octree/CMakeLists.txt +++ b/tests/octree/CMakeLists.txt @@ -7,4 +7,6 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro () -setup_hifi_testcase(Script Network) \ No newline at end of file +setup_memory_debugger() + +setup_hifi_testcase(Script Network) diff --git a/tests/physics/CMakeLists.txt b/tests/physics/CMakeLists.txt index 36cf21c681..1a6f49430b 100644 --- a/tests/physics/CMakeLists.txt +++ b/tests/physics/CMakeLists.txt @@ -21,4 +21,6 @@ macro (SETUP_TESTCASE_DEPENDENCIES) copy_dlls_beside_windows_executable() endmacro () +setup_memory_debugger() + setup_hifi_testcase(Script) diff --git a/tests/render-utils/CMakeLists.txt b/tests/render-utils/CMakeLists.txt index 97d3214744..1b47f85099 100644 --- a/tests/render-utils/CMakeLists.txt +++ b/tests/render-utils/CMakeLists.txt @@ -9,4 +9,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") link_hifi_libraries(render-utils gpu shared) message(${PROJECT_BINARY_DIR}) + +setup_memory_debugger() + copy_dlls_beside_windows_executable() diff --git a/tests/shaders/CMakeLists.txt b/tests/shaders/CMakeLists.txt index eefdf2aa3a..3ee9f4ae9f 100644 --- a/tests/shaders/CMakeLists.txt +++ b/tests/shaders/CMakeLists.txt @@ -18,4 +18,7 @@ include_directories("${PROJECT_BINARY_DIR}/../../libraries/entities-renderer/") include_directories("${PROJECT_BINARY_DIR}/../../libraries/model/") message(${PROJECT_BINARY_DIR}) + +setup_memory_debugger() + copy_dlls_beside_windows_executable() diff --git a/tests/shared/CMakeLists.txt b/tests/shared/CMakeLists.txt index bc6eab0212..c3d6cfd810 100644 --- a/tests/shared/CMakeLists.txt +++ b/tests/shared/CMakeLists.txt @@ -8,4 +8,6 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro () -setup_hifi_testcase() \ No newline at end of file +setup_memory_debugger() + +setup_hifi_testcase() diff --git a/tests/ui/CMakeLists.txt b/tests/ui/CMakeLists.txt index ad1009d925..c0f20a280f 100644 --- a/tests/ui/CMakeLists.txt +++ b/tests/ui/CMakeLists.txt @@ -13,4 +13,6 @@ endif() # link in the shared libraries link_hifi_libraries(ui render-utils gpu shared) -copy_dlls_beside_windows_executable() \ No newline at end of file +setup_memory_debugger() + +copy_dlls_beside_windows_executable() diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 55994f3d89..4d8618b37c 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,3 +1,5 @@ +setup_memory_debugger() + # add the tool directories add_subdirectory(mtc) set_target_properties(mtc PROPERTIES FOLDER "Tools") @@ -7,4 +9,3 @@ set_target_properties(scribe PROPERTIES FOLDER "Tools") add_subdirectory(vhacd-util) set_target_properties(vhacd-util PROPERTIES FOLDER "Tools") - diff --git a/tools/mtc/CMakeLists.txt b/tools/mtc/CMakeLists.txt index 5c598eaf0b..fe5f1920bc 100644 --- a/tools/mtc/CMakeLists.txt +++ b/tools/mtc/CMakeLists.txt @@ -1,4 +1,6 @@ set(TARGET_NAME mtc) setup_hifi_project() -copy_dlls_beside_windows_executable() \ No newline at end of file +setup_memory_debugger() + +copy_dlls_beside_windows_executable() diff --git a/tools/scribe/CMakeLists.txt b/tools/scribe/CMakeLists.txt index b71a287e46..0abf70f727 100755 --- a/tools/scribe/CMakeLists.txt +++ b/tools/scribe/CMakeLists.txt @@ -1,2 +1,5 @@ set(TARGET_NAME scribe) -setup_hifi_project() \ No newline at end of file + +setup_memory_debugger() + +setup_hifi_project() diff --git a/tools/vhacd-util/CMakeLists.txt b/tools/vhacd-util/CMakeLists.txt index c94b2ad083..b79a6a1893 100644 --- a/tools/vhacd-util/CMakeLists.txt +++ b/tools/vhacd-util/CMakeLists.txt @@ -8,6 +8,8 @@ find_package(VHACD REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${VHACD_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${VHACD_LIBRARIES}) +setup_memory_debugger() + if (UNIX AND NOT APPLE) include(FindOpenMP) if(OPENMP_FOUND) From 0ac885da80ab10197608c5c7f902d6259deffc63 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 20 Aug 2015 10:17:13 -0700 Subject: [PATCH 044/103] fix when memory debugging message is printed --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc4c938f08..b2f35b1443 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -215,6 +215,9 @@ if (ANDROID OR DESKTOP_GVR) endif () if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) + SET( HIFI_MEMORY_DEBUGGING true ) +endif () +if (HIFI_MEMORY_DEBUGGING) if (UNIX) MESSAGE("-- Memory debugging is enabled") endif (UNIX) From aac15e52bb223fac707f9c2f4e77ab6982ef11d1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 20 Aug 2015 10:19:34 -0700 Subject: [PATCH 045/103] add explicit cast to double for uptime --- domain-server/src/DomainServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 369cb3b761..23957380e6 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1502,7 +1502,7 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { nodeJson[JSON_KEY_LOCAL_SOCKET] = jsonForSocket(node->getLocalSocket()); // add the node uptime in our list - nodeJson[JSON_KEY_UPTIME] = QString::number((QDateTime::currentMSecsSinceEpoch() - node->getWakeTimestamp()) / 1000.0); + nodeJson[JSON_KEY_UPTIME] = QString::number(double(QDateTime::currentMSecsSinceEpoch() - node->getWakeTimestamp()) / 1000.0); // if the node has pool information, add it DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); From f8344183846c6e1a17b57d004f8af86fa0e94309 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 20 Aug 2015 11:23:16 -0700 Subject: [PATCH 046/103] Fix edit.js to warn when creating entities outside of world bounds --- examples/edit.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index deda035d5e..988e0a04b1 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1036,7 +1036,6 @@ function handeMenuEvent(menuItem) { // This function tries to find a reasonable position to place a new entity based on the camera // position. If a reasonable position within the world bounds can't be found, `null` will // be returned. The returned position will also take into account grid snapping settings. -// FIXME - technically we should guard against very large positions too function getPositionToCreateEntity() { var distance = cameraManager.enabled ? cameraManager.zoomDistance : DEFAULT_ENTITY_DRAG_DROP_DISTANCE; var direction = Quat.getFront(Camera.orientation); @@ -1047,18 +1046,20 @@ function getPositionToCreateEntity() { var HALF_TREE_SCALE = 16384; - var cameraOutOfBounds = cameraPosition.x < -HALF_TREE_SCALE || cameraPosition.y < -HALF_TREE_SCALE || - cameraPosition.z < -HALF_TREE_SCALE; - var placementOutOfBounds = placementPosition.x < -HALF_TREE_SCALE || placementPosition.y < -HALF_TREE_SCALE || - placementPosition.z < -HALF_TREE_SCALE; + var cameraOutOfBounds = Math.abs(cameraPosition.x) > HALF_TREE_SCALE + || Math.abs(cameraPosition.y) > HALF_TREE_SCALE + || Math.abs(cameraPosition.z) > HALF_TREE_SCALE; + var placementOutOfBounds = Math.abs(placementPosition.x) > HALF_TREE_SCALE + || Math.abs(placementPosition.y) > HALF_TREE_SCALE + || Math.abs(placementPosition.z) > HALF_TREE_SCALE; if (cameraOutOfBounds && placementOutOfBounds) { return null; } - placementPosition.x = Math.max(-HALF_TREE_SCALE, placementPosition.x); - placementPosition.y = Math.max(-HALF_TREE_SCALE, placementPosition.y); - placementPosition.z = Math.max(-HALF_TREE_SCALE, placementPosition.z); + placementPosition.x = Math.min(HALF_TREE_SCALE, Math.max(-HALF_TREE_SCALE, placementPosition.x)); + placementPosition.y = Math.min(HALF_TREE_SCALE, Math.max(-HALF_TREE_SCALE, placementPosition.y)); + placementPosition.z = Math.min(HALF_TREE_SCALE, Math.max(-HALF_TREE_SCALE, placementPosition.z)); return placementPosition; } From e93b360908141742750fe10a231bc41e6132effa Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 20 Aug 2015 12:12:43 -0700 Subject: [PATCH 047/103] expose options property to AI script interface --- libraries/audio/src/AudioInjector.cpp | 30 +++++++++---------- libraries/audio/src/AudioInjector.h | 9 +++--- libraries/avatars/src/Player.cpp | 2 +- .../script-engine/src/ScriptAudioInjector.h | 6 ++-- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 22d57176a5..8fd7cb9ce5 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -77,9 +77,9 @@ void AudioInjector::injectAudio() { int byteOffset = (int) floorf(AudioConstants::SAMPLE_RATE * _options.secondOffset * (_options.stereo ? 2.0f : 1.0f)); byteOffset *= sizeof(int16_t); - _currentSendPosition = byteOffset; + _currentSendOffset = byteOffset; } else { - _currentSendPosition = 0; + _currentSendOffset = 0; } if (_options.localOnly) { @@ -119,7 +119,7 @@ void AudioInjector::injectLocally() { _localBuffer->setVolume(_options.volume); // give our current send position to the local buffer - _localBuffer->setCurrentOffset(_currentSendPosition); + _localBuffer->setCurrentOffset(_currentSendOffset); success = _localAudioInterface->outputLocalInjector(_options.stereo, this); @@ -144,9 +144,9 @@ void AudioInjector::injectLocally() { const uchar MAX_INJECTOR_VOLUME = 0xFF; void AudioInjector::injectToMixer() { - if (_currentSendPosition < 0 || - _currentSendPosition >= _audioData.size()) { - _currentSendPosition = 0; + if (_currentSendOffset < 0 || + _currentSendOffset >= _audioData.size()) { + _currentSendOffset = 0; } auto nodeList = DependencyManager::get(); @@ -203,15 +203,15 @@ void AudioInjector::injectToMixer() { // loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks quint16 outgoingInjectedAudioSequenceNumber = 0; - while (_currentSendPosition < _audioData.size() && !_shouldStop) { + while (_currentSendOffset < _audioData.size() && !_shouldStop) { int bytesToCopy = std::min(((_options.stereo) ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, - _audioData.size() - _currentSendPosition); + _audioData.size() - _currentSendOffset); // Measure the loudness of this frame _loudness = 0.0f; for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) { - _loudness += abs(*reinterpret_cast(_audioData.data() + _currentSendPosition + i)) / + _loudness += abs(*reinterpret_cast(_audioData.data() + _currentSendOffset + i)) / (AudioConstants::MAX_SAMPLE_VALUE / 2.0f); } _loudness /= (float)(bytesToCopy / sizeof(int16_t)); @@ -220,7 +220,7 @@ void AudioInjector::injectToMixer() { // pack the sequence number audioPacket->writePrimitive(outgoingInjectedAudioSequenceNumber); - + audioPacket->seek(positionOptionOffset); audioPacket->writePrimitive(_options.position); audioPacket->writePrimitive(_options.orientation); @@ -232,7 +232,7 @@ void AudioInjector::injectToMixer() { audioPacket->seek(audioDataOffset); // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet - audioPacket->write(_audioData.data() + _currentSendPosition, bytesToCopy); + audioPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy); // set the correct size used for this packet audioPacket->setPayloadSize(audioPacket->pos()); @@ -246,11 +246,11 @@ void AudioInjector::injectToMixer() { outgoingInjectedAudioSequenceNumber++; } - _currentSendPosition += bytesToCopy; + _currentSendOffset += bytesToCopy; // send two packets before the first sleep so the mixer can start playback right away - if (_currentSendPosition != bytesToCopy && _currentSendPosition < _audioData.size()) { + if (_currentSendOffset != bytesToCopy && _currentSendOffset < _audioData.size()) { // process events in case we have been told to stop and be deleted QCoreApplication::processEvents(); @@ -268,8 +268,8 @@ void AudioInjector::injectToMixer() { } } - if (shouldLoop && _currentSendPosition >= _audioData.size()) { - _currentSendPosition = 0; + if (shouldLoop && _currentSendOffset >= _audioData.size()) { + _currentSendOffset = 0; } } } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 88d3f1e151..d65925b865 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -31,7 +31,6 @@ class AbstractAudioInterface; class AudioInjector : public QObject { Q_OBJECT - Q_PROPERTY(AudioInjectorOptions options WRITE setOptions READ getOptions) public: AudioInjector(QObject* parent); AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions); @@ -39,7 +38,8 @@ public: bool isFinished() const { return _isFinished; } - int getCurrentSendPosition() const { return _currentSendPosition; } + int getCurrentSendOffset() const { return _currentSendOffset; } + void setCurrentSendOffset(int currentSendOffset) { _currentSendOffset = currentSendOffset; } AudioInjectorLocalBuffer* getLocalBuffer() const { return _localBuffer; } bool isLocalOnly() const { return _options.localOnly; } @@ -58,9 +58,8 @@ public slots: void stopAndDeleteLater(); const AudioInjectorOptions& getOptions() const { return _options; } - void setOptions(const AudioInjectorOptions& options) { _options = options; } + void setOptions(const AudioInjectorOptions& options) { _options = options; } - void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; } float getLoudness() const { return _loudness; } bool isPlaying() const { return _isPlaying; } void restartPortionAfterFinished(); @@ -82,7 +81,7 @@ private: bool _isStarted = false; bool _isFinished = false; bool _shouldDeleteAfterFinish = false; - int _currentSendPosition = 0; + int _currentSendOffset = 0; AbstractAudioInterface* _localAudioInterface = NULL; AudioInjectorLocalBuffer* _localBuffer = NULL; }; diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index e7d94f0735..29544924b2 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -371,7 +371,7 @@ void Player::setAudioInjectorPosition() { int MSEC_PER_SEC = 1000; int FRAME_SIZE = sizeof(AudioConstants::AudioSample) * _recording->numberAudioChannel(); int currentAudioFrame = elapsed() * FRAME_SIZE * (AudioConstants::SAMPLE_RATE / MSEC_PER_SEC); - _injector->setCurrentSendPosition(currentAudioFrame); + _injector->setCurrentSendOffset(currentAudioFrame); } void Player::setPlayFromCurrentLocation(bool playFromCurrentLocation) { diff --git a/libraries/script-engine/src/ScriptAudioInjector.h b/libraries/script-engine/src/ScriptAudioInjector.h index 92bc5d31da..0d16b26fdf 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.h +++ b/libraries/script-engine/src/ScriptAudioInjector.h @@ -21,13 +21,15 @@ class ScriptAudioInjector : public QObject { Q_PROPERTY(bool isPlaying READ isPlaying) Q_PROPERTY(float loudness READ getLoudness) + Q_PROPERTY(AudioInjectorOptions options WRITE setOptions READ getOptions) public: ScriptAudioInjector(AudioInjector* injector); ~ScriptAudioInjector(); public slots: void restart() { _injector->restart(); } void stop() { _injector->stop(); } - + + const AudioInjectorOptions& getOptions() const { return _injector->getOptions(); } void setOptions(const AudioInjectorOptions& options) { _injector->setOptions(options); } float getLoudness() const { return _injector->getLoudness(); } @@ -49,4 +51,4 @@ Q_DECLARE_METATYPE(ScriptAudioInjector*) QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in); void injectorFromScriptValue(const QScriptValue& object, ScriptAudioInjector*& out); -#endif // hifi_ScriptAudioInjector_h \ No newline at end of file +#endif // hifi_ScriptAudioInjector_h From f26849c7e15a25b836308f9c3a613e43ef48eded Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 20 Aug 2015 12:34:50 -0700 Subject: [PATCH 048/103] Proper HMD scaling. --- interface/src/avatar/MyAvatar.cpp | 40 +++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 52473a6d47..cd968c3d59 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -846,7 +846,7 @@ void MyAvatar::sendKillAvatar() { void MyAvatar::updateLookAtTargetAvatar() { // // Look at the avatar whose eyes are closest to the ray in direction of my avatar's head - // + // And set the correctedLookAt for all (nearby) avatars that are looking at me. _lookAtTargetAvatar.reset(); _targetAvatarPosition = glm::vec3(0.0f); @@ -870,14 +870,39 @@ void MyAvatar::updateLookAtTargetAvatar() { smallestAngleTo = angleTo; } if (Application::getInstance()->isLookingAtMyAvatar(avatar)) { + // Alter their gaze to look directly at my camera; this looks more natural than looking at my avatar's face. // Offset their gaze according to whether they're looking at one of my eyes or my mouth. - glm::vec3 gazeOffset = avatar->getHead()->getLookAtPosition() - getHead()->getEyePosition(); - const float HUMAN_EYE_SEPARATION = 0.065f; - float myEyeSeparation = glm::length(getHead()->getLeftEyePosition() - getHead()->getRightEyePosition()); - gazeOffset = gazeOffset * HUMAN_EYE_SEPARATION / myEyeSeparation; - avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition() - + gazeOffset); + + // The camera isn't at the point midway between the avatar eyes. (Even without an HMD, the head can be offset a bit.) + // First find out where (in world space) the person is looking relative to that bridge-of-the-avatar point. + // (We will be adding that offset to the camera position, after making some other adjustments.) + glm::vec3 lookAtPosition = avatar->getHead()->getLookAtPosition(); + glm::vec3 gazeOffset = lookAtPosition - getHead()->getEyePosition(); + + // Scale by proportional differences between avatar and human. + glm::mat4 leftEye = Application::getInstance()->getEyeOffset(Eye::Left); // Pose? + glm::mat4 rightEye = Application::getInstance()->getEyeOffset(Eye::Right); + glm::vec3 leftEyePosition = glm::vec3(leftEye[3]); + glm::vec3 rightEyePosition = glm::vec3(rightEye[3]); + float humanEyeSeparationInModelSpace = glm::length(leftEyePosition - rightEyePosition); + float avatarEyeSeparation = glm::length(getHead()->getLeftEyePosition() - getHead()->getRightEyePosition()); + gazeOffset = gazeOffset * humanEyeSeparationInModelSpace / avatarEyeSeparation; + + // If the camera is also not oriented with the head, adjust by getting the offset in head-space... + /* Not needed (i.e., code is a no-op), but I'm leaving the example code here in case something like this is needed someday. + glm::quat avatarHeadOrientation = getHead()->getOrientation(); + glm::vec3 gazeOffsetLocalToHead = glm::inverse(avatarHeadOrientation) * gazeOffset; + // ... and treat that as though it were in camera space, bringing it back to world space. + // But camera is fudged to make the picture feel like the avatar's orientation. + glm::quat humanOrientation = Application::getInstance()->getViewFrustum()->getOrientation(); // or just avatar getOrienation() ? + gazeOffset = humanOrientation * gazeOffsetLocalToHead; + */ + + // And now we can finally add that offset to the camera. + glm::vec3 corrected = Application::getInstance()->getViewFrustum()->getPosition() + gazeOffset; + avatar->getHead()->setCorrectedLookAtPosition(corrected); + } else { avatar->getHead()->clearCorrectedLookAtPosition(); } @@ -1114,6 +1139,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl getHead()->render(renderArgs, 1.0f, renderFrustum); } + // This is drawing the lookat vectors from our avatar to wherever we're looking. if (qApp->isHMDMode()) { glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); From e9b3d481650e7fdfa1baa6044b07677fc22aba0b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 20 Aug 2015 16:10:31 -0700 Subject: [PATCH 049/103] Add menu item that disables adjusting eyelids to follow pupil Developer > Avatar > Disable Eyelid Adjustment --- interface/src/Menu.cpp | 1 + interface/src/Menu.h | 1 + interface/src/avatar/Head.cpp | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f074dc5ac7..11bc38c85e 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -444,6 +444,7 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Connexion, 0, false, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index ca46b80f92..278da363d1 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -168,6 +168,7 @@ namespace MenuOption { const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DeleteBookmark = "Delete Bookmark..."; const QString DisableActivityLogger = "Disable Activity Logger"; + const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment"; const QString DisableLightEntities = "Disable Light Entities"; const QString DisableNackPackets = "Disable Entity NACK Packets"; const QString DiskCacheEditor = "Disk Cache Editor"; diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 9ab2c83a79..d645253eab 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -277,6 +277,10 @@ void Head::calculateMouthShapes() { void Head::applyEyelidOffset(glm::quat headOrientation) { // Adjusts the eyelid blendshape coefficients so that the eyelid follows the iris as the head pitches. + if (Menu::getInstance()->isOptionChecked(MenuOption::DisableEyelidAdjustment)) { + return; + } + glm::quat eyeRotation = rotationBetween(headOrientation * IDENTITY_FRONT, getLookAtPosition() - _eyePosition); eyeRotation = eyeRotation * glm::angleAxis(safeEulerAngles(headOrientation).y, IDENTITY_UP); // Rotation w.r.t. head float eyePitch = safeEulerAngles(eyeRotation).x; From 05f4145acb97974b8fb40a85ac28e2a333b647c0 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 20 Aug 2015 16:28:40 -0700 Subject: [PATCH 050/103] Checkpoint. Working? --- interface/src/avatar/MyAvatar.cpp | 54 ++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index cd968c3d59..1b8a3b2f89 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -843,6 +843,7 @@ void MyAvatar::sendKillAvatar() { DependencyManager::get()->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::AvatarMixer); } +static int counter = 0; void MyAvatar::updateLookAtTargetAvatar() { // // Look at the avatar whose eyes are closest to the ray in direction of my avatar's head @@ -872,21 +873,34 @@ void MyAvatar::updateLookAtTargetAvatar() { if (Application::getInstance()->isLookingAtMyAvatar(avatar)) { // Alter their gaze to look directly at my camera; this looks more natural than looking at my avatar's face. - // Offset their gaze according to whether they're looking at one of my eyes or my mouth. + glm::vec3 lookAtPosition = avatar->getHead()->getLookAtPosition(); // A position, in world space, on my avatar. // The camera isn't at the point midway between the avatar eyes. (Even without an HMD, the head can be offset a bit.) + // Let's get everything to world space: + glm::vec3 avatarLeftEye = getHead()->getLeftEyePosition(); + glm::vec3 avatarRightEye = getHead()->getRightEyePosition(); + // When not in HMD, these might both answer identity (i.e., the bridge of the nose). That's ok. + // By my inpsection of the code and live testing, getEyeOffset and getEyePose are the same. (Application hands identity as offset matrix.) + glm::mat4 leftEye = Application::getInstance()->getEyeOffset(Eye::Left); + glm::mat4 rightEye = Application::getInstance()->getEyeOffset(Eye::Right); + glm::vec3 leftEyeHeadLocal = glm::vec3(leftEye[3]); + glm::vec3 rightEyeHeadLocal = glm::vec3(rightEye[3]); + auto humanSystem = Application::getInstance()->getViewFrustum(); + glm::vec3 humanLeftEye = humanSystem->getPosition() + (humanSystem->getOrientation() * leftEyeHeadLocal); + glm::vec3 humanRightEye = humanSystem->getPosition() + (humanSystem->getOrientation() * rightEyeHeadLocal); + + // debugging or some code paths + glm::vec3 avatarAverage = avatarLeftEye + ((avatarRightEye - avatarLeftEye) * 0.5f); + glm::vec3 humanAverage = humanLeftEye + ((humanRightEye - humanLeftEye) * 0.5f); + +#if 1 // First find out where (in world space) the person is looking relative to that bridge-of-the-avatar point. // (We will be adding that offset to the camera position, after making some other adjustments.) - glm::vec3 lookAtPosition = avatar->getHead()->getLookAtPosition(); glm::vec3 gazeOffset = lookAtPosition - getHead()->getEyePosition(); // Scale by proportional differences between avatar and human. - glm::mat4 leftEye = Application::getInstance()->getEyeOffset(Eye::Left); // Pose? - glm::mat4 rightEye = Application::getInstance()->getEyeOffset(Eye::Right); - glm::vec3 leftEyePosition = glm::vec3(leftEye[3]); - glm::vec3 rightEyePosition = glm::vec3(rightEye[3]); - float humanEyeSeparationInModelSpace = glm::length(leftEyePosition - rightEyePosition); - float avatarEyeSeparation = glm::length(getHead()->getLeftEyePosition() - getHead()->getRightEyePosition()); + float humanEyeSeparationInModelSpace = glm::length(humanLeftEye - humanRightEye); + float avatarEyeSeparation = glm::length(avatarLeftEye - avatarRightEye); gazeOffset = gazeOffset * humanEyeSeparationInModelSpace / avatarEyeSeparation; // If the camera is also not oriented with the head, adjust by getting the offset in head-space... @@ -895,13 +909,30 @@ void MyAvatar::updateLookAtTargetAvatar() { glm::vec3 gazeOffsetLocalToHead = glm::inverse(avatarHeadOrientation) * gazeOffset; // ... and treat that as though it were in camera space, bringing it back to world space. // But camera is fudged to make the picture feel like the avatar's orientation. - glm::quat humanOrientation = Application::getInstance()->getViewFrustum()->getOrientation(); // or just avatar getOrienation() ? + glm::quat humanOrientation = humanSystem->getOrientation(); // or just avatar getOrienation() ? gazeOffset = humanOrientation * gazeOffsetLocalToHead; - */ + glm::vec3 corrected = humanSystem->getPosition() + gazeOffset; + */ // And now we can finally add that offset to the camera. glm::vec3 corrected = Application::getInstance()->getViewFrustum()->getPosition() + gazeOffset; +#else + //glm::vec3 gazeOffset = ((humanRightEye - avatarRightEye) + (humanLeftEye - avatarLeftEye)) * 0.5f; + glm::vec3 gazeOffset = humanAverage - avatarAverage; + glm::vec3 corrected = lookAtPosition + gazeOffset; +#endif avatar->getHead()->setCorrectedLookAtPosition(corrected); + + if (counter++ > 60) { + counter = 0; + qCDebug(interfaceapp) << Application::getInstance()->isHMDMode(); + qCDebug(interfaceapp) << "camera:" << Application::getInstance()->getViewFrustum()->getPosition() << "delta from av human:" << (humanAverage - Application::getInstance()->getViewFrustum()->getPosition()); + + qCDebug(interfaceapp) << "lt avatar:" << avatarLeftEye << " lt human:" << humanLeftEye; + qCDebug(interfaceapp) << "rt avatar:" << avatarRightEye << " rt human:" << humanRightEye; + qCDebug(interfaceapp) << "av avatar:" << avatarAverage << " av humn:" << humanAverage; + qCDebug(interfaceapp) << "offset:" << gazeOffset << " corrected:" << corrected << " from:" << lookAtPosition; + } } else { avatar->getHead()->clearCorrectedLookAtPosition(); @@ -1140,6 +1171,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl } // This is drawing the lookat vectors from our avatar to wherever we're looking. + /* if (qApp->isHMDMode()) { glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); @@ -1155,7 +1187,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl cameraPosition + getOrientation() * (rightEyePosition - headPosition)); } else { getHead()->renderLookAts(renderArgs); - } + }*/ getHand()->render(renderArgs, true); } From ea02583875178380c429046fd5e8ef3fcfb8258a Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 20 Aug 2015 17:33:10 -0700 Subject: [PATCH 051/103] Cleanup. --- interface/src/avatar/MyAvatar.cpp | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1b8a3b2f89..501c7eb8d5 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -881,6 +881,7 @@ void MyAvatar::updateLookAtTargetAvatar() { glm::vec3 avatarRightEye = getHead()->getRightEyePosition(); // When not in HMD, these might both answer identity (i.e., the bridge of the nose). That's ok. // By my inpsection of the code and live testing, getEyeOffset and getEyePose are the same. (Application hands identity as offset matrix.) + // This might be more work than needed for any given use, but as we explore different formulations, we go mad if we don't work in world space. glm::mat4 leftEye = Application::getInstance()->getEyeOffset(Eye::Left); glm::mat4 rightEye = Application::getInstance()->getEyeOffset(Eye::Right); glm::vec3 leftEyeHeadLocal = glm::vec3(leftEye[3]); @@ -888,12 +889,8 @@ void MyAvatar::updateLookAtTargetAvatar() { auto humanSystem = Application::getInstance()->getViewFrustum(); glm::vec3 humanLeftEye = humanSystem->getPosition() + (humanSystem->getOrientation() * leftEyeHeadLocal); glm::vec3 humanRightEye = humanSystem->getPosition() + (humanSystem->getOrientation() * rightEyeHeadLocal); - - // debugging or some code paths - glm::vec3 avatarAverage = avatarLeftEye + ((avatarRightEye - avatarLeftEye) * 0.5f); - glm::vec3 humanAverage = humanLeftEye + ((humanRightEye - humanLeftEye) * 0.5f); -#if 1 + // First find out where (in world space) the person is looking relative to that bridge-of-the-avatar point. // (We will be adding that offset to the camera position, after making some other adjustments.) glm::vec3 gazeOffset = lookAtPosition - getHead()->getEyePosition(); @@ -916,23 +913,8 @@ void MyAvatar::updateLookAtTargetAvatar() { // And now we can finally add that offset to the camera. glm::vec3 corrected = Application::getInstance()->getViewFrustum()->getPosition() + gazeOffset; -#else - //glm::vec3 gazeOffset = ((humanRightEye - avatarRightEye) + (humanLeftEye - avatarLeftEye)) * 0.5f; - glm::vec3 gazeOffset = humanAverage - avatarAverage; - glm::vec3 corrected = lookAtPosition + gazeOffset; -#endif + avatar->getHead()->setCorrectedLookAtPosition(corrected); - - if (counter++ > 60) { - counter = 0; - qCDebug(interfaceapp) << Application::getInstance()->isHMDMode(); - qCDebug(interfaceapp) << "camera:" << Application::getInstance()->getViewFrustum()->getPosition() << "delta from av human:" << (humanAverage - Application::getInstance()->getViewFrustum()->getPosition()); - - qCDebug(interfaceapp) << "lt avatar:" << avatarLeftEye << " lt human:" << humanLeftEye; - qCDebug(interfaceapp) << "rt avatar:" << avatarRightEye << " rt human:" << humanRightEye; - qCDebug(interfaceapp) << "av avatar:" << avatarAverage << " av humn:" << humanAverage; - qCDebug(interfaceapp) << "offset:" << gazeOffset << " corrected:" << corrected << " from:" << lookAtPosition; - } } else { avatar->getHead()->clearCorrectedLookAtPosition(); From 642e56903329bbbe3c70d0795f40a2fc3f7913f7 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 20 Aug 2015 17:38:49 -0700 Subject: [PATCH 052/103] Uncomment code that was commented out to simplify debugging. --- interface/src/avatar/MyAvatar.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 501c7eb8d5..eada41eb29 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1153,7 +1153,6 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl } // This is drawing the lookat vectors from our avatar to wherever we're looking. - /* if (qApp->isHMDMode()) { glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); @@ -1169,7 +1168,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl cameraPosition + getOrientation() * (rightEyePosition - headPosition)); } else { getHead()->renderLookAts(renderArgs); - }*/ + } getHand()->render(renderArgs, true); } From 1b3d7fabc8a4ac6021441d671d249885ee4c741d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 7 Aug 2015 17:58:43 -0700 Subject: [PATCH 053/103] ResourceCache, NetworkGeometry and Model refactoring and optimizations. * Removed validation logic from Resource class, Qt does this internally and is more standards compliant. This should result in more accurate caching and faster resource fetching when cache is stale and validation fails. * Added loaded and failed slots to Resource class, so it does not have to be polled. * NetworkGeometry now uses multiple Resource objects to download the fst/mapping file and the fbx/obj models. * NetworkGeometry is no longer a subclass of Resource * NetworkGeometry now has signals for success and failure, you no longer have to poll it to determine when loading is complete (except for textures *sigh*) Some functionality was removed * NetworkGeometry no longer has a fallback * NetworkGeometry no longer loads LODs or has lod logic. * The number of FBXGeometry copies is greatly reduced. * Model::setURL no supports fallback URL, delayLoad or retainCurrent option. This can result in a pop when switching avatars, and there's no longer a default if avatar loading fails. --- interface/src/ModelPackager.cpp | 11 +- interface/src/ModelPackager.h | 4 +- interface/src/avatar/Avatar.cpp | 24 +- interface/src/avatar/Head.cpp | 3 - libraries/animation/src/AnimationCache.cpp | 17 +- libraries/animation/src/AnimationCache.h | 9 +- .../src/RenderableZoneEntityItem.cpp | 4 +- libraries/fbx/src/FBXReader.cpp | 22 +- libraries/fbx/src/FBXReader.h | 4 +- libraries/fbx/src/OBJReader.cpp | 9 +- libraries/fbx/src/OBJReader.h | 4 +- libraries/networking/src/ResourceCache.cpp | 115 +-- libraries/networking/src/ResourceCache.h | 17 +- libraries/render-utils/src/GeometryCache.cpp | 806 +++++++----------- libraries/render-utils/src/GeometryCache.h | 145 ++-- libraries/render-utils/src/Model.cpp | 175 +--- libraries/render-utils/src/Model.h | 21 +- tests/networking/src/ResourceTests.cpp | 95 +++ tests/networking/src/ResourceTests.h | 23 + tools/vhacd-util/src/VHACDUtil.cpp | 7 +- 20 files changed, 682 insertions(+), 833 deletions(-) create mode 100644 tests/networking/src/ResourceTests.cpp create mode 100644 tests/networking/src/ResourceTests.h diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index 09d572c31d..0b564f3574 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -106,16 +106,17 @@ bool ModelPackager::loadModel() { } qCDebug(interfaceapp) << "Reading FBX file : " << _fbxInfo.filePath(); QByteArray fbxContents = fbx.readAll(); - _geometry = readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath()); - + + _geometry.reset(readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath())); + // make sure we have some basic mappings - populateBasicMapping(_mapping, _fbxInfo.filePath(), _geometry); + populateBasicMapping(_mapping, _fbxInfo.filePath(), *_geometry); return true; } bool ModelPackager::editProperties() { // open the dialog to configure the rest - ModelPropertiesDialog properties(_modelType, _mapping, _modelFile.path(), _geometry); + ModelPropertiesDialog properties(_modelType, _mapping, _modelFile.path(), *_geometry); if (properties.exec() == QDialog::Rejected) { return false; } @@ -339,7 +340,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename void ModelPackager::listTextures() { _textures.clear(); - foreach (FBXMesh mesh, _geometry.meshes) { + foreach (FBXMesh mesh, _geometry->meshes) { foreach (FBXMeshPart part, mesh.parts) { if (!part.diffuseTexture.filename.isEmpty() && part.diffuseTexture.content.isEmpty() && !_textures.contains(part.diffuseTexture.filename)) { diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h index c681ae436f..10942833f9 100644 --- a/interface/src/ModelPackager.h +++ b/interface/src/ModelPackager.h @@ -39,11 +39,11 @@ private: QString _texDir; QVariantHash _mapping; - FBXGeometry _geometry; + std::unique_ptr _geometry; QStringList _textures; }; -#endif // hifi_ModelPackager_h \ No newline at end of file +#endif // hifi_ModelPackager_h diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index c1a1d11268..19f84018f8 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -196,7 +196,6 @@ void Avatar::simulate(float deltaTime) { PerformanceTimer perfTimer("hand"); getHand()->simulate(deltaTime, false); } - _skeletonModel.setLODDistance(getLODDistance()); if (!_shouldRenderBillboard && inViewFrustum) { { @@ -562,24 +561,22 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { } void Avatar::fixupModelsInScene() { - if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { - return; - } // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene render::ScenePointer scene = Application::getInstance()->getMain3DScene(); render::PendingChanges pendingChanges; - if (_skeletonModel.needsFixupInScene()) { + if (_skeletonModel.isRenderable() && _skeletonModel.needsFixupInScene()) { _skeletonModel.removeFromScene(scene, pendingChanges); _skeletonModel.addToScene(scene, pendingChanges); } - if (getHead()->getFaceModel().needsFixupInScene()) { - getHead()->getFaceModel().removeFromScene(scene, pendingChanges); - getHead()->getFaceModel().addToScene(scene, pendingChanges); + Model& faceModel = getHead()->getFaceModel(); + if (faceModel.isRenderable() && faceModel.needsFixupInScene()) { + faceModel.removeFromScene(scene, pendingChanges); + faceModel.addToScene(scene, pendingChanges); } for (auto attachmentModel : _attachmentModels) { - if (attachmentModel->needsFixupInScene()) { + if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) { attachmentModel->removeFromScene(scene, pendingChanges); attachmentModel->addToScene(scene, pendingChanges); } @@ -621,11 +618,8 @@ void Avatar::simulateAttachments(float deltaTime) { int jointIndex = getJointIndex(attachment.jointName); glm::vec3 jointPosition; glm::quat jointRotation; - if (!isMyAvatar()) { - model->setLODDistance(getLODDistance()); - } if (_skeletonModel.getJointPositionInWorldFrame(jointIndex, jointPosition) && - _skeletonModel.getJointCombinedRotation(jointIndex, jointRotation)) { + _skeletonModel.getJointCombinedRotation(jointIndex, jointRotation)) { model->setTranslation(jointPosition + jointRotation * attachment.translation * _scale); model->setRotation(jointRotation * attachment.rotation); model->setScaleToFit(true, _scale * attachment.scale, true); // hack to force rescale @@ -978,12 +972,12 @@ void Avatar::scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const { void Avatar::setFaceModelURL(const QUrl& faceModelURL) { AvatarData::setFaceModelURL(faceModelURL); - getHead()->getFaceModel().setURL(_faceModelURL, AvatarData::defaultFullAvatarModelUrl(), true, !isMyAvatar()); + getHead()->getFaceModel().setURL(_faceModelURL); } void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { AvatarData::setSkeletonModelURL(skeletonModelURL); - _skeletonModel.setURL(_skeletonModelURL, AvatarData::defaultFullAvatarModelUrl(), true, !isMyAvatar()); + _skeletonModel.setURL(_skeletonModelURL); } void Avatar::setAttachmentData(const QVector& attachmentData) { diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index d645253eab..3806dd6edc 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -233,9 +233,6 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { _saccade = glm::vec3(); } - if (!isMine) { - _faceModel.setLODDistance(static_cast(_owningAvatar)->getLODDistance()); - } _leftEyePosition = _rightEyePosition = getPosition(); if (!billboard) { _faceModel.simulate(deltaTime); diff --git a/libraries/animation/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp index 634e0589b7..7f3f393a8b 100644 --- a/libraries/animation/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -13,6 +13,7 @@ #include #include "AnimationCache.h" +#include "AnimationLogging.h" static int animationPointerMetaTypeId = qRegisterMetaType(); @@ -62,11 +63,15 @@ void AnimationReader::run() { QSharedPointer animation = _animation.toStrongRef(); if (!animation.isNull()) { QMetaObject::invokeMethod(animation.data(), "setGeometry", - Q_ARG(const FBXGeometry&, readFBX(_reply->readAll(), QVariantHash(), _reply->property("url").toString()))); + Q_ARG(FBXGeometry*, readFBX(_reply->readAll(), QVariantHash(), _reply->property("url").toString()))); } _reply->deleteLater(); } +bool Animation::isLoaded() const { + return _loaded && _geometry; +} + QStringList Animation::getJointNames() const { if (QThread::currentThread() != thread()) { QStringList result; @@ -75,7 +80,7 @@ QStringList Animation::getJointNames() const { return result; } QStringList names; - foreach (const FBXJoint& joint, _geometry.joints) { + foreach (const FBXJoint& joint, _geometry->joints) { names.append(joint.name); } return names; @@ -88,15 +93,15 @@ QVector Animation::getFrames() const { Q_RETURN_ARG(QVector, result)); return result; } - return _geometry.animationFrames; + return _geometry->animationFrames; } const QVector& Animation::getFramesReference() const { - return _geometry.animationFrames; + return _geometry->animationFrames; } -void Animation::setGeometry(const FBXGeometry& geometry) { - _geometry = geometry; +void Animation::setGeometry(FBXGeometry* geometry) { + _geometry.reset(geometry); finishedLoading(true); } diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index 6a0a77f659..3ff5957fa2 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -52,7 +52,10 @@ public: Animation(const QUrl& url); - const FBXGeometry& getGeometry() const { return _geometry; } + const FBXGeometry& getGeometry() const { return *_geometry; } + + virtual bool isLoaded() const override; + Q_INVOKABLE QStringList getJointNames() const; @@ -62,13 +65,13 @@ public: protected: - Q_INVOKABLE void setGeometry(const FBXGeometry& geometry); + Q_INVOKABLE void setGeometry(FBXGeometry* geometry); virtual void downloadFinished(QNetworkReply* reply); private: - FBXGeometry _geometry; + std::unique_ptr _geometry; }; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index bb0a35f7b0..930a684617 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -38,7 +38,7 @@ void RenderableZoneEntityItem::changeProperties(Lambda setNewProperties) { _model = getModel(); _needsInitialSimulation = true; - _model->setURL(getCompoundShapeURL(), QUrl(), true, true); + _model->setURL(getCompoundShapeURL()); } if (oldPosition != getPosition() || oldRotation != getRotation() || @@ -85,7 +85,7 @@ void RenderableZoneEntityItem::initialSimulation() { void RenderableZoneEntityItem::updateGeometry() { if (_model && !_model->isActive() && hasCompoundShapeURL()) { // Since we have a delayload, we need to update the geometry if it has been downloaded - _model->setURL(getCompoundShapeURL(), QUrl(), true); + _model->setURL(getCompoundShapeURL()); } if (_model && _model->isActive() && _needsInitialSimulation) { initialSimulation(); diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 2db5f5fa51..f0d13f8792 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1373,12 +1373,12 @@ FBXLight extractLight(const FBXNode& object) { #if USE_MODEL_MESH -void buildModelMesh(ExtractedMesh& extracted) { +void buildModelMesh(ExtractedMesh& extracted, const QString& url) { static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("buildModelMesh failed -- .*"); if (extracted.mesh.vertices.size() == 0) { extracted.mesh._mesh = model::Mesh(); - qCDebug(modelformat) << "buildModelMesh failed -- no vertices"; + qCDebug(modelformat) << "buildModelMesh failed -- no vertices, url = " << url; return; } FBXMesh& fbxMesh = extracted.mesh; @@ -1465,7 +1465,7 @@ void buildModelMesh(ExtractedMesh& extracted) { if (! totalIndices) { extracted.mesh._mesh = model::Mesh(); - qCDebug(modelformat) << "buildModelMesh failed -- no indices"; + qCDebug(modelformat) << "buildModelMesh failed -- no indices, url = " << url; return; } @@ -1505,7 +1505,7 @@ void buildModelMesh(ExtractedMesh& extracted) { mesh.setPartBuffer(pbv); } else { extracted.mesh._mesh = model::Mesh(); - qCDebug(modelformat) << "buildModelMesh failed -- no parts"; + qCDebug(modelformat) << "buildModelMesh failed -- no parts, url = " << url; return; } @@ -1530,7 +1530,7 @@ QByteArray fileOnUrl(const QByteArray& filenameString, const QString& url) { return filename; } -FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { +FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { QHash meshes; QHash modelIDsToNames; QHash meshIDsToMeshIndices; @@ -1615,7 +1615,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, #if defined(DEBUG_FBXREADER) int unknown = 0; #endif - FBXGeometry geometry; + FBXGeometry* geometryPtr = new FBXGeometry; + FBXGeometry& geometry = *geometryPtr; + float unitScaleFactor = 1.0f; glm::vec3 ambientColor; QString hifiGlobalNodeID; @@ -2680,7 +2682,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex); # if USE_MODEL_MESH - buildModelMesh(extracted); + buildModelMesh(extracted, url); # endif if (extracted.mesh.isEye) { @@ -2761,15 +2763,15 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, } } - return geometry; + return geometryPtr; } -FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { +FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { QBuffer buffer(const_cast(&model)); buffer.open(QIODevice::ReadOnly); return readFBX(&buffer, mapping, url, loadLightmaps, lightmapLevel); } -FBXGeometry readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { +FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { return extractFBXGeometry(parseFBX(device), mapping, url, loadLightmaps, lightmapLevel); } diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index b8a22b0b80..471a9c1777 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -272,10 +272,10 @@ Q_DECLARE_METATYPE(FBXGeometry) /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing -FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); +FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing -FBXGeometry readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); +FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); #endif // hifi_FBXReader_h diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index b7ae948490..841fdcfad9 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -399,15 +399,16 @@ done: } -FBXGeometry OBJReader::readOBJ(const QByteArray& model, const QVariantHash& mapping) { +FBXGeometry* OBJReader::readOBJ(const QByteArray& model, const QVariantHash& mapping) { QBuffer buffer(const_cast(&model)); buffer.open(QIODevice::ReadOnly); return readOBJ(&buffer, mapping, nullptr); } -FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url) { - FBXGeometry geometry; +FBXGeometry* OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url) { + FBXGeometry* geometryPtr = new FBXGeometry(); + FBXGeometry& geometry = *geometryPtr; OBJTokenizer tokenizer(device); float scaleGuess = 1.0f; @@ -545,7 +546,7 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, Q qCDebug(modelformat) << "OBJ reader fail: " << e.what(); } - return geometry; + return geometryPtr; } diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 2e7b050b0a..df4c88553e 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -71,8 +71,8 @@ public: QHash materials; QNetworkReply* request(QUrl& url, bool isTest); - FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping); - FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url); + FBXGeometry* readOBJ(const QByteArray& model, const QVariantHash& mapping); + FBXGeometry* readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url); private: QUrl* _url = nullptr; diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index e127380630..75028abe93 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -320,7 +320,6 @@ void Resource::attemptRequest() { void Resource::finishedLoading(bool success) { if (success) { _loaded = true; - emit loaded(); } else { _failedToLoad = true; } @@ -333,91 +332,26 @@ void Resource::reinsert() { static const int REPLY_TIMEOUT_MS = 5000; void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { - if (!_reply->isFinished()) { - _bytesReceived = bytesReceived; - _bytesTotal = bytesTotal; - _replyTimer->start(REPLY_TIMEOUT_MS); - return; - } - _reply->disconnect(this); - _replyTimer->disconnect(this); - QNetworkReply* reply = _reply; - _reply = nullptr; - _replyTimer->deleteLater(); - _replyTimer = nullptr; - ResourceCache::requestCompleted(this); - - downloadFinished(reply); + _bytesReceived = bytesReceived; + _bytesTotal = bytesTotal; + _replyTimer->start(REPLY_TIMEOUT_MS); } void Resource::handleReplyError() { - handleReplyError(_reply->error(), qDebug() << _reply->errorString()); + handleReplyErrorInternal(_reply->error()); } void Resource::handleReplyTimeout() { - handleReplyError(QNetworkReply::TimeoutError, qDebug() << "Timed out loading" << _reply->url() << - "received" << _bytesReceived << "total" << _bytesTotal); -} - -void Resource::maybeRefresh() { - if (Q_LIKELY(NetworkAccessManager::getInstance().cache())) { - QNetworkReply* reply = qobject_cast(sender()); - QVariant variant = reply->header(QNetworkRequest::LastModifiedHeader); - QNetworkCacheMetaData metaData = NetworkAccessManager::getInstance().cache()->metaData(_url); - if (variant.isValid() && variant.canConvert() && metaData.isValid()) { - QDateTime lastModified = variant.value(); - QDateTime lastModifiedOld = metaData.lastModified(); - if (lastModified.isValid() && lastModifiedOld.isValid() && - lastModifiedOld >= lastModified) { // With >=, cache won't thrash in eventually-consistent cdn. - qCDebug(networking) << "Using cached version of" << _url.fileName(); - // We don't need to update, return - return; - } - } else if (!variant.isValid() || !variant.canConvert() || - !variant.value().isValid() || variant.value().isNull()) { - qCDebug(networking) << "Cannot determine when" << _url.fileName() << "was modified last, cached version might be outdated"; - return; - } - qCDebug(networking) << "Loaded" << _url.fileName() << "from the disk cache but the network version is newer, refreshing."; - refresh(); - } + handleReplyErrorInternal(QNetworkReply::TimeoutError); } void Resource::makeRequest() { _reply = NetworkAccessManager::getInstance().get(_request); - + connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64))); connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError())); connect(_reply, SIGNAL(finished()), SLOT(handleReplyFinished())); - - if (_reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool()) { - // If the file as been updated since it was cached, refresh it - QNetworkRequest request(_request); - request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); - request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false); - QNetworkReply* reply = NetworkAccessManager::getInstance().head(request); - connect(reply, &QNetworkReply::finished, this, &Resource::maybeRefresh); - } else { - if (Q_LIKELY(NetworkAccessManager::getInstance().cache())) { - QNetworkCacheMetaData metaData = NetworkAccessManager::getInstance().cache()->metaData(_url); - bool needUpdate = false; - if (metaData.expirationDate().isNull() || metaData.expirationDate() <= QDateTime::currentDateTime()) { - // If the expiration date is NULL or in the past, - // put one far enough away that it won't be an issue. - metaData.setExpirationDate(QDateTime::currentDateTime().addYears(100)); - needUpdate = true; - } - if (metaData.lastModified().isNull()) { - // If the lastModified date is NULL, set it to now. - metaData.setLastModified(QDateTime::currentDateTime()); - needUpdate = true; - } - if (needUpdate) { - NetworkAccessManager::getInstance().cache()->updateMetaData(metaData); - } - } - } - + _replyTimer = new QTimer(this); connect(_replyTimer, SIGNAL(timeout()), SLOT(handleReplyTimeout())); _replyTimer->setSingleShot(true); @@ -425,7 +359,8 @@ void Resource::makeRequest() { _bytesReceived = _bytesTotal = 0; } -void Resource::handleReplyError(QNetworkReply::NetworkError error, QDebug debug) { +void Resource::handleReplyErrorInternal(QNetworkReply::NetworkError error) { + _reply->disconnect(this); _replyTimer->disconnect(this); _reply->deleteLater(); @@ -433,7 +368,7 @@ void Resource::handleReplyError(QNetworkReply::NetworkError error, QDebug debug) _replyTimer->deleteLater(); _replyTimer = nullptr; ResourceCache::requestCompleted(this); - + // retry for certain types of failures switch (error) { case QNetworkReply::RemoteHostClosedError: @@ -444,26 +379,46 @@ void Resource::handleReplyError(QNetworkReply::NetworkError error, QDebug debug) case QNetworkReply::UnknownNetworkError: case QNetworkReply::UnknownProxyError: case QNetworkReply::UnknownContentError: - case QNetworkReply::ProtocolFailure: { + case QNetworkReply::ProtocolFailure: { // retry with increasing delays const int MAX_ATTEMPTS = 8; const int BASE_DELAY_MS = 1000; if (++_attempts < MAX_ATTEMPTS) { QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(attemptRequest())); - debug << "-- retrying..."; + qCWarning(networking) << "error downloading url =" << _url.toDisplayString() << ", error =" << error << ", retrying (" << _attempts << "/" << MAX_ATTEMPTS << ")"; return; } // fall through to final failure - } + } default: + qCCritical(networking) << "error downloading, url =" << _url.toDisplayString() << ", error =" << error; + emit failed(error); finishedLoading(false); break; } } void Resource::handleReplyFinished() { - qCDebug(networking) << "Got finished without download progress/error?" << _url; - handleDownloadProgress(0, 0); + + bool fromCache = _reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(); + qCDebug(networking) << "success downloading url =" << _url.toDisplayString() << (fromCache ? "from cache" : ""); + + _reply->disconnect(this); + _replyTimer->disconnect(this); + QNetworkReply* reply = _reply; + _reply = nullptr; + _replyTimer->deleteLater(); + _replyTimer = nullptr; + ResourceCache::requestCompleted(this); + + finishedLoading(true); + emit loaded(*reply); + downloadFinished(reply); +} + + +void Resource::downloadFinished(QNetworkReply* reply) { + ; } uint qHash(const QPointer& value, uint seed) { diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 93ddfe77be..9a88c434e1 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -150,7 +150,7 @@ public: float getLoadPriority(); /// Checks whether the resource has loaded. - bool isLoaded() const { return _loaded; } + virtual bool isLoaded() const { return _loaded; } /// For loading resources, returns the number of bytes received. qint64 getBytesReceived() const { return _bytesReceived; } @@ -174,21 +174,22 @@ public: signals: /// Fired when the resource has been loaded. - void loaded(); + void loaded(QNetworkReply& request); + + /// Fired when resource failed to load. + void failed(QNetworkReply::NetworkError error); + + /// Fired when resource is refreshed. void onRefresh(); protected slots: void attemptRequest(); - - /// Refreshes the resource if the last modified date on the network - /// is greater than the last modified date in the cache. - void maybeRefresh(); protected: virtual void init(); /// Called when the download has finished. The recipient should delete the reply when done with it. - virtual void downloadFinished(QNetworkReply* reply) = 0; + virtual void downloadFinished(QNetworkReply* reply); /// Should be called by subclasses when all the loading that will be done has been done. Q_INVOKABLE void finishedLoading(bool success); @@ -216,7 +217,7 @@ private: void makeRequest(); - void handleReplyError(QNetworkReply::NetworkError error, QDebug debug); + void handleReplyErrorInternal(QNetworkReply::NetworkError error); friend class ResourceCache; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 31b030f75a..f48ceb9b62 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -14,7 +14,6 @@ #include #include -#include #include #include @@ -50,6 +49,13 @@ GeometryCache::~GeometryCache() { #endif //def WANT_DEBUG } +QSharedPointer GeometryCache::createResource(const QUrl& url, const QSharedPointer& fallback, + bool delayLoad, const void* extra) { + // NetworkGeometry is no longer a subclass of Resource, but requires this method because, it is pure virtual. + assert(false); + return QSharedPointer(); +} + const int NUM_VERTICES_PER_TRIANGLE = 3; const int NUM_TRIANGLES_PER_QUAD = 2; const int NUM_VERTICES_PER_TRIANGULATED_QUAD = NUM_VERTICES_PER_TRIANGLE * NUM_TRIANGLES_PER_QUAD; @@ -1643,19 +1649,6 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm batch.draw(gpu::LINES, 2, 0); } - -QSharedPointer GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad) { - return getResource(url, fallback, delayLoad, NULL).staticCast(); -} - -QSharedPointer GeometryCache::createResource(const QUrl& url, const QSharedPointer& fallback, - bool delayLoad, const void* extra) { - QSharedPointer geometry(new NetworkGeometry(url, fallback.staticCast(), delayLoad), - &Resource::allReferencesCleared); - geometry->setLODParent(geometry); - return geometry.staticCast(); -} - void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { if (!_standardDrawPipeline) { auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert))); @@ -1685,33 +1678,82 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { } } -const float NetworkGeometry::NO_HYSTERESIS = -1.0f; +GeometryReader::GeometryReader(const QUrl& url, QNetworkReply* reply, const QVariantHash& mapping) : + _url(url), + _reply(reply), + _mapping(mapping) { +} -NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, - const QVariantHash& mapping, const QUrl& textureBase) : - Resource(url, delayLoad), - _mapping(mapping), - _textureBase(textureBase.isValid() ? textureBase : url), - _fallback(fallback) -{ - - if (url.isEmpty()) { - // make the minimal amount of dummy geometry to satisfy Model - FBXJoint joint = { false, QVector(), -1, 0.0f, 0.0f, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), - glm::quat(), glm::mat4(), glm::mat4(), glm::vec3(), glm::vec3(), glm::quat(), glm::quat(), - glm::mat4(), QString(""), false}; - _geometry.joints.append(joint); - _geometry.leftEyeJointIndex = -1; - _geometry.rightEyeJointIndex = -1; - _geometry.neckJointIndex = -1; - _geometry.rootJointIndex = -1; - _geometry.leanJointIndex = -1; - _geometry.headJointIndex = -1; - _geometry.leftHandJointIndex = -1; - _geometry.rightHandJointIndex = -1; +void GeometryReader::run() { + try { + if (!_reply) { + throw QString("Reply is NULL ?!"); + } + QString urlname = _url.path().toLower(); + bool urlValid = true; + urlValid &= !urlname.isEmpty(); + urlValid &= !_url.path().isEmpty(); + urlValid &= _url.path().toLower().endsWith(".fbx") || _url.path().toLower().endsWith(".obj"); + + if (urlValid) { + // Let's read the binaries from the network + FBXGeometry* fbxgeo = nullptr; + if (_url.path().toLower().endsWith(".fbx")) { + const bool grabLightmaps = true; + const float lightmapLevel = 1.0f; + fbxgeo = readFBX(_reply, _mapping, _url.path(), grabLightmaps, lightmapLevel); + } else if (_url.path().toLower().endsWith(".obj")) { + fbxgeo = OBJReader().readOBJ(_reply, _mapping, &_url); + } else { + QString errorStr("usupported format"); + emit onError(NetworkGeometry::ModelParseError, errorStr); + } + emit onSuccess(fbxgeo); + } else { + throw QString("url is invalid"); + } + + } catch (const QString& error) { + qCDebug(renderutils) << "Error reading " << _url << ": " << error; + emit onError(NetworkGeometry::ModelParseError, error); } - - connect(this, &Resource::loaded, this, &NetworkGeometry::replaceTexturesWithPendingChanges); + _reply->deleteLater(); +} + +NetworkGeometry::NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl) : + _url(url), + _mapping(mapping), + _textureBaseUrl(textureBaseUrl) { + + if (delayLoad) { + _state = DelayState; + } else { + attemptRequestInternal(); + } +} + +NetworkGeometry::~NetworkGeometry() { + if (_resource) { + _resource->deleteLater(); + } +} + +void NetworkGeometry::attemptRequest() { + if (_state == DelayState) { + attemptRequestInternal(); + } +} + +void NetworkGeometry::attemptRequestInternal() { + if (_url.path().toLower().endsWith(".fst")) { + requestMapping(_url); + } else { + requestModel(_url); + } +} + +bool NetworkGeometry::isLoaded() const { + return _state == SuccessState; } bool NetworkGeometry::isLoadedWithTextures() const { @@ -1719,12 +1761,12 @@ bool NetworkGeometry::isLoadedWithTextures() const { return false; } if (!_isLoadedWithTextures) { - foreach (const NetworkMesh& mesh, _meshes) { - foreach (const NetworkMeshPart& part, mesh.parts) { - if ((part.diffuseTexture && !part.diffuseTexture->isLoaded()) || - (part.normalTexture && !part.normalTexture->isLoaded()) || - (part.specularTexture && !part.specularTexture->isLoaded()) || - (part.emissiveTexture && !part.emissiveTexture->isLoaded())) { + for (auto&& mesh : _meshes) { + for (auto && part : mesh->_parts) { + if ((part->diffuseTexture && !part->diffuseTexture->isLoaded()) || + (part->normalTexture && !part->normalTexture->isLoaded()) || + (part->specularTexture && !part->specularTexture->isLoaded()) || + (part->emissiveTexture && !part->emissiveTexture->isLoaded())) { return false; } } @@ -1734,183 +1776,38 @@ bool NetworkGeometry::isLoadedWithTextures() const { return true; } -QSharedPointer NetworkGeometry::getLODOrFallback(float distance, float& hysteresis, bool delayLoad) const { - if (_lodParent.data() != this) { - return _lodParent.data()->getLODOrFallback(distance, hysteresis, delayLoad); - } - if (_failedToLoad && _fallback) { - return _fallback; - } - QSharedPointer lod = _lodParent; - float lodDistance = 0.0f; - QMap >::const_iterator it = _lods.upperBound(distance); - if (it != _lods.constBegin()) { - it = it - 1; - lod = it.value(); - lodDistance = it.key(); - } - if (hysteresis != NO_HYSTERESIS && hysteresis != lodDistance) { - // if we previously selected a different distance, make sure we've moved far enough to justify switching - const float HYSTERESIS_PROPORTION = 0.1f; - if (glm::abs(distance - qMax(hysteresis, lodDistance)) / fabsf(hysteresis - lodDistance) < HYSTERESIS_PROPORTION) { - lod = _lodParent; - lodDistance = 0.0f; - it = _lods.upperBound(hysteresis); - if (it != _lods.constBegin()) { - it = it - 1; - lod = it.value(); - lodDistance = it.key(); - } - } - } - if (lod && lod->isLoaded()) { - hysteresis = lodDistance; - return lod; - } - // if the ideal LOD isn't loaded, we need to make sure it's started to load, and possibly return the closest loaded one - if (!delayLoad) { - lod->ensureLoading(); - } - float closestDistance = FLT_MAX; - if (isLoaded()) { - lod = _lodParent; - closestDistance = distance; - } - for (it = _lods.constBegin(); it != _lods.constEnd(); it++) { - float distanceToLOD = glm::abs(distance - it.key()); - if (it.value()->isLoaded() && distanceToLOD < closestDistance) { - lod = it.value(); - closestDistance = distanceToLOD; - } - } - hysteresis = NO_HYSTERESIS; - return lod; -} - -uint qHash(const QWeakPointer& animation, uint seed = 0) { - return qHash(animation.data(), seed); -} - -QVector NetworkGeometry::getJointMappings(const AnimationPointer& animation) { - QVector mappings = _jointMappings.value(animation); - if (mappings.isEmpty() && isLoaded() && animation && animation->isLoaded()) { - const FBXGeometry& animationGeometry = animation->getGeometry(); - for (int i = 0; i < animationGeometry.joints.size(); i++) { - mappings.append(_geometry.jointIndices.value(animationGeometry.joints.at(i).name) - 1); - } - _jointMappings.insert(animation, mappings); - } - return mappings; -} - -void NetworkGeometry::setLoadPriority(const QPointer& owner, float priority) { - Resource::setLoadPriority(owner, priority); - - for (int i = 0; i < _meshes.size(); i++) { - NetworkMesh& mesh = _meshes[i]; - for (int j = 0; j < mesh.parts.size(); j++) { - NetworkMeshPart& part = mesh.parts[j]; - if (part.diffuseTexture) { - part.diffuseTexture->setLoadPriority(owner, priority); - } - if (part.normalTexture) { - part.normalTexture->setLoadPriority(owner, priority); - } - if (part.specularTexture) { - part.specularTexture->setLoadPriority(owner, priority); - } - if (part.emissiveTexture) { - part.emissiveTexture->setLoadPriority(owner, priority); - } - } - } -} - -void NetworkGeometry::setLoadPriorities(const QHash, float>& priorities) { - Resource::setLoadPriorities(priorities); - - for (int i = 0; i < _meshes.size(); i++) { - NetworkMesh& mesh = _meshes[i]; - for (int j = 0; j < mesh.parts.size(); j++) { - NetworkMeshPart& part = mesh.parts[j]; - if (part.diffuseTexture) { - part.diffuseTexture->setLoadPriorities(priorities); - } - if (part.normalTexture) { - part.normalTexture->setLoadPriorities(priorities); - } - if (part.specularTexture) { - part.specularTexture->setLoadPriorities(priorities); - } - if (part.emissiveTexture) { - part.emissiveTexture->setLoadPriorities(priorities); - } - } - } -} - -void NetworkGeometry::clearLoadPriority(const QPointer& owner) { - Resource::clearLoadPriority(owner); - - for (int i = 0; i < _meshes.size(); i++) { - NetworkMesh& mesh = _meshes[i]; - for (int j = 0; j < mesh.parts.size(); j++) { - NetworkMeshPart& part = mesh.parts[j]; - if (part.diffuseTexture) { - part.diffuseTexture->clearLoadPriority(owner); - } - if (part.normalTexture) { - part.normalTexture->clearLoadPriority(owner); - } - if (part.specularTexture) { - part.specularTexture->clearLoadPriority(owner); - } - if (part.emissiveTexture) { - part.emissiveTexture->clearLoadPriority(owner); - } - } - } -} - void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& url) { if (_meshes.size() > 0) { auto textureCache = DependencyManager::get(); - for (int i = 0; i < _meshes.size(); i++) { - NetworkMesh& mesh = _meshes[i]; - for (int j = 0; j < mesh.parts.size(); j++) { - NetworkMeshPart& part = mesh.parts[j]; - + for (size_t i = 0; i < _meshes.size(); i++) { + NetworkMesh& mesh = *(_meshes[i].get()); + for (size_t j = 0; j < mesh._parts.size(); j++) { + NetworkMeshPart& part = *(mesh._parts[j].get()); QSharedPointer matchingTexture = QSharedPointer(); if (part.diffuseTextureName == name) { - part.diffuseTexture = textureCache->getTexture(url, DEFAULT_TEXTURE, _geometry.meshes[i].isEye); - part.diffuseTexture->setLoadPriorities(_loadPriorities); + part.diffuseTexture = textureCache->getTexture(url, DEFAULT_TEXTURE, _geometry->meshes[i].isEye); } else if (part.normalTextureName == name) { part.normalTexture = textureCache->getTexture(url); - part.normalTexture->setLoadPriorities(_loadPriorities); } else if (part.specularTextureName == name) { part.specularTexture = textureCache->getTexture(url); - part.specularTexture->setLoadPriorities(_loadPriorities); } else if (part.emissiveTextureName == name) { part.emissiveTexture = textureCache->getTexture(url); - part.emissiveTexture->setLoadPriorities(_loadPriorities); } } } } else { - qCDebug(renderutils) << "Adding a name url pair to pending" << name << url; - // we don't have meshes downloaded yet, so hold this texture as pending - _pendingTextureChanges.insert(name, url); + qCWarning(renderutils) << "Ignoring setTextureWirthNameToURL() geometry not ready." << name << url; } _isLoadedWithTextures = false; } QStringList NetworkGeometry::getTextureNames() const { QStringList result; - for (int i = 0; i < _meshes.size(); i++) { - const NetworkMesh& mesh = _meshes[i]; - for (int j = 0; j < mesh.parts.size(); j++) { - const NetworkMeshPart& part = mesh.parts[j]; - + for (size_t i = 0; i < _meshes.size(); i++) { + const NetworkMesh& mesh = *(_meshes[i].get()); + for (size_t j = 0; j < mesh._parts.size(); j++) { + const NetworkMeshPart& part = *(mesh._parts[j].get()); + if (!part.diffuseTextureName.isEmpty() && part.diffuseTexture) { QString textureURL = part.diffuseTexture->getURL().toString(); result << part.diffuseTextureName + ":" + textureURL; @@ -1935,320 +1832,259 @@ QStringList NetworkGeometry::getTextureNames() const { return result; } -void NetworkGeometry::replaceTexturesWithPendingChanges() { - QHash::Iterator it = _pendingTextureChanges.begin(); - - while (it != _pendingTextureChanges.end()) { - setTextureWithNameToURL(it.key(), it.value()); - it = _pendingTextureChanges.erase(it); +void NetworkGeometry::requestMapping(const QUrl& url) { + _state = RequestMappingState; + if (_resource) { + _resource->deleteLater(); } + _resource = new Resource(url, false); + connect(_resource, SIGNAL(loaded(QNetworkReply&)), SLOT(mappingRequestDone(QNetworkReply&))); + connect(_resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(mappingRequestError(QNetworkReply::NetworkError))); } -/// Reads geometry in a worker thread. -class GeometryReader : public QRunnable { -public: - - GeometryReader(const QWeakPointer& geometry, const QUrl& url, - QNetworkReply* reply, const QVariantHash& mapping); - - virtual void run(); - -private: - - QWeakPointer _geometry; - QUrl _url; - QNetworkReply* _reply; - QVariantHash _mapping; -}; - -GeometryReader::GeometryReader(const QWeakPointer& geometry, const QUrl& url, - QNetworkReply* reply, const QVariantHash& mapping) : - _geometry(geometry), - _url(url), - _reply(reply), - _mapping(mapping) { +void NetworkGeometry::requestModel(const QUrl& url) { + _state = RequestModelState; + if (_resource) { + _resource->deleteLater(); + } + _resource = new Resource(url, false); + connect(_resource, SIGNAL(loaded(QNetworkReply&)), SLOT(modelRequestDone(QNetworkReply&))); + connect(_resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(modelRequestError(QNetworkReply::NetworkError))); } -void GeometryReader::run() { - QSharedPointer geometry = _geometry.toStrongRef(); - if (geometry.isNull()) { - _reply->deleteLater(); - return; - } - try { - if (!_reply) { - throw QString("Reply is NULL ?!"); - } - QString urlname = _url.path().toLower(); - bool urlValid = true; - urlValid &= !urlname.isEmpty(); - urlValid &= !_url.path().isEmpty(); - urlValid &= _url.path().toLower().endsWith(".fbx") - || _url.path().toLower().endsWith(".obj") - || _url.path().toLower().endsWith(".svo"); +void NetworkGeometry::mappingRequestDone(QNetworkReply& reply) { + assert(_state == RequestMappingState); - if (urlValid) { - // Let's read the binaries from the network - FBXGeometry fbxgeo; - if (_url.path().toLower().endsWith(".fbx")) { - bool grabLightmaps = true; - float lightmapLevel = 1.0f; - // HACK: For monday 12/01/2014 we need to kill lighmaps loading in starchamber... - if (_url.path().toLower().endsWith("loungev4_11-18.fbx")) { - grabLightmaps = false; - } else if (_url.path().toLower().endsWith("apt8_reboot.fbx")) { - lightmapLevel = 4.0f; - } else if (_url.path().toLower().endsWith("palaceoforinthilian4.fbx")) { - lightmapLevel = 3.5f; - } - fbxgeo = readFBX(_reply, _mapping, _url.path(), grabLightmaps, lightmapLevel); - } else if (_url.path().toLower().endsWith(".obj")) { - fbxgeo = OBJReader().readOBJ(_reply, _mapping, &_url); + // parse the mapping file + _mapping = FSTReader::readMapping(reply.readAll()); + + QUrl replyUrl = reply.url(); + QString modelUrlStr = _mapping.value("filename").toString(); + if (modelUrlStr.isNull()) { + qCDebug(renderutils) << "Mapping file " << _url << "has no \"filename\" entry"; + emit onFailure(*this, MissingFilenameInMapping); + } else { + // read _textureBase from mapping file, if present + QString texdir = _mapping.value("texdir").toString(); + if (!texdir.isNull()) { + if (!texdir.endsWith('/')) { + texdir += '/'; } - QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo)); - } else { - throw QString("url is invalid"); + _textureBaseUrl = replyUrl.resolved(texdir); } - } catch (const QString& error) { - qCDebug(renderutils) << "Error reading " << _url << ": " << error; - QMetaObject::invokeMethod(geometry.data(), "finishedLoading", Q_ARG(bool, false)); - } - _reply->deleteLater(); -} - -void NetworkGeometry::init() { - _mapping = QVariantHash(); - _geometry = FBXGeometry(); - _meshes.clear(); - _lods.clear(); - _pendingTextureChanges.clear(); - _request.setUrl(_url); - Resource::init(); -} - -void NetworkGeometry::downloadFinished(QNetworkReply* reply) { - QUrl url = reply->url(); - if (url.path().toLower().endsWith(".fst")) { - // it's a mapping file; parse it and get the mesh filename - _mapping = FSTReader::readMapping(reply->readAll()); - reply->deleteLater(); - QString filename = _mapping.value("filename").toString(); - if (filename.isNull()) { - qCDebug(renderutils) << "Mapping file " << url << " has no filename."; - finishedLoading(false); - - } else { - QString texdir = _mapping.value("texdir").toString(); - if (!texdir.isNull()) { - if (!texdir.endsWith('/')) { - texdir += '/'; - } - _textureBase = url.resolved(texdir); - } - QVariantHash lods = _mapping.value("lod").toHash(); - for (QVariantHash::const_iterator it = lods.begin(); it != lods.end(); it++) { - auto geometry = QSharedPointer::create(url.resolved(it.key()), - QSharedPointer(), true, _mapping, _textureBase); - geometry->setSelf(geometry.staticCast()); - geometry->setLODParent(_lodParent); - _lods.insert(it.value().toFloat(), geometry); - } - _request.setUrl(url.resolved(filename)); - - // make the request immediately only if we have no LODs to switch between - _startedLoading = false; - if (_lods.isEmpty()) { - attemptRequest(); - } - } - return; - } - - // send the reader off to the thread pool - QThreadPool::globalInstance()->start(new GeometryReader(_self, url, reply, _mapping)); -} - -void NetworkGeometry::reinsert() { - Resource::reinsert(); - - _lodParent = qWeakPointerCast(_self); - foreach (const QSharedPointer& lod, _lods) { - lod->setLODParent(_lodParent); + QUrl modelUrl = replyUrl.resolved(modelUrlStr); + requestModel(modelUrl); } } -void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { - _geometry = geometry; +void NetworkGeometry::mappingRequestError(QNetworkReply::NetworkError error) { + assert(_state == RequestMappingState); + _state = ErrorState; + emit onFailure(*this, MappingRequestError); +} +void NetworkGeometry::modelRequestDone(QNetworkReply& reply) { + assert(_state == RequestModelState); + + _state = ParsingModelState; + + // asynchronously parse the model file. + GeometryReader* geometryReader = new GeometryReader(reply.url(), &reply, _mapping); + connect(geometryReader, SIGNAL(onSuccess(FBXGeometry*)), SLOT(modelParseSuccess(FBXGeometry*))); + connect(geometryReader, SIGNAL(onError(int, QString)), SLOT(modelParseError(int, QString))); + + QThreadPool::globalInstance()->start(geometryReader); +} + +void NetworkGeometry::modelRequestError(QNetworkReply::NetworkError error) { + assert(_state == RequestModelState); + _state = ErrorState; + emit onFailure(*this, ModelRequestError); +} + +static NetworkMesh* buildNetworkMesh(const FBXMesh& mesh, const QUrl& textureBaseUrl) { auto textureCache = DependencyManager::get(); - - foreach (const FBXMesh& mesh, _geometry.meshes) { - NetworkMesh networkMesh; - - int totalIndices = 0; - bool checkForTexcoordLightmap = false; - foreach (const FBXMeshPart& part, mesh.parts) { - NetworkMeshPart networkPart; - if (!part.diffuseTexture.filename.isEmpty()) { - networkPart.diffuseTexture = textureCache->getTexture( - _textureBase.resolved(QUrl(part.diffuseTexture.filename)), DEFAULT_TEXTURE, - mesh.isEye, part.diffuseTexture.content); - networkPart.diffuseTextureName = part.diffuseTexture.name; - networkPart.diffuseTexture->setLoadPriorities(_loadPriorities); - } - if (!part.normalTexture.filename.isEmpty()) { - networkPart.normalTexture = textureCache->getTexture( - _textureBase.resolved(QUrl(part.normalTexture.filename)), NORMAL_TEXTURE, - false, part.normalTexture.content); - networkPart.normalTextureName = part.normalTexture.name; - networkPart.normalTexture->setLoadPriorities(_loadPriorities); - } - if (!part.specularTexture.filename.isEmpty()) { - networkPart.specularTexture = textureCache->getTexture( - _textureBase.resolved(QUrl(part.specularTexture.filename)), SPECULAR_TEXTURE, - false, part.specularTexture.content); - networkPart.specularTextureName = part.specularTexture.name; - networkPart.specularTexture->setLoadPriorities(_loadPriorities); - } - if (!part.emissiveTexture.filename.isEmpty()) { - networkPart.emissiveTexture = textureCache->getTexture( - _textureBase.resolved(QUrl(part.emissiveTexture.filename)), EMISSIVE_TEXTURE, - false, part.emissiveTexture.content); - networkPart.emissiveTextureName = part.emissiveTexture.name; - networkPart.emissiveTexture->setLoadPriorities(_loadPriorities); - checkForTexcoordLightmap = true; - } - networkMesh.parts.append(networkPart); - - totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); + NetworkMesh* networkMesh = new NetworkMesh(); + + int totalIndices = 0; + bool checkForTexcoordLightmap = false; + + // process network parts + foreach (const FBXMeshPart& part, mesh.parts) { + NetworkMeshPart* networkPart = new NetworkMeshPart(); + + if (!part.diffuseTexture.filename.isEmpty()) { + networkPart->diffuseTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(part.diffuseTexture.filename)), DEFAULT_TEXTURE, + mesh.isEye, part.diffuseTexture.content); + networkPart->diffuseTextureName = part.diffuseTexture.name; } - - { - networkMesh._indexBuffer = std::make_shared(); - networkMesh._indexBuffer->resize(totalIndices * sizeof(int)); - int offset = 0; - foreach(const FBXMeshPart& part, mesh.parts) { - networkMesh._indexBuffer->setSubData(offset, part.quadIndices.size() * sizeof(int), - (gpu::Byte*) part.quadIndices.constData()); - offset += part.quadIndices.size() * sizeof(int); - networkMesh._indexBuffer->setSubData(offset, part.triangleIndices.size() * sizeof(int), - (gpu::Byte*) part.triangleIndices.constData()); - offset += part.triangleIndices.size() * sizeof(int); - } + if (!part.normalTexture.filename.isEmpty()) { + networkPart->normalTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(part.normalTexture.filename)), NORMAL_TEXTURE, + false, part.normalTexture.content); + networkPart->normalTextureName = part.normalTexture.name; } - - { - networkMesh._vertexBuffer = std::make_shared(); - // if we don't need to do any blending, the positions/normals can be static - if (mesh.blendshapes.isEmpty()) { - int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); - int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); - int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); - int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); - int texCoords1Offset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); - int clusterIndicesOffset = texCoords1Offset + mesh.texCoords1.size() * sizeof(glm::vec2); - int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); - - networkMesh._vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); - - networkMesh._vertexBuffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); - networkMesh._vertexBuffer->setSubData(normalsOffset, mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); - networkMesh._vertexBuffer->setSubData(tangentsOffset, - mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); - networkMesh._vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); - networkMesh._vertexBuffer->setSubData(texCoordsOffset, - mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); - networkMesh._vertexBuffer->setSubData(texCoords1Offset, - mesh.texCoords1.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords1.constData()); - networkMesh._vertexBuffer->setSubData(clusterIndicesOffset, - mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); - networkMesh._vertexBuffer->setSubData(clusterWeightsOffset, - mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); - - // otherwise, at least the cluster indices/weights can be static - networkMesh._vertexStream = std::make_shared(); - networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, 0, sizeof(glm::vec3)); - if (mesh.normals.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, normalsOffset, sizeof(glm::vec3)); - if (mesh.tangents.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, tangentsOffset, sizeof(glm::vec3)); - if (mesh.colors.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, colorsOffset, sizeof(glm::vec3)); - if (mesh.texCoords.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); - if (mesh.texCoords1.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoords1Offset, sizeof(glm::vec2)); - if (mesh.clusterIndices.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); - if (mesh.clusterWeights.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); - - int channelNum = 0; - networkMesh._vertexFormat = std::make_shared(); - networkMesh._vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - if (mesh.normals.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.tangents.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.colors.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); - if (mesh.texCoords.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - if (mesh.texCoords1.size()) { - networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - } else if (checkForTexcoordLightmap && mesh.texCoords.size()) { - // need lightmap texcoord UV but doesn't have uv#1 so just reuse the same channel - networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum - 1, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - } - if (mesh.clusterIndices.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); - if (mesh.clusterWeights.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); - } - else { - int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); - int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); - int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); - int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); - - networkMesh._vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); - networkMesh._vertexBuffer->setSubData(0, mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); - networkMesh._vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); - networkMesh._vertexBuffer->setSubData(texCoordsOffset, - mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); - networkMesh._vertexBuffer->setSubData(clusterIndicesOffset, - mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); - networkMesh._vertexBuffer->setSubData(clusterWeightsOffset, - mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); - - networkMesh._vertexStream = std::make_shared(); - if (mesh.tangents.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, 0, sizeof(glm::vec3)); - if (mesh.colors.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, colorsOffset, sizeof(glm::vec3)); - if (mesh.texCoords.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); - if (mesh.clusterIndices.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); - if (mesh.clusterWeights.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); - - int channelNum = 0; - networkMesh._vertexFormat = std::make_shared(); - networkMesh._vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.normals.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.tangents.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.colors.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); - if (mesh.texCoords.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - if (mesh.clusterIndices.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); - if (mesh.clusterWeights.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); - - } + if (!part.specularTexture.filename.isEmpty()) { + networkPart->specularTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(part.specularTexture.filename)), SPECULAR_TEXTURE, + false, part.specularTexture.content); + networkPart->specularTextureName = part.specularTexture.name; } - - _meshes.append(networkMesh); + if (!part.emissiveTexture.filename.isEmpty()) { + networkPart->emissiveTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(part.emissiveTexture.filename)), EMISSIVE_TEXTURE, + false, part.emissiveTexture.content); + networkPart->emissiveTextureName = part.emissiveTexture.name; + checkForTexcoordLightmap = true; + } + networkMesh->_parts.emplace_back(networkPart); + totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); } - - finishedLoading(true); + + // initialize index buffer + { + networkMesh->_indexBuffer = std::make_shared(); + networkMesh->_indexBuffer->resize(totalIndices * sizeof(int)); + int offset = 0; + foreach(const FBXMeshPart& part, mesh.parts) { + networkMesh->_indexBuffer->setSubData(offset, part.quadIndices.size() * sizeof(int), + (gpu::Byte*) part.quadIndices.constData()); + offset += part.quadIndices.size() * sizeof(int); + networkMesh->_indexBuffer->setSubData(offset, part.triangleIndices.size() * sizeof(int), + (gpu::Byte*) part.triangleIndices.constData()); + offset += part.triangleIndices.size() * sizeof(int); + } + } + + // initialize vertex buffer + { + networkMesh->_vertexBuffer = std::make_shared(); + // if we don't need to do any blending, the positions/normals can be static + if (mesh.blendshapes.isEmpty()) { + int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); + int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); + int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); + int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); + int texCoords1Offset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); + int clusterIndicesOffset = texCoords1Offset + mesh.texCoords1.size() * sizeof(glm::vec2); + int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); + + networkMesh->_vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); + + networkMesh->_vertexBuffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); + networkMesh->_vertexBuffer->setSubData(normalsOffset, mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); + networkMesh->_vertexBuffer->setSubData(tangentsOffset, + mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); + networkMesh->_vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); + networkMesh->_vertexBuffer->setSubData(texCoordsOffset, + mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); + networkMesh->_vertexBuffer->setSubData(texCoords1Offset, + mesh.texCoords1.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords1.constData()); + networkMesh->_vertexBuffer->setSubData(clusterIndicesOffset, + mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); + networkMesh->_vertexBuffer->setSubData(clusterWeightsOffset, + mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); + + // otherwise, at least the cluster indices/weights can be static + networkMesh->_vertexStream = std::make_shared(); + networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, 0, sizeof(glm::vec3)); + if (mesh.normals.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, normalsOffset, sizeof(glm::vec3)); + if (mesh.tangents.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, tangentsOffset, sizeof(glm::vec3)); + if (mesh.colors.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, colorsOffset, sizeof(glm::vec3)); + if (mesh.texCoords.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); + if (mesh.texCoords1.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoords1Offset, sizeof(glm::vec2)); + if (mesh.clusterIndices.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); + if (mesh.clusterWeights.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); + + int channelNum = 0; + networkMesh->_vertexFormat = std::make_shared(); + networkMesh->_vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); + if (mesh.normals.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.tangents.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.colors.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); + if (mesh.texCoords.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + if (mesh.texCoords1.size()) { + networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + } else if (checkForTexcoordLightmap && mesh.texCoords.size()) { + // need lightmap texcoord UV but doesn't have uv#1 so just reuse the same channel + networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum - 1, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + } + if (mesh.clusterIndices.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + if (mesh.clusterWeights.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + } + else { + int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); + int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); + int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); + int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); + + networkMesh->_vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); + networkMesh->_vertexBuffer->setSubData(0, mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); + networkMesh->_vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); + networkMesh->_vertexBuffer->setSubData(texCoordsOffset, + mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); + networkMesh->_vertexBuffer->setSubData(clusterIndicesOffset, + mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); + networkMesh->_vertexBuffer->setSubData(clusterWeightsOffset, + mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); + + networkMesh->_vertexStream = std::make_shared(); + if (mesh.tangents.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, 0, sizeof(glm::vec3)); + if (mesh.colors.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, colorsOffset, sizeof(glm::vec3)); + if (mesh.texCoords.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); + if (mesh.clusterIndices.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); + if (mesh.clusterWeights.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); + + int channelNum = 0; + networkMesh->_vertexFormat = std::make_shared(); + networkMesh->_vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.normals.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.tangents.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.colors.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); + if (mesh.texCoords.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + if (mesh.clusterIndices.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + if (mesh.clusterWeights.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + } + } + + return networkMesh; +} + +void NetworkGeometry::modelParseSuccess(FBXGeometry* geometry) { + // assume owner ship of geometry pointer + _geometry.reset(geometry); + + foreach(const FBXMesh& mesh, _geometry->meshes) { + _meshes.emplace_back(buildNetworkMesh(mesh, _textureBaseUrl)); + } + + _state = SuccessState; + emit onSuccess(*this, *_geometry.get()); + + delete _resource; + _resource = nullptr; +} + +void NetworkGeometry::modelParseError(int error, QString str) { + _state = ErrorState; + emit onFailure(*this, (NetworkGeometry::Error)error); + + delete _resource; + _resource = nullptr; } bool NetworkMeshPart::isTranslucent() const { return diffuseTexture && diffuseTexture->isTranslucent(); } - bool NetworkMesh::isPartTranslucent(const FBXMesh& fbxMesh, int partIndex) const { assert(partIndex >= 0); - assert(partIndex < parts.size()); - return (parts.at(partIndex).isTranslucent() || fbxMesh.parts.at(partIndex).opacity != 1.0f); + assert((size_t)partIndex < _parts.size()); + return (_parts.at(partIndex)->isTranslucent() || fbxMesh.parts.at(partIndex).opacity != 1.0f); } int NetworkMesh::getTranslucentPartCount(const FBXMesh& fbxMesh) const { int count = 0; - for (int i = 0; i < parts.size(); i++) { + + for (size_t i = 0; i < _parts.size(); i++) { if (isPartTranslucent(fbxMesh, i)) { count++; } diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index f70eae6380..774df1561c 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -13,6 +13,7 @@ #define hifi_GeometryCache_h #include +#include #include #include @@ -129,6 +130,9 @@ public: int allocateID() { return _nextID++; } static const int UNKNOWN_ID; + virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, + bool delayLoad, const void* extra); + void renderSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec3& color, bool solid = true, int id = UNKNOWN_ID) { renderSphere(batch, radius, slices, stacks, glm::vec4(color, 1.0f), solid, id); } @@ -208,11 +212,6 @@ public: /// Set a batch to the simple pipeline, returning the previous pipeline void useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend = false); -protected: - - virtual QSharedPointer createResource(const QUrl& url, - const QSharedPointer& fallback, bool delayLoad, const void* extra); - private: GeometryCache(); virtual ~GeometryCache(); @@ -305,70 +304,104 @@ private: QHash > _networkGeometry; }; -/// Geometry loaded from the network. -class NetworkGeometry : public Resource { +class NetworkGeometry : public QObject { Q_OBJECT public: - - /// A hysteresis value indicating that we have no state memory. - static const float NO_HYSTERESIS; - - NetworkGeometry(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, - const QVariantHash& mapping = QVariantHash(), const QUrl& textureBase = QUrl()); + // mapping is only used if url is a .fbx or .obj file, it is essentially the content of an fst file. + // if delayLoad is true, the url will not be immediately downloaded. + // use the attemptRequest method to initiate the download. + NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl = QUrl()); + ~NetworkGeometry(); - /// Checks whether the geometry and its textures are loaded. + const QUrl& getURL() const { return _url; } + + void attemptRequest(); + + // true when the geometry is loaded (but maybe not it's associated textures) + bool isLoaded() const; + + // true when the requested geometry and its textures are loaded. bool isLoadedWithTextures() const; - /// Returns a pointer to the geometry appropriate for the specified distance. - /// \param hysteresis a hysteresis parameter that prevents rapid model switching - QSharedPointer getLODOrFallback(float distance, float& hysteresis, bool delayLoad = false) const; + // WARNING: only valid when isLoaded returns true. + const FBXGeometry& getFBXGeometry() const { return *_geometry; } + const std::vector>& getMeshes() const { return _meshes; } - const FBXGeometry& getFBXGeometry() const { return _geometry; } - const QVector& getMeshes() const { return _meshes; } - - QVector getJointMappings(const AnimationPointer& animation); - - virtual void setLoadPriority(const QPointer& owner, float priority); - virtual void setLoadPriorities(const QHash, float>& priorities); - virtual void clearLoadPriority(const QPointer& owner); - void setTextureWithNameToURL(const QString& name, const QUrl& url); QStringList getTextureNames() const; - + + enum Error { + MissingFilenameInMapping = 0, + MappingRequestError, + ModelRequestError, + ModelParseError + }; + +signals: + // Fired when everything has downloaded and parsed successfully. + void onSuccess(NetworkGeometry& networkGeometry, FBXGeometry& fbxGeometry); + + // Fired when something went wrong. + void onFailure(NetworkGeometry& networkGeometry, Error error); + +protected slots: + void mappingRequestDone(QNetworkReply& reply); + void mappingRequestError(QNetworkReply::NetworkError error); + + void modelRequestDone(QNetworkReply& reply); + void modelRequestError(QNetworkReply::NetworkError error); + + void modelParseSuccess(FBXGeometry* geometry); + void modelParseError(int error, QString str); + protected: + void attemptRequestInternal(); + void requestMapping(const QUrl& url); + void requestModel(const QUrl& url); - virtual void init(); - virtual void downloadFinished(QNetworkReply* reply); - virtual void reinsert(); - - Q_INVOKABLE void setGeometry(const FBXGeometry& geometry); - -private slots: - void replaceTexturesWithPendingChanges(); -private: - - friend class GeometryCache; - - void setLODParent(const QWeakPointer& lodParent) { _lodParent = lodParent; } - + enum State { DelayState, + RequestMappingState, + RequestModelState, + ParsingModelState, + SuccessState, + ErrorState }; + State _state; + + QUrl _url; QVariantHash _mapping; - QUrl _textureBase; - QSharedPointer _fallback; - - QMap > _lods; - FBXGeometry _geometry; - QVector _meshes; - - QWeakPointer _lodParent; - - QHash, QVector > _jointMappings; - - QHash _pendingTextureChanges; + QUrl _textureBaseUrl; + Resource* _resource = nullptr; + std::unique_ptr _geometry; + std::vector> _meshes; + + // cache for isLoadedWithTextures() mutable bool _isLoadedWithTextures = false; }; +/// Reads geometry in a worker thread. +class GeometryReader : public QObject, public QRunnable { + Q_OBJECT + +public: + + GeometryReader(const QUrl& url, QNetworkReply* reply, const QVariantHash& mapping); + + virtual void run(); + +signals: + void onSuccess(FBXGeometry* geometry); + void onError(int error, QString str); + +private: + + QWeakPointer _geometry; + QUrl _url; + QNetworkReply* _reply; + QVariantHash _mapping; +}; + /// The state associated with a single mesh part. class NetworkMeshPart { public: @@ -394,9 +427,9 @@ public: gpu::BufferStreamPointer _vertexStream; gpu::Stream::FormatPointer _vertexFormat; - - QVector parts; - + + std::vector> _parts; + int getTranslucentPartCount(const FBXMesh& fbxMesh) const; bool isPartTranslucent(const FBXMesh& fbxMesh, int partIndex) const; }; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7452c32ed2..c2d723a323 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -48,6 +48,8 @@ #include "model_lightmap_specular_map_frag.h" #include "model_translucent_frag.h" +#include "RenderUtilsLogging.h" + using namespace std; static int modelPointerTypeId = qRegisterMetaType >(); @@ -66,7 +68,6 @@ Model::Model(RigPointer rig, QObject* parent) : _snappedToRegistrationPoint(false), _showTrueJointTransforms(true), _cauterizeBones(false), - _lodDistance(0.0f), _pupilDilation(0.0f), _url(HTTP_INVALID_COM), _urlAsString(HTTP_INVALID_COM), @@ -234,7 +235,7 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { }; void Model::initJointTransforms() { - if (!_geometry) { + if (!_geometry || !_geometry->isLoaded()) { return; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -368,8 +369,10 @@ void Model::init() { } void Model::reset() { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - _rig->reset(geometry.joints); + if (_geometry && _geometry->isLoaded()) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + _rig->reset(geometry.joints); + } _meshGroupsKnown = false; _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid @@ -378,68 +381,23 @@ void Model::reset() { bool Model::updateGeometry() { PROFILE_RANGE(__FUNCTION__); bool needFullUpdate = false; - bool needToRebuild = false; - if (_nextGeometry) { - _nextGeometry = _nextGeometry->getLODOrFallback(_lodDistance, _nextLODHysteresis); - _nextGeometry->setLoadPriority(this, -_lodDistance); - _nextGeometry->ensureLoading(); - if (_nextGeometry->isLoaded()) { - applyNextGeometry(); - needToRebuild = true; - } - } - if (!_geometry) { + + if (!_geometry || !_geometry->isLoaded()) { // geometry is not ready return false; } - QSharedPointer geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis); - if (_geometry != geometry) { + _needsReload = false; - // NOTE: it is theoretically impossible to reach here after passing through the applyNextGeometry() call above. - // Which means we don't need to worry about calling deleteGeometry() below immediately after creating new geometry. - - const FBXGeometry& newGeometry = geometry->getFBXGeometry(); - QVector newJointStates = createJointStates(newGeometry); - - if (! _rig->jointStatesEmpty()) { - // copy the existing joint states - const FBXGeometry& oldGeometry = _geometry->getFBXGeometry(); - for (QHash::const_iterator it = oldGeometry.jointIndices.constBegin(); - it != oldGeometry.jointIndices.constEnd(); it++) { - int oldIndex = it.value() - 1; - int newIndex = newGeometry.getJointIndex(it.key()); - if (newIndex != -1) { - newJointStates[newIndex].copyState(_rig->getJointState(oldIndex)); - } - } - } - - deleteGeometry(); - _dilatedTextures.clear(); - if (!geometry) { - std::cout << "WARNING: no geometry in Model::updateGeometry\n"; - } - setGeometry(geometry); - - _meshGroupsKnown = false; - _readyWhenAdded = false; // in case any of our users are using scenes - invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid - initJointStates(newJointStates); - needToRebuild = true; - } else if (_rig->jointStatesEmpty()) { + QSharedPointer geometry = _geometry; + if (_rig->jointStatesEmpty()) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); if (fbxGeometry.joints.size() > 0) { initJointStates(createJointStates(fbxGeometry)); needToRebuild = true; } - } else if (!geometry->isLoaded()) { - deleteGeometry(); - _dilatedTextures.clear(); } - _geometry->setLoadPriority(this, -_lodDistance); - _geometry->ensureLoading(); if (needToRebuild) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); @@ -454,7 +412,7 @@ bool Model::updateGeometry() { buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3)); buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), - mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); + mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); } _blendedVertexBuffers.push_back(buffer); } @@ -1069,53 +1027,36 @@ int Model::getLastFreeJointIndex(int jointIndex) const { return (isActive() && jointIndex != -1) ? _geometry->getFBXGeometry().joints.at(jointIndex).freeLineage.last() : -1; } -void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bool delayLoad) { +void Model::setURL(const QUrl& url) { + // don't recreate the geometry if it's the same URL if (_url == url && _geometry && _geometry->getURL() == url) { return; } - _readyWhenAdded = false; // reset out render items. - _needsReload = true; - invalidCalculatedMeshBoxes(); - _url = url; _urlAsString = _url.toString(); + { + render::PendingChanges pendingChanges; + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + removeFromScene(scene, pendingChanges); + scene->enqueuePendingChanges(pendingChanges); + } + + _needsReload = true; + _meshGroupsKnown = false; + invalidCalculatedMeshBoxes(); + deleteGeometry(); + + _geometry.reset(new NetworkGeometry(url, false, QVariantHash())); onInvalidate(); - - // if so instructed, keep the current geometry until the new one is loaded - _nextGeometry = DependencyManager::get()->getGeometry(url, fallback, delayLoad); - _nextLODHysteresis = NetworkGeometry::NO_HYSTERESIS; - if (!retainCurrent || !isActive() || (_nextGeometry && _nextGeometry->isLoaded())) { - applyNextGeometry(); - } } -void Model::geometryRefreshed() { - QObject* sender = QObject::sender(); - - if (sender == _geometry) { - _readyWhenAdded = false; // reset out render items. - _needsReload = true; - invalidCalculatedMeshBoxes(); - - onInvalidate(); - - // if so instructed, keep the current geometry until the new one is loaded - _nextGeometry = DependencyManager::get()->getGeometry(_url); - _nextLODHysteresis = NetworkGeometry::NO_HYSTERESIS; - applyNextGeometry(); - } else { - sender->disconnect(this, SLOT(geometryRefreshed())); - } -} - - const QSharedPointer Model::getCollisionGeometry(bool delayLoad) { if (_collisionGeometry.isNull() && !_collisionUrl.isEmpty()) { - _collisionGeometry = DependencyManager::get()->getGeometry(_collisionUrl, QUrl(), delayLoad); + _collisionGeometry.reset(new NetworkGeometry(_collisionUrl, delayLoad, QVariantHash())); } if (_collisionGeometry && _collisionGeometry->isLoaded()) { @@ -1130,7 +1071,7 @@ void Model::setCollisionModelURL(const QUrl& url) { return; } _collisionUrl = url; - _collisionGeometry = DependencyManager::get()->getGeometry(url, QUrl(), true); + _collisionGeometry.reset(new NetworkGeometry(url, false, QVariantHash())); } bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { @@ -1461,46 +1402,23 @@ void Model::setGeometry(const QSharedPointer& newGeometry) { if (_geometry == newGeometry) { return; } - - if (_geometry) { - _geometry->disconnect(_geometry.data(), &Resource::onRefresh, this, &Model::geometryRefreshed); - } _geometry = newGeometry; - QObject::connect(_geometry.data(), &Resource::onRefresh, this, &Model::geometryRefreshed); -} - -void Model::applyNextGeometry() { - // delete our local geometry and custom textures - deleteGeometry(); - _dilatedTextures.clear(); - _lodHysteresis = _nextLODHysteresis; - - // we retain a reference to the base geometry so that its reference count doesn't fall to zero - setGeometry(_nextGeometry); - - _meshGroupsKnown = false; - _readyWhenAdded = false; // in case any of our users are using scenes - _needsReload = false; // we are loaded now! - invalidCalculatedMeshBoxes(); - _nextGeometry.reset(); } void Model::deleteGeometry() { _blendedVertexBuffers.clear(); _rig->clearJointStates(); _meshStates.clear(); - _rig->deleteAnimations(); - - if (_geometry) { - _geometry->clearLoadPriority(this); - } - _blendedBlendshapeCoefficients.clear(); } AABox Model::getPartBounds(int meshIndex, int partIndex) { + if (!_geometry || !_geometry->isLoaded()) { + return AABox(); + } + if (meshIndex < _meshStates.size()) { const MeshState& state = _meshStates.at(meshIndex); bool isSkinned = state.clusterMatrices.size() > 1; @@ -1531,7 +1449,7 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) { } void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent) { - // PROFILE_RANGE(__FUNCTION__); +// PROFILE_RANGE(__FUNCTION__); PerformanceTimer perfTimer("Model::renderPart"); if (!_readyWhenAdded) { return; // bail asap @@ -1557,14 +1475,14 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const QVector& networkMeshes = _geometry->getMeshes(); + const std::vector>& networkMeshes = _geometry->getMeshes(); // guard against partially loaded meshes - if (meshIndex >= networkMeshes.size() || meshIndex >= geometry.meshes.size() || meshIndex >= _meshStates.size() ) { + if (meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)geometry.meshes.size() || meshIndex >= (int)_meshStates.size() ) { return; } - const NetworkMesh& networkMesh = networkMeshes.at(meshIndex); + const NetworkMesh& networkMesh = *(networkMeshes.at(meshIndex).get()); const FBXMesh& mesh = geometry.meshes.at(meshIndex); const MeshState& state = _meshStates.at(meshIndex); @@ -1614,8 +1532,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown // to false to rebuild out mesh groups. - - if (meshIndex < 0 || meshIndex >= networkMeshes.size() || meshIndex > geometry.meshes.size()) { + if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex > geometry.meshes.size()) { _meshGroupsKnown = false; // regenerate these lists next time around. _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid @@ -1669,11 +1586,11 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } // guard against partially loaded meshes - if (partIndex >= networkMesh.parts.size() || partIndex >= mesh.parts.size()) { + if (partIndex >= (int)networkMesh._parts.size() || partIndex >= mesh.parts.size()) { return; } - const NetworkMeshPart& networkPart = networkMesh.parts.at(partIndex); + const NetworkMeshPart& networkPart = *(networkMesh._parts.at(partIndex).get()); const FBXMeshPart& part = mesh.parts.at(partIndex); model::MaterialPointer material = part._material; @@ -1790,10 +1707,10 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran void Model::segregateMeshGroups() { const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const QVector& networkMeshes = _geometry->getMeshes(); + const std::vector>& networkMeshes = _geometry->getMeshes(); // all of our mesh vectors must match in size - if (networkMeshes.size() != geometry.meshes.size() || + if ((int)networkMeshes.size() != geometry.meshes.size() || geometry.meshes.size() != _meshStates.size()) { qDebug() << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; return; @@ -1803,12 +1720,12 @@ void Model::segregateMeshGroups() { _opaqueRenderItems.clear(); // Run through all of the meshes, and place them into their segregated, but unsorted buckets - for (int i = 0; i < networkMeshes.size(); i++) { - const NetworkMesh& networkMesh = networkMeshes.at(i); + for (int i = 0; i < (int)networkMeshes.size(); i++) { + const NetworkMesh& networkMesh = *(networkMeshes.at(i).get()); const FBXMesh& mesh = geometry.meshes.at(i); const MeshState& state = _meshStates.at(i); - bool translucentMesh = networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size(); + bool translucentMesh = networkMesh.getTranslucentPartCount(mesh) == (int)networkMesh._parts.size(); bool hasTangents = !mesh.tangents.isEmpty(); bool hasSpecular = mesh.hasSpecularTexture(); bool hasLightmap = mesh.hasEmissiveTexture(); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index c9b63b598e..e55bff6aca 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -68,11 +68,7 @@ public: /// Sets the URL of the model to render. - /// \param fallback the URL of a fallback model to render if the requested model fails to load - /// \param retainCurrent if true, keep rendering the current model until the new one is loaded - /// \param delayLoad if true, don't load the model immediately; wait until actually requested - Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl(), - bool retainCurrent = false, bool delayLoad = false); + Q_INVOKABLE void setURL(const QUrl& url); const QUrl& getURL() const { return _url; } const QString& getURLAsString() const { return _urlAsString; } @@ -89,7 +85,7 @@ public: render::Item::Status::Getters& statusGetters); void removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); void renderSetup(RenderArgs* args); - bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().isEmpty()); } + bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().empty()); } bool isVisible() const { return _isVisible; } @@ -141,14 +137,11 @@ public: const QUrl& getCollisionURL() const { return _collisionUrl; } /// Returns a reference to the shared collision geometry. - const QSharedPointer getCollisionGeometry(bool delayLoad = true); + const QSharedPointer getCollisionGeometry(bool delayLoad = false); void setOffset(const glm::vec3& offset); const glm::vec3& getOffset() const { return _offset; } - /// Sets the distance parameter used for LOD computations. - void setLODDistance(float distance) { _lodDistance = distance; } - void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false); bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled @@ -309,20 +302,12 @@ protected: // hook for derived classes to be notified when setUrl invalidates the current model. virtual void onInvalidate() {}; - void geometryRefreshed(); - private: - void applyNextGeometry(); void deleteGeometry(); QVector createJointStates(const FBXGeometry& geometry); void initJointTransforms(); - QSharedPointer _nextGeometry; - float _lodDistance; - float _lodHysteresis; - float _nextLODHysteresis; - QSharedPointer _collisionGeometry; float _pupilDilation; diff --git a/tests/networking/src/ResourceTests.cpp b/tests/networking/src/ResourceTests.cpp new file mode 100644 index 0000000000..f18f676398 --- /dev/null +++ b/tests/networking/src/ResourceTests.cpp @@ -0,0 +1,95 @@ +// +// ResoruceTests.cpp +// +// 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 "ResourceCache.h" +#include "NetworkAccessManager.h" +#include "DependencyManager.h" + +#include "ResourceTests.h" + +QTEST_MAIN(ResourceTests) + +void ResourceTests::initTestCase() { + + auto resourceCacheSharedItems = DependencyManager::set(); + + const qint64 MAXIMUM_CACHE_SIZE = 1024 * 1024 * 1024; // 1GB + + // set up the file cache + //QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + QString cachePath = "./resourceTestCache"; + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkDiskCache* cache = new QNetworkDiskCache(); + cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); + cache->setCacheDirectory(cachePath); + cache->clear(); // clear the cache + networkAccessManager.setCache(cache); +} + +static Resource* resource = nullptr; + + +static bool waitForSignal(QObject *sender, const char *signal, int timeout = 1000) { + QEventLoop loop; + QTimer timer; + timer.setInterval(timeout); + timer.setSingleShot(true); + + loop.connect(sender, signal, SLOT(quit())); + loop.connect(&timer, SIGNAL(timeout()), SLOT(quit())); + timer.start(); + loop.exec(); + + return timer.isActive(); +} + +void ResourceTests::downloadFirst() { + + // download the Mery fst file + QUrl meryUrl = QUrl("http://hifi-public.s3.amazonaws.com/marketplace/contents/e21c0b95-e502-4d15-8c41-ea2fc40f1125/3585ddf674869a67d31d5964f7b52de1.fst"); + resource = new Resource(meryUrl, false); + + const int timeout = 1000; + QEventLoop loop; + QTimer timer; + timer.setInterval(timeout); + timer.setSingleShot(true); + + loop.connect(resource, SIGNAL(loaded(QNetworkReply&)), SLOT(quit())); + loop.connect(resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(quit())); + loop.connect(&timer, SIGNAL(timeout()), SLOT(quit())); + timer.start(); + loop.exec(); + + QVERIFY(resource->isLoaded()); +} + +void ResourceTests::downloadAgain() { + + // download the Mery fst file + QUrl meryUrl = QUrl("http://hifi-public.s3.amazonaws.com/marketplace/contents/e21c0b95-e502-4d15-8c41-ea2fc40f1125/3585ddf674869a67d31d5964f7b52de1.fst"); + resource = new Resource(meryUrl, false); + + const int timeout = 1000; + QEventLoop loop; + QTimer timer; + timer.setInterval(timeout); + timer.setSingleShot(true); + + loop.connect(resource, SIGNAL(loaded(QNetworkReply&)), SLOT(quit())); + loop.connect(resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(quit())); + loop.connect(&timer, SIGNAL(timeout()), SLOT(quit())); + timer.start(); + loop.exec(); + + QVERIFY(resource->isLoaded()); + +} diff --git a/tests/networking/src/ResourceTests.h b/tests/networking/src/ResourceTests.h new file mode 100644 index 0000000000..32fc151982 --- /dev/null +++ b/tests/networking/src/ResourceTests.h @@ -0,0 +1,23 @@ +// +// ResourceTests.h +// +// 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_ResourceTests_h +#define hifi_ResourceTests_h + +#include + +class ResourceTests : public QObject { + Q_OBJECT +private slots: + void initTestCase(); + void downloadFirst(); + void downloadAgain(); +}; + +#endif // hifi_ResourceTests_h diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index 3c02c956e4..f39bea9cf9 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -36,15 +36,16 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { std::cout << "Reading FBX.....\n"; QByteArray fbxContents = fbx.readAll(); - + FBXGeometry* geom; if (filename.toLower().endsWith(".obj")) { - result = OBJReader().readOBJ(fbxContents, QVariantHash()); + geom = OBJReader().readOBJ(fbxContents, QVariantHash()); } else if (filename.toLower().endsWith(".fbx")) { - result = readFBX(fbxContents, QVariantHash(), filename); + geom = readFBX(fbxContents, QVariantHash(), filename); } else { qDebug() << "unknown file extension"; return false; } + result = *geom; reSortFBXGeometryMeshes(result); From 9f501d4d72ccb0274798e0786905bd237ccb5ec0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Aug 2015 16:55:19 -0700 Subject: [PATCH 054/103] first cut at attempting to hide menu in full screen --- interface/src/Application.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a28435e258..0bc4f9b943 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4985,6 +4985,12 @@ void Application::setFullscreen(const QScreen* target) { #endif _window->windowHandle()->setScreen((QScreen*)target); _window->showFullScreen(); + + // also hide the QMainWindow's menuBar + QMenuBar* menuBar = _window->menuBar(); + if (menuBar) { + menuBar->setVisible(false); + } } void Application::unsetFullscreen(const QScreen* avoid) { @@ -5015,6 +5021,12 @@ void Application::unsetFullscreen(const QScreen* avoid) { #else _window->setGeometry(targetGeometry); #endif + + // also show the QMainWindow's menuBar + QMenuBar* menuBar = _window->menuBar(); + if (menuBar) { + menuBar->setVisible(true); + } } From 06b2a88fb6f730c7fd157f9543671c2c39efc373 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Aug 2015 19:11:15 -0700 Subject: [PATCH 055/103] toggle menu near top of window --- interface/src/Application.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0bc4f9b943..a088c42f87 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1744,6 +1744,27 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { return; } +#if 1 //ndef Q_OS_MAC + // If in full screen, and our main windows menu bar is hidden, and we're close to the top of the QMainWindow + // then show the menubar. + if (_window->isFullScreen()) { + QMenuBar* menuBar = _window->menuBar(); + if (menuBar) { + static const int MENU_TOGGLE_AREA = 10; + if (!menuBar->isVisible()) { + if (event->pos().y() <= MENU_TOGGLE_AREA) { + menuBar->setVisible(true); + } + } else { + if (event->pos().y() > MENU_TOGGLE_AREA) { + menuBar->setVisible(false); + } + } + } + } +#endif + + _entities.mouseMoveEvent(event, deviceID); _controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts @@ -4986,11 +5007,13 @@ void Application::setFullscreen(const QScreen* target) { _window->windowHandle()->setScreen((QScreen*)target); _window->showFullScreen(); +#ifndef Q_OS_MAC // also hide the QMainWindow's menuBar QMenuBar* menuBar = _window->menuBar(); if (menuBar) { menuBar->setVisible(false); } +#endif } void Application::unsetFullscreen(const QScreen* avoid) { @@ -5022,11 +5045,13 @@ void Application::unsetFullscreen(const QScreen* avoid) { _window->setGeometry(targetGeometry); #endif +#ifndef Q_OS_MAC // also show the QMainWindow's menuBar QMenuBar* menuBar = _window->menuBar(); if (menuBar) { menuBar->setVisible(true); } +#endif } From bd29fb247497856f688f699780904eef1958c41f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Aug 2015 19:31:51 -0700 Subject: [PATCH 056/103] fix ifdef --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a088c42f87..b9bafbab1a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1744,7 +1744,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { return; } -#if 1 //ndef Q_OS_MAC +#ifndef Q_OS_MAC // If in full screen, and our main windows menu bar is hidden, and we're close to the top of the QMainWindow // then show the menubar. if (_window->isFullScreen()) { From ef4ad3107e1c95f58208e470948070050412b35d Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 20 Aug 2015 22:11:11 -0700 Subject: [PATCH 057/103] First pass, grenade toy. Needs art. --- examples/toys/grenade.js | 197 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 examples/toys/grenade.js diff --git a/examples/toys/grenade.js b/examples/toys/grenade.js new file mode 100644 index 0000000000..545bfbb982 --- /dev/null +++ b/examples/toys/grenade.js @@ -0,0 +1,197 @@ +// +// Grenade.js +// examples +// +// Created by Philip Rosedale on August 20, 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 +// + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + +var grenadeURL = HIFI_PUBLIC_BUCKET + "models/props/grenade/grenade.fbx"; +var fuseSoundURL = HIFI_PUBLIC_BUCKET + "sounds/burningFuse.wav"; +var boomSoundURL = HIFI_PUBLIC_BUCKET + "sounds/explosion.wav"; + +var AudioRotationOffset = Quat.fromPitchYawRollDegrees(0, -90, 0); +var audioOptions = { + volume: 0.5, + loop: true +} + +var injector = null; + +var fuseSound = SoundCache.getSound(fuseSoundURL, audioOptions.isStereo); +var boomSound = SoundCache.getSound(boomSoundURL, audioOptions.isStereo); + +var grenade = null; +var particles = null; +var properties = null; +var originalPosition = null; +var isGrenade = false; +var isBurning = false; + +var animationSettings = JSON.stringify({ + running: true, + loop: true + }); +var explodeAnimationSettings = JSON.stringify({ + running: true, + loop: false + }); + +var GRAVITY = -9.8; +var TIME_TO_EXPLODE = 2500; +var DISTANCE_IN_FRONT_OF_ME = 1.0; + +function makeGrenade() { + var position = Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, + { x: 0, y: 0.0, z: -DISTANCE_IN_FRONT_OF_ME })); + var rotation = Quat.multiply(MyAvatar.orientation, + Quat.fromPitchYawRollDegrees(0, -90, 0)); + grenade = Entities.addEntity({ + type: "Model", + position: position, + rotation: rotation, + dimensions: { x: 0.09, + y: 0.20, + z: 0.09 }, + collisionsWillMove: true, + modelURL: grenadeURL, + shapeType: "box" + }); + + properties = Entities.getEntityProperties(grenade); + audioOptions.position = position; + audioOptions.orientation = rotation; + originalPosition = position; +} + +function update() { + if (!grenade) { + makeGrenade(); + } else { + var newProperties = Entities.getEntityProperties(grenade); + if (!isBurning) { + // If moved, start fuse + var FUSE_START_MOVE = 0.01; + if (Vec3.length(Vec3.subtract(newProperties.position, originalPosition)) > FUSE_START_MOVE) { + isBurning = true; + // Create fuse particles + particles = Entities.addEntity({ + type: "ParticleEffect", + animationSettings: animationSettings, + position: newProperties.position, + textures: 'https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png', + emitRate: 100, + emitStrength: 2.0, + emitDirection: { x: 0.0, y: 1.0, z: 0.0 }, + color: { red: 200, green: 0, blue: 0 }, + lifespan: 10.0, + visible: true, + locked: false + + }); + // Start fuse sound + injector = Audio.playSound(fuseSound, audioOptions); + // Start explosion timer + Script.setTimeout(boom, TIME_TO_EXPLODE); + originalPosition = newProperties.position; + // Add gravity + Entities.editEntity(grenade, { gravity: {x: 0, y: GRAVITY, z: 0 }}); + } + } + + if (newProperties.type === "Model") { + if (newProperties.position != properties.position) { + audioOptions.position = newProperties.position; + } + if (newProperties.orientation != properties.orientation) { + audioOptions.orientation = newProperties.orientation; + } + + properties = newProperties; + // Update sound location if playing + if (injector) { + injector.options = audioOptions; + } + if (particles) { + Entities.editEntity(particles, { position: newProperties.position }); + } + } else { + grenade = null; + Script.update.disconnect(update); + Script.scriptEnding.connect(scriptEnding); + scriptEnding(); + Script.stop(); + } + } +} +function boom() { + injector.stop(); + isBurning = false; + var audioOptions = { + position: properties.position, + volume: 0.75, + loop: false + } + Audio.playSound(boomSound, audioOptions); + Entities.addEntity({ + type: "ParticleEffect", + animationSettings: explodeAnimationSettings, + position: properties.position, + textures: 'https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png', + emitRate: 200, + emitStrength: 3.0, + emitDirection: { x: 0.0, y: 1.0, z: 0.0 }, + color: { red: 255, green: 255, blue: 0 }, + lifespan: 2.0, + visible: true, + lifetime: 2, + locked: false + + }); + var BLAST_RADIUS = 20.0; + var LIFT_DEPTH = 2.0; + var epicenter = properties.position; + epicenter.y -= LIFT_DEPTH; + blowShitUp(epicenter, BLAST_RADIUS); + deleteStuff(); +} + +function blowShitUp(position, radius) { + var stuff = Entities.findEntities(position, radius); + var numMoveable = 0; + var STRENGTH = 3.5; + for (var i = 0; i < stuff.length; i++) { + var properties = Entities.getEntityProperties(stuff[i]); + if (properties.collisionsWillMove) { + var diff = Vec3.subtract(properties.position, position); + var distance = Vec3.length(diff); + var velocity = Vec3.sum(properties.velocity, Vec3.multiply(STRENGTH * 1.0 / distance, Vec3.normalize(diff))); + Entities.editEntity(stuff[i], { velocity: velocity }); + } + } +} + +function scriptEnding() { + deleteStuff(); +} + +function deleteStuff() { + if (grenade != null) { + Entities.deleteEntity(grenade); + grenade = null; + } + if (particles != null) { + Entities.deleteEntity(particles); + particles = null; + } +} + +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); + From 7284e3aa3c42e4b188672531411e5f7d3d512f46 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 20 Aug 2015 22:20:51 -0700 Subject: [PATCH 058/103] added a little bit of spin, too. --- examples/toys/grenade.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/toys/grenade.js b/examples/toys/grenade.js index 545bfbb982..b2dfebb888 100644 --- a/examples/toys/grenade.js +++ b/examples/toys/grenade.js @@ -166,13 +166,17 @@ function blowShitUp(position, radius) { var stuff = Entities.findEntities(position, radius); var numMoveable = 0; var STRENGTH = 3.5; + var SPIN_RATE = 20.0; for (var i = 0; i < stuff.length; i++) { var properties = Entities.getEntityProperties(stuff[i]); if (properties.collisionsWillMove) { var diff = Vec3.subtract(properties.position, position); var distance = Vec3.length(diff); var velocity = Vec3.sum(properties.velocity, Vec3.multiply(STRENGTH * 1.0 / distance, Vec3.normalize(diff))); - Entities.editEntity(stuff[i], { velocity: velocity }); + var angularVelocity = { x: Math.random() * SPIN_RATE, y: Math.random() * SPIN_RATE, z: Math.random() * SPIN_RATE }; + angularVelocity = Vec3.multiply( 1.0 / distance, angularVelocity); + Entities.editEntity(stuff[i], { velocity: velocity, + angularVelocity: angularVelocity }); } } } From cee570d2b26fb787e0f04dc26ed5ec692e949144 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 20 Aug 2015 23:06:05 -0700 Subject: [PATCH 059/103] First pass at basketball, just appears in front of you with some reasonable values --- examples/toys/basketball.js | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 examples/toys/basketball.js diff --git a/examples/toys/basketball.js b/examples/toys/basketball.js new file mode 100644 index 0000000000..d30dce6e72 --- /dev/null +++ b/examples/toys/basketball.js @@ -0,0 +1,82 @@ +// +// basketball.js +// examples +// +// Created by Philip Rosedale on August 20, 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 +// + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + +var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx"; +var collisionSoundURL = HIFI_PUBLIC_BUCKET + "sounds/basketball/basketball.wav"; + + +var basketball = null; +var originalPosition = null; +var hasMoved = false; + +var GRAVITY = -9.8; +var DISTANCE_IN_FRONT_OF_ME = 1.0; +var START_MOVE = 0.01; +var DIAMETER = 0.30; + +function makeBasketball() { + var position = Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, + { x: 0, y: 0.0, z: -DISTANCE_IN_FRONT_OF_ME })); + var rotation = Quat.multiply(MyAvatar.orientation, + Quat.fromPitchYawRollDegrees(0, -90, 0)); + basketball = Entities.addEntity({ + type: "Model", + position: position, + rotation: rotation, + dimensions: { x: DIAMETER, + y: DIAMETER, + z: DIAMETER }, + collisionsWillMove: true, + collisionSoundURL: collisionSoundURL, + modelURL: basketballURL, + restitution: 1.0, + linearDamping: 0.00001, + shapeType: "sphere" + }); + originalPosition = position; +} + +function update() { + if (!basketball) { + makeBasketball(); + } else { + var newProperties = Entities.getEntityProperties(basketball); + var moved = Vec3.length(Vec3.subtract(originalPosition, newProperties.position)); + if (!hasMoved && (moved > START_MOVE)) { + hasMoved = true; + Entities.editEntity(basketball, { gravity: {x: 0, y: GRAVITY, z: 0 }}); + } + var MAX_DISTANCE = 10.0; + var distance = Vec3.length(Vec3.subtract(MyAvatar.position, newProperties.position)); + if (distance > MAX_DISTANCE) { + deleteStuff(); + } + } +} + +function scriptEnding() { + deleteStuff(); +} + +function deleteStuff() { + if (basketball != null) { + Entities.deleteEntity(basketball); + basketball = null; + hasMoved = false; + } +} + +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); + From 76f236adf61f4103298cb3d37beeff688161efbb Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 12 Aug 2015 22:44:16 -0700 Subject: [PATCH 060/103] New stereo rendering implementation --- interface/src/Application.cpp | 111 ++++++---------- interface/src/Stars.cpp | 4 +- interface/src/ui/ApplicationCompositor.cpp | 2 - interface/src/ui/ApplicationOverlay.cpp | 1 - .../src/display-plugins/DisplayPlugin.cpp | 2 +- .../src/display-plugins/DisplayPlugin.h | 4 + .../oculus/OculusBaseDisplayPlugin.cpp | 8 +- .../oculus/OculusBaseDisplayPlugin.h | 4 +- .../stereo/SideBySideStereoDisplayPlugin.cpp | 2 +- .../stereo/StereoDisplayPlugin.cpp | 12 +- .../stereo/StereoDisplayPlugin.h | 3 + libraries/gpu/src/gpu/Batch.cpp | 15 +++ libraries/gpu/src/gpu/Batch.h | 18 ++- libraries/gpu/src/gpu/Context.cpp | 35 +++++ libraries/gpu/src/gpu/Context.h | 44 ++++++- libraries/gpu/src/gpu/Forward.h | 77 +++++++++++ libraries/gpu/src/gpu/GLBackend.cpp | 80 ++++++------ libraries/gpu/src/gpu/GLBackend.h | 27 ++-- libraries/gpu/src/gpu/GLBackendOutput.cpp | 3 + libraries/gpu/src/gpu/GLBackendState.cpp | 6 + libraries/gpu/src/gpu/GLBackendTransform.cpp | 121 +++++++++++------- .../input-plugins/ViveControllerManager.cpp | 1 - .../src/AmbientOcclusionEffect.cpp | 1 - .../src/DeferredLightingEffect.cpp | 5 + .../render-utils/src/RenderDeferredTask.cpp | 65 ++++++---- libraries/render/src/render/DrawStatus.cpp | 5 +- libraries/render/src/render/DrawTask.cpp | 6 +- 27 files changed, 452 insertions(+), 210 deletions(-) create mode 100644 libraries/gpu/src/gpu/Forward.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b9bafbab1a..68b2322394 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1058,29 +1058,18 @@ void Application::paintGL() { // Using the latter will cause the camera to wobble with idle animations, // or with changes from the face tracker renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - - if (!getActiveDisplayPlugin()->isHmd()) { - _myCamera.setPosition(_myAvatar->getDefaultEyePosition()); - _myCamera.setRotation(_myAvatar->getHead()->getCameraOrientation()); - } else { - mat4 camMat = _myAvatar->getSensorToWorldMatrix() * _myAvatar->getHMDSensorMatrix(); - _myCamera.setPosition(extractTranslation(camMat)); - _myCamera.setRotation(glm::quat_cast(camMat)); - } + _myCamera.setPosition(_myAvatar->getDefaultEyePosition()); + _myCamera.setRotation(_myAvatar->getOrientation()); } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { - if (isHMDMode()) { - _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation()); - } else { - _myCamera.setRotation(_myAvatar->getHead()->getOrientation()); - } - if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + - _myCamera.getRotation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale()); - } else { - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + - _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale()); - } + _myCamera.setRotation(_myAvatar->getOrientation()); + // https://www.youtube.com/watch?v=pFriRcIwqNU + vec3 boomStick = glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale(); + quat boomRotation = _myAvatar->getOrientation(); + if (!isHMDMode() && Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { + boomRotation = _myCamera.getRotation(); + } + _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + boomRotation * boomStick); } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + @@ -1089,7 +1078,6 @@ void Application::paintGL() { glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; } - // Update camera position if (!isHMDMode()) { _myCamera.update(1.0f / _fps); @@ -1105,57 +1093,37 @@ void Application::paintGL() { QSize size = DependencyManager::get()->getFrameBufferSize(); renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); - { - PROFILE_RANGE(__FUNCTION__ "/clear"); - doInBatch(&renderArgs, [&](gpu::Batch& batch) { - auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); - batch.setFramebuffer(primaryFbo); - // clear the normal and specular buffers - batch.clearFramebuffer( - gpu::Framebuffer::BUFFER_COLOR0 | - gpu::Framebuffer::BUFFER_COLOR1 | - gpu::Framebuffer::BUFFER_COLOR2 | - gpu::Framebuffer::BUFFER_DEPTH, - vec4(vec3(0), 1), 1.0, 0.0); - }); - } + doInBatch(&renderArgs, [&](gpu::Batch& batch) { + auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); + batch.setFramebuffer(primaryFbo); + // clear the normal and specular buffers + batch.clearFramebuffer( + gpu::Framebuffer::BUFFER_COLOR0 | + gpu::Framebuffer::BUFFER_COLOR1 | + gpu::Framebuffer::BUFFER_COLOR2 | + gpu::Framebuffer::BUFFER_DEPTH, + vec4(vec3(0), 1), 1.0, 0.0); + }); + renderArgs._viewport = gpu::Vec4i(0, 0, size.width(), size.height()); if (displayPlugin->isStereo()) { - PROFILE_RANGE(__FUNCTION__ "/stereoRender"); - QRect currentViewport(QPoint(0, 0), QSize(size.width() / 2, size.height())); - glEnable(GL_SCISSOR_TEST); - for_each_eye([&](Eye eye){ - // Load the view frustum, used by meshes - Camera eyeCamera; - if (qApp->isHMDMode()) { - // Allow the displayPlugin to compose the final eye transform, based on the most up-to-date head motion. - eyeCamera.setTransform(displayPlugin->getModelview(eye, _myAvatar->getSensorToWorldMatrix())); - } else { - eyeCamera.setTransform(displayPlugin->getModelview(eye, _myCamera.getTransform())); - } - eyeCamera.setProjection(displayPlugin->getProjection(eye, _myCamera.getProjection())); - renderArgs._viewport = toGlm(currentViewport); - doInBatch(&renderArgs, [&](gpu::Batch& batch) { - batch.setViewportTransform(renderArgs._viewport); - batch.setStateScissorRect(renderArgs._viewport); - }); - displaySide(&renderArgs, eyeCamera); - }, [&] { - currentViewport.moveLeft(currentViewport.width()); + //_myCamera.setProjection(displayPlugin->getProjection(Mono, _myCamera.getProjection())); + renderArgs._context->enableStereo(true); + mat4 eyeViews[2]; + mat4 eyeProjections[2]; + auto baseProjection = renderArgs._viewFrustum->getProjection(); + // FIXME we don't need to set these every frame, + // only when the display plugin changes + for_each_eye([&](Eye eye) { + eyeViews[eye] = displayPlugin->getModelview(eye, mat4()); + eyeProjections[eye] = displayPlugin->getProjection(eye, baseProjection); }); - glDisable(GL_SCISSOR_TEST); - } else { - PROFILE_RANGE(__FUNCTION__ "/monoRender"); - renderArgs._viewport = gpu::Vec4i(0, 0, size.width(), size.height()); - // Viewport is assigned to the size of the framebuffer - doInBatch(&renderArgs, [&](gpu::Batch& batch) { - batch.setViewportTransform(renderArgs._viewport); - batch.setStateScissorRect(renderArgs._viewport); - }); - displaySide(&renderArgs, _myCamera); + renderArgs._context->setStereoProjections(eyeProjections); + renderArgs._context->setStereoViews(eyeViews); } - - doInBatch(&renderArgs, [](gpu::Batch& batch){ + displaySide(&renderArgs, _myCamera); + renderArgs._context->enableStereo(false); + doInBatch(&renderArgs, [](gpu::Batch& batch) { batch.setFramebuffer(nullptr); }); } @@ -4997,6 +4965,11 @@ mat4 Application::getHMDSensorPose() const { return mat4(); } +// FIXME there is a bug in the fullscreen setting, where leaving +// fullscreen does not restore the window frame, making it difficult +// or impossible to move or size the window. +// Additionally, setting fullscreen isn't hiding the menu on windows +// make it useless for stereoscopic modes. void Application::setFullscreen(const QScreen* target) { if (!_window->isFullScreen()) { _savedGeometry = _window->geometry(); diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp index 42b1a3f2e2..119b9ed1a2 100644 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -191,7 +191,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { auto geometryCache = DependencyManager::get(); auto textureCache = DependencyManager::get(); - gpu::Batch batch; + gpu::Batch& batch = *renderArgs->_batch; batch.setViewTransform(Transform()); batch.setProjectionTransform(renderArgs->_viewFrustum->getProjection()); batch.setModelTransform(Transform().setRotation(glm::inverse(renderArgs->_viewFrustum->getOrientation()) * @@ -219,6 +219,4 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { batch.setInputBuffer(VERTICES_SLOT, posView); batch.setInputBuffer(COLOR_SLOT, colView); batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS); - - renderArgs->_context->render(batch); } diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index abc1e49101..98634d7aed 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -211,7 +211,6 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { //Handle fading and deactivation/activation of UI gpu::Batch batch; - renderArgs->_context->syncCache(); auto geometryCache = DependencyManager::get(); geometryCache->useSimpleDrawPipeline(batch); @@ -279,7 +278,6 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int vec2 canvasSize = qApp->getCanvasSize(); _textureAspectRatio = aspect(canvasSize); - renderArgs->_context->syncCache(); auto geometryCache = DependencyManager::get(); gpu::Batch batch; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 6f18cac127..7254295c2f 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -92,7 +92,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope renderStatsAndLogs(renderArgs); // currently renders nothing - renderArgs->_context->syncCache(); renderArgs->_context->render(batch); renderArgs->_batch = nullptr; // so future users of renderArgs don't try to use our batch diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp index 2316ff70c4..8bfe8c20da 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp @@ -28,7 +28,7 @@ DisplayPluginList getDisplayPlugins() { // Stereo modes // FIXME fix stereo display plugins - //new SideBySideStereoDisplayPlugin(), + new SideBySideStereoDisplayPlugin(), //new InterleavedStereoDisplayPlugin(), // HMDs diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h index 45a5923a1f..da3bc98135 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h @@ -107,6 +107,10 @@ public: static const glm::mat4 pose; return pose; } + virtual glm::vec3 getEyeOffset(Eye eye) const { + static const glm::vec3 offset; return offset; + } + virtual glm::mat4 getHeadPose() const { static const glm::mat4 pose; return pose; } diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp index 7d0fb705df..0490e82106 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp @@ -14,6 +14,9 @@ using namespace Oculus; +OculusBaseDisplayPlugin::OculusBaseDisplayPlugin() : _ipd(OVR_DEFAULT_IPD) { +} + void OculusBaseDisplayPlugin::activate() { glm::uvec2 eyeSizes[2]; ovr_for_each_eye([&](ovrEyeType eye) { @@ -27,9 +30,12 @@ void OculusBaseDisplayPlugin::activate() { ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded); _compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection); - _eyeOffsets[eye] = erd.HmdToEyeViewOffset; + // We handle the eye offsets slightly differently, using an _ipd in the base class + // _eyeOffsets[eye] = erd.HmdToEyeViewOffset; + _eyeOffsets[eye] = { 0, 0, 0 }; eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f)); }); + _ipd = ovrHmd_GetFloat(_hmd, OVR_KEY_IPD, _ipd); _desiredFramebufferSize = uvec2( eyeSizes[0].x + eyeSizes[1].x, std::max(eyeSizes[0].y, eyeSizes[1].y)); diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h index e376ae12ba..401ee6579a 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h @@ -11,6 +11,7 @@ class OculusBaseDisplayPlugin : public MainWindowOpenGLDisplayPlugin { public: + OculusBaseDisplayPlugin(); // Stereo specific methods virtual bool isHmd() const override { return true; } virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; @@ -22,5 +23,6 @@ public: virtual void resetSensors() override; virtual glm::mat4 getEyePose(Eye eye) const override; virtual glm::mat4 getHeadPose() const override; - +protected: + float _ipd; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp index e348143250..72ef5249a8 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp @@ -17,7 +17,7 @@ #include -const QString SideBySideStereoDisplayPlugin::NAME("SBS Stereo Display"); +const QString SideBySideStereoDisplayPlugin::NAME("Debug Stereo Display"); const QString & SideBySideStereoDisplayPlugin::getName() const { return NAME; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index c741967328..3d5f73bfa8 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -52,6 +52,16 @@ glm::mat4 StereoDisplayPlugin::getModelview(Eye eye, const glm::mat4& baseModelv void StereoDisplayPlugin::activate() { WindowOpenGLDisplayPlugin::activate(); - CONTAINER->setFullscreen(qApp->primaryScreen()); + // FIXME there is a bug in the fullscreen setting, see + // Application::setFullscreen + //CONTAINER->setFullscreen(qApp->primaryScreen()); // FIXME Add menu items } + +glm::vec3 StereoDisplayPlugin::getEyeOffset(Eye eye) const { + glm::vec3 result(_ipd / 2.0f, 0, 0); + if (eye == Eye::Right) { + result *= -1.0f; + } + return result; +} diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h index bb1a4fd42c..77b1141aed 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h @@ -20,5 +20,8 @@ public: virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const override; + virtual glm::vec3 getEyeOffset(Eye eye) const override; +protected: + float _ipd{ 0.064f }; }; diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 3ecbc3b2f3..fb6618e953 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -288,3 +288,18 @@ void Batch::resetStages() { ADD_COMMAND(resetStages); } +void Batch::enableStereo(bool enable) { + _enableStereo = enable; +} + +bool Batch::isStereoEnabled() const { + return _enableStereo; +} + +void Batch::enableSkybox(bool enable) { + _enableSkybox = enable; +} + +bool Batch::isSkyboxEnabled() const { + return _enableSkybox; +} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index d48ffef209..ca74032c5e 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -26,7 +26,7 @@ ProfileRange(const char *name); ~ProfileRange(); }; - #define PROFILE_RANGE(name) ProfileRange profileRangeThis(name); +#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name); #else #define PROFILE_RANGE(name) #endif @@ -47,6 +47,19 @@ public: ~Batch(); void clear(); + + // Batches may need to override the context level stereo settings + // if they're performing framebuffer copy operations, like the + // deferred lighting resolution mechanism + void enableStereo(bool enable = true); + bool isStereoEnabled() const; + + // Stereo batches will pre-translate the view matrix, but this isn't + // appropriate for skyboxes or other things intended to be drawn at + // infinite distance, so provide a mechanism to render in stereo + // without the pre-translation of the view. + void enableSkybox(bool enable = true); + bool isSkyboxEnabled() const; // Drawcalls void draw(Primitive primitiveType, uint32 numVertices, uint32 startVertex = 0); @@ -276,6 +289,9 @@ public: FramebufferCaches _framebuffers; QueryCaches _queries; + bool _enableStereo{ true }; + bool _enableSkybox{ false }; + protected: }; diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 239c460c77..561a51b477 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -40,6 +40,18 @@ void Context::render(Batch& batch) { _backend->render(batch); } +void Context::enableStereo(bool enable) { + _backend->enableStereo(enable); +} + +void Context::setStereoProjections(const mat4 eyeProjections[2]) { + _backend->setStereoProjections(eyeProjections); +} + +void Context::setStereoViews(const mat4 eyeViews[2]) { + _backend->setStereoViews(eyeViews); +} + void Context::syncCache() { PROFILE_RANGE(__FUNCTION__); _backend->syncCache(); @@ -49,3 +61,26 @@ void Context::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, cons _backend->downloadFramebuffer(srcFramebuffer, region, destImage); } +const Backend::TransformCamera& Backend::TransformCamera::recomputeDerived() const { + _projectionInverse = glm::inverse(_projection); + _viewInverse = glm::inverse(_view); + + Mat4 viewUntranslated = _view; + viewUntranslated[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f); + _projectionViewUntranslated = _projection * viewUntranslated; + return *this; +} + +Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const StereoState& _stereo) const { + TransformCamera result = *this; + if (!_stereo._skybox) { + result._view = _stereo._eyeViews[eye] * result._view; + } else { + glm::mat4 skyboxView = _stereo._eyeViews[eye]; + skyboxView[3] = vec4(0, 0, 0, 1); + result._view = skyboxView * result._view; + } + result._projection = _stereo._eyeProjections[eye]; + result.recomputeDerived(); + return result; +} diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 7158bd1a6d..9aca83e577 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -14,6 +14,8 @@ #include #include +#include + #include "Batch.h" #include "Resource.h" @@ -25,28 +27,58 @@ class QImage; namespace gpu { +struct StereoState { + bool _enable{ false }; + bool _skybox{ false }; + // 0 for left eye, 1 for right eye + uint8_t _pass{ 0 }; + mat4 _eyeViews[2]; + mat4 _eyeProjections[2]; +}; + class Backend { public: virtual~ Backend() {}; virtual void render(Batch& batch) = 0; + virtual void enableStereo(bool enable) { + _stereo._enable = enable; + } + + void setStereoProjections(const mat4 eyeProjections[2]) { + for (int i = 0; i < 2; ++i) { + _stereo._eyeProjections[i] = eyeProjections[i]; + } + } + + void setStereoViews(const mat4 views[2]) { + for (int i = 0; i < 2; ++i) { + _stereo._eyeViews[i] = views[i]; + } + } + virtual void syncCache() = 0; virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0; + // UBO class... layout MUST match the layout in TransformCamera.slh class TransformObject { public: Mat4 _model; Mat4 _modelInverse; }; + // UBO class... layout MUST match the layout in TransformCamera.slh class TransformCamera { public: Mat4 _view; - Mat4 _viewInverse; - Mat4 _projectionViewUntranslated; + mutable Mat4 _viewInverse; + mutable Mat4 _projectionViewUntranslated; Mat4 _projection; - Mat4 _projectionInverse; + mutable Mat4 _projectionInverse; Vec4 _viewport; // Public value is int but float in the shader to stay in floats for all the transform computations. + + const Backend::TransformCamera& recomputeDerived() const; + TransformCamera getEyeCamera(int eye, const StereoState& stereo) const; }; template< typename T > @@ -113,7 +145,7 @@ public: } protected: - + StereoState _stereo; }; class Context { @@ -136,7 +168,9 @@ public: ~Context(); void render(Batch& batch); - + void enableStereo(bool enable = true); + void setStereoProjections(const mat4 eyeProjections[2]); + void setStereoViews(const mat4 eyeViews[2]); void syncCache(); // Downloading the Framebuffer is a synchronous action that is not efficient. diff --git a/libraries/gpu/src/gpu/Forward.h b/libraries/gpu/src/gpu/Forward.h new file mode 100644 index 0000000000..0fa315ef1c --- /dev/null +++ b/libraries/gpu/src/gpu/Forward.h @@ -0,0 +1,77 @@ +// +// Created by Bradley Austin Davis on 2015/08/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_gpu_Forward_h +#define hifi_gpu_Forward_h + +namespace gpu { + class Batch; + class Backend; + class Context; + typedef std::shared_ptr ContextPointer; + class GPUObject; + + typedef int Stamp; + typedef uint32_t uint32; + typedef int32_t int32; + typedef uint16_t uint16; + typedef int16_t int16; + typedef uint8_t uint8; + typedef int8_t int8; + + typedef uint8 Byte; + typedef uint32 Offset; + typedef std::vector Offsets; + + typedef glm::mat4 Mat4; + typedef glm::mat3 Mat3; + typedef glm::vec4 Vec4; + typedef glm::ivec4 Vec4i; + typedef glm::vec3 Vec3; + typedef glm::vec2 Vec2; + typedef glm::ivec2 Vec2i; + typedef glm::uvec2 Vec2u; + + class Element; + typedef Element Format; + class Swapchain; + typedef std::shared_ptr SwapchainPointer; + class Framebuffer; + typedef std::shared_ptr FramebufferPointer; + class Pipeline; + typedef std::shared_ptr PipelinePointer; + typedef std::vector Pipelines; + class Query; + typedef std::shared_ptr QueryPointer; + typedef std::vector Queries; + class Resource; + class Buffer; + typedef std::shared_ptr BufferPointer; + typedef std::vector Buffers; + class BufferView; + class Shader; + typedef Shader::Pointer ShaderPointer; + typedef std::vector Shaders; + class State; + typedef std::shared_ptr StatePointer; + typedef std::vector States; + class Stream; + class BufferStream; + typedef std::shared_ptr BufferStreamPointer; + class Texture; + class SphericalHarmonics; + typedef std::shared_ptr SHPointer; + class Sampler; + class Texture; + typedef std::shared_ptr TexturePointer; + typedef std::vector Textures; + class TextureView; + typedef std::vector TextureViews; +} + +#endif diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 4afd2ba940..2270c0dce7 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -127,39 +127,29 @@ void GLBackend::renderPassTransfer(Batch& batch) { const Batch::Commands::value_type* command = batch.getCommands().data(); const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data(); - _transform._cameraTransforms.resize(0); - _transform._cameraTransforms.push_back(TransformCamera()); + // Reset the transform buffers + _transform._cameras.resize(0); _transform._cameraOffsets.clear(); - _transform._cameraOffsets.push_back(TransformStageState::Pair(0, 0)); - - _transform._objectTransforms.push_back(TransformObject()); - _transform._objectOffsets.push_back(TransformStageState::Pair(0, 0)); + _transform._objects.resize(0); _transform._objectOffsets.clear(); - _transform._objectTransforms.resize(0); - - _commandIndex = 0; - preUpdateTransform(); - int drawCount = 0; for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) { switch (*command) { case Batch::COMMAND_draw: case Batch::COMMAND_drawIndexed: case Batch::COMMAND_drawInstanced: case Batch::COMMAND_drawIndexedInstanced: - preUpdateTransform(); - ++drawCount; + _transform.preUpdate(_commandIndex, _stereo); break; case Batch::COMMAND_setModelTransform: case Batch::COMMAND_setViewportTransform: case Batch::COMMAND_setViewTransform: - case Batch::COMMAND_setProjectionTransform: - { + case Batch::COMMAND_setProjectionTransform: { CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); + break; } - break; default: break; @@ -167,44 +157,31 @@ void GLBackend::renderPassTransfer(Batch& batch) { command++; offset++; } - - - static QByteArray bufferData; - glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformCameraBuffer); - bufferData.resize(_transform._cameraUboSize * _transform._cameraTransforms.size()); - for (size_t i = 0; i < _transform._cameraTransforms.size(); ++i) { - memcpy(bufferData.data() + (_transform._cameraUboSize * i), &_transform._cameraTransforms[i], sizeof(TransformCamera)); - } - glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformObjectBuffer); - bufferData.resize(_transform._objectUboSize * _transform._objectTransforms.size()); - for (size_t i = 0; i < _transform._objectTransforms.size(); ++i) { - memcpy(bufferData.data() + (_transform._objectUboSize * i), &_transform._objectTransforms[i], sizeof(TransformObject)); - } - glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); - CHECK_GL_ERROR(); + _transform.transfer(); } void GLBackend::renderPassDraw(Batch& batch) { + _transform._objectsItr = _transform._objectOffsets.begin(); + _transform._camerasItr = _transform._cameraOffsets.begin(); const size_t numCommands = batch.getCommands().size(); const Batch::Commands::value_type* command = batch.getCommands().data(); const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data(); for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) { switch (*command) { // Ignore these commands on this pass, taken care of in the transfer pass + // Note we allow COMMAND_setViewportTransform to occur in both passes + // as it both updates the transform object (and thus the uniforms in the + // UBO) as well as executes the actual viewport call case Batch::COMMAND_setModelTransform: - case Batch::COMMAND_setViewportTransform: case Batch::COMMAND_setViewTransform: case Batch::COMMAND_setProjectionTransform: break; - default: - { + default: { CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); + break; } - break; } command++; @@ -213,8 +190,33 @@ void GLBackend::renderPassDraw(Batch& batch) { } void GLBackend::render(Batch& batch) { - renderPassTransfer(batch); - renderPassDraw(batch); + _stereo._skybox = batch.isSkyboxEnabled(); + // Allow the batch to override the rendering stereo settings + // for things like full framebuffer copy operations (deferred lighting passes) + bool savedStereo = _stereo._enable; + if (!batch.isStereoEnabled()) { + _stereo._enable = false; + } + + { + PROFILE_RANGE("Transfer"); + renderPassTransfer(batch); + } + + { + PROFILE_RANGE(_stereo._enable ? "LeftRender" : "Render"); + renderPassDraw(batch); + } + + if (_stereo._enable) { + PROFILE_RANGE("RightRender"); + _stereo._pass = 1; + renderPassDraw(batch); + _stereo._pass = 0; + } + + // Restore the saved stereo state for the next batch + _stereo._enable = savedStereo; } bool GLBackend::checkGLError(const char* name) { diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 455240deb8..c97ea4e615 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -310,19 +310,22 @@ protected: void killTransform(); // Synchronize the state cache of this Backend with the actual real state of the GL Context void syncTransformStateCache(); - void updateTransform(); - void preUpdateTransform(); + void updateTransform() const; void resetTransformStage(); - struct TransformStageState { - TransformObject _transformObject; - TransformCamera _transformCamera; - std::vector _objectTransforms; - std::vector _cameraTransforms; + struct TransformStageState { + using TransformObjects = std::vector; + using TransformCameras = std::vector; + + TransformObject _object; + TransformCamera _camera; + TransformObjects _objects; + TransformCameras _cameras; + size_t _cameraUboSize{ 0 }; size_t _objectUboSize{ 0 }; - GLuint _transformObjectBuffer{ 0 }; - GLuint _transformCameraBuffer{ 0 }; + GLuint _objectBuffer{ 0 }; + GLuint _cameraBuffer{ 0 }; Transform _model; Transform _view; Mat4 _projection; @@ -336,6 +339,12 @@ protected: using List = std::list; List _cameraOffsets; List _objectOffsets; + mutable List::const_iterator _objectsItr; + mutable List::const_iterator _camerasItr; + + void preUpdate(size_t commandIndex, const StereoState& stereo); + void update(size_t commandIndex, const StereoState& stereo) const; + void transfer() const; } _transform; int32_t _uboAlignment{ 0 }; diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp index d75d0cf521..33ae1dd0a3 100755 --- a/libraries/gpu/src/gpu/GLBackendOutput.cpp +++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp @@ -198,6 +198,9 @@ void GLBackend::do_setFramebuffer(Batch& batch, uint32 paramOffset) { } void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) { + if (_stereo._enable && !_pipeline._stateCache.scissorEnable) { + qWarning("Clear without scissor in stereo mode"); + } uint32 masks = batch._params[paramOffset + 7]._uint; Vec4 color; diff --git a/libraries/gpu/src/gpu/GLBackendState.cpp b/libraries/gpu/src/gpu/GLBackendState.cpp index c5cc987fd1..9fdcbc0870 100644 --- a/libraries/gpu/src/gpu/GLBackendState.cpp +++ b/libraries/gpu/src/gpu/GLBackendState.cpp @@ -768,6 +768,12 @@ void GLBackend::do_setStateScissorRect(Batch& batch, uint32 paramOffset) { Vec4i rect; memcpy(&rect, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i)); + if (_stereo._enable) { + rect.z /= 2; + if (_stereo._pass) { + rect.x += rect.z; + } + } glScissor(rect.x, rect.y, rect.z, rect.w); (void) CHECK_GL_ERROR(); } diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp index 23b3dba14e..e33a8f8cde 100755 --- a/libraries/gpu/src/gpu/GLBackendTransform.cpp +++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp @@ -33,16 +33,26 @@ void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) { void GLBackend::do_setViewportTransform(Batch& batch, uint32 paramOffset) { memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i)); + ivec4& vp = _transform._viewport; + // Where we assign the GL viewport - glViewport(_transform._viewport.x, _transform._viewport.y, _transform._viewport.z, _transform._viewport.w); + if (_stereo._enable) { + vp.z /= 2; + if (_stereo._pass) { + vp.x += vp.z; + } + int i = 0; + } + + glViewport(vp.x, vp.y, vp.z, vp.w); // The Viewport is tagged invalid because the CameraTransformUBO is not up to date and willl need update on next drawcall _transform._invalidViewport = true; } void GLBackend::initTransform() { - glGenBuffers(1, &_transform._transformObjectBuffer); - glGenBuffers(1, &_transform._transformCameraBuffer); + glGenBuffers(1, &_transform._objectBuffer); + glGenBuffers(1, &_transform._cameraBuffer); size_t cameraSize = sizeof(TransformCamera); while (_transform._cameraUboSize < cameraSize) { _transform._cameraUboSize += _uboAlignment; @@ -54,8 +64,8 @@ void GLBackend::initTransform() { } void GLBackend::killTransform() { - glDeleteBuffers(1, &_transform._transformObjectBuffer); - glDeleteBuffers(1, &_transform._transformCameraBuffer); + glDeleteBuffers(1, &_transform._objectBuffer); + glDeleteBuffers(1, &_transform._cameraBuffer); } void GLBackend::syncTransformStateCache() { @@ -72,73 +82,98 @@ void GLBackend::syncTransformStateCache() { _transform._model.setIdentity(); } -void GLBackend::preUpdateTransform() { +void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const StereoState& stereo) { // Check all the dirty flags and update the state accordingly - if (_transform._invalidViewport) { - _transform._transformCamera._viewport = glm::vec4(_transform._viewport); + if (_invalidViewport) { + _camera._viewport = glm::vec4(_viewport); } - if (_transform._invalidProj) { - _transform._transformCamera._projection = _transform._projection; - _transform._transformCamera._projectionInverse = glm::inverse(_transform._projection); + if (_invalidProj) { + _camera._projection = _projection; } - if (_transform._invalidView) { - _transform._view.getInverseMatrix(_transform._transformCamera._view); - _transform._view.getMatrix(_transform._transformCamera._viewInverse); + if (_invalidView) { + _view.getInverseMatrix(_camera._view); } - if (_transform._invalidModel) { - _transform._model.getMatrix(_transform._transformObject._model); - _transform._model.getInverseMatrix(_transform._transformObject._modelInverse); + if (_invalidModel) { + _model.getMatrix(_object._model); + _model.getInverseMatrix(_object._modelInverse); } - if (_transform._invalidView || _transform._invalidProj) { - Mat4 viewUntranslated = _transform._transformCamera._view; - viewUntranslated[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f); - _transform._transformCamera._projectionViewUntranslated = _transform._transformCamera._projection * viewUntranslated; + if (_invalidView || _invalidProj || _invalidViewport) { + size_t offset = _cameraUboSize * _cameras.size(); + if (stereo._enable) { + _cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset)); + for (int i = 0; i < 2; ++i) { + _cameras.push_back(_camera.getEyeCamera(i, stereo)); + } + } else { + _cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset)); + _cameras.push_back(_camera.recomputeDerived()); + } } - if (_transform._invalidView || _transform._invalidProj || _transform._invalidViewport) { - _transform._cameraOffsets.push_back(TransformStageState::Pair(_commandIndex, _transform._cameraUboSize * _transform._cameraTransforms.size())); - _transform._cameraTransforms.push_back(_transform._transformCamera); - } - - if (_transform._invalidModel) { - _transform._objectOffsets.push_back(TransformStageState::Pair(_commandIndex, _transform._objectUboSize * _transform._objectTransforms.size())); - _transform._objectTransforms.push_back(_transform._transformObject); + if (_invalidModel) { + size_t offset = _objectUboSize * _objects.size(); + _objectOffsets.push_back(TransformStageState::Pair(commandIndex, offset)); + _objects.push_back(_object); } // Flags are clean - _transform._invalidView = _transform._invalidProj = _transform._invalidModel = _transform._invalidViewport = false; + _invalidView = _invalidProj = _invalidModel = _invalidViewport = false; } -void GLBackend::updateTransform() { +void GLBackend::TransformStageState::transfer() const { + static QByteArray bufferData; + glBindBuffer(GL_UNIFORM_BUFFER, _cameraBuffer); + bufferData.resize(_cameraUboSize * _cameras.size()); + for (size_t i = 0; i < _cameras.size(); ++i) { + memcpy(bufferData.data() + (_cameraUboSize * i), &_cameras[i], sizeof(TransformCamera)); + } + glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, _objectBuffer); + bufferData.resize(_objectUboSize * _objects.size()); + for (size_t i = 0; i < _objects.size(); ++i) { + memcpy(bufferData.data() + (_objectUboSize * i), &_objects[i], sizeof(TransformObject)); + } + glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + CHECK_GL_ERROR(); +} + +void GLBackend::TransformStageState::update(size_t commandIndex, const StereoState& stereo) const { int offset = -1; - while (!_transform._objectOffsets.empty() && _commandIndex >= _transform._objectOffsets.front().first) { - offset = _transform._objectOffsets.front().second; - _transform._objectOffsets.pop_front(); + while ((_objectsItr != _objectOffsets.end()) && (commandIndex >= (*_objectsItr).first)) { + offset = (*_objectsItr).second; + ++_objectsItr; } if (offset >= 0) { - glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, - _transform._transformObjectBuffer, - offset, sizeof(Backend::TransformObject)); + glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, + _objectBuffer, offset, sizeof(Backend::TransformObject)); } offset = -1; - while (!_transform._cameraOffsets.empty() && _commandIndex >= _transform._cameraOffsets.front().first) { - offset = _transform._cameraOffsets.front().second; - _transform._cameraOffsets.pop_front(); + while ((_camerasItr != _cameraOffsets.end()) && (commandIndex >= (*_camerasItr).first)) { + offset = (*_camerasItr).second; + ++_camerasItr; } if (offset >= 0) { - glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, - _transform._transformCameraBuffer, - offset, sizeof(Backend::TransformCamera)); + // We include both camera offsets for stereo + if (stereo._enable && stereo._pass) { + offset += _cameraUboSize; + } + glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, + _cameraBuffer, offset, sizeof(Backend::TransformCamera)); } (void)CHECK_GL_ERROR(); } +void GLBackend::updateTransform() const { + _transform.update(_commandIndex, _stereo); +} + void GLBackend::resetTransformStage() { } diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index fc53670fd0..e76983cce9 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -189,7 +189,6 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint renderHand(rightHand, batch, RIGHT_HAND); } - args->_context->syncCache(); args->_context->render(batch); } } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 5f0afd37d1..ecf7c34dfe 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -246,6 +246,5 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons DependencyManager::get()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); // Ready to render - args->_context->syncCache(); args->_context->render((batch)); } diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 6776fa6c79..a785a5d2ec 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -225,6 +225,7 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu void DeferredLightingEffect::prepare(RenderArgs* args) { gpu::Batch batch; + batch.enableStereo(false); batch.setStateScissorRect(args->_viewport); @@ -244,6 +245,9 @@ gpu::FramebufferPointer _copyFBO; void DeferredLightingEffect::render(RenderArgs* args) { gpu::Batch batch; + // Framebuffer copy operations cannot function as multipass stereo operations. + batch.enableStereo(false); + // perform deferred lighting, rendering to free fbo auto framebufferCache = DependencyManager::get(); @@ -555,6 +559,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { void DeferredLightingEffect::copyBack(RenderArgs* args) { gpu::Batch batch; + batch.enableStereo(false); auto framebufferCache = DependencyManager::get(); QSize framebufferSize = framebufferCache->getFrameBufferSize(); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index efef423623..ca3f87f53f 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -35,6 +35,7 @@ void SetupDeferred::run(const SceneContextPointer& sceneContext, const RenderCon auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor(); gpu::Batch batch; + batch.enableStereo(false); batch.setFramebuffer(nullptr); batch.setFramebuffer(primaryFbo); @@ -159,6 +160,8 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend RenderArgs* args = renderContext->args; gpu::Batch batch; + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); args->_batch = &batch; renderContext->_numDrawnOpaqueItems = inItems.size(); @@ -188,6 +191,8 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const RenderArgs* args = renderContext->args; gpu::Batch batch; + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); args->_batch = &batch; renderContext->_numDrawnTransparentItems = inItems.size(); @@ -247,30 +252,42 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon renderContext->_numFeedOverlay3DItems = inItems.size(); renderContext->_numDrawnOverlay3DItems = inItems.size(); - 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); - - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - batch.setPipeline(getOpaquePipeline()); - batch.setResourceTexture(0, args->_whiteTexture); - if (!inItems.empty()) { - batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true); - renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOverlay3DItems); - } + RenderArgs* args = renderContext->args; - args->_context->render((*args->_batch)); - args->_batch = nullptr; - args->_whiteTexture.reset(); + // Clear the framebuffer without stereo + // Needs to be distinct from the other batch because using the clear call + // while stereo is enabled triggers a warning + { + gpu::Batch batch; + batch.enableStereo(false); + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true); + args->_context->render(batch); + } + + // Render the items + { + gpu::Batch batch; + args->_batch = &batch; + args->_whiteTexture = DependencyManager::get()->getWhiteTexture(); + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + + batch.setPipeline(getOpaquePipeline()); + batch.setResourceTexture(0, args->_whiteTexture); + renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOverlay3DItems); + + args->_context->render((*args->_batch)); + args->_batch = nullptr; + args->_whiteTexture.reset(); + } + } } diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 0c1b92b559..28b4344bf1 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -161,8 +161,5 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex batch.draw(gpu::TRIANGLES, 24, 0); } - // Before rendering the batch make sure we re in sync with gl state - args->_context->syncCache(); - renderContext->args->_context->syncCache(); - args->_context->render((batch)); + args->_context->render(batch); } diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index ff2cf6ff41..0754d81bde 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -258,6 +258,9 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo } RenderArgs* args = renderContext->args; gpu::Batch batch; + batch.enableSkybox(true); + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); args->_batch = &batch; glm::mat4 projMat; @@ -271,9 +274,6 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo renderItems(sceneContext, renderContext, inItems); args->_context->render((*args->_batch)); args->_batch = nullptr; - - // Force the context sync - args->_context->syncCache(); } void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) { From 7fb491e48c2507db85391a2ed3d7cf282fa31b7f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 19 Aug 2015 19:09:21 -0700 Subject: [PATCH 061/103] Cleanup plugin interface, break up oculus plugins --- cmake/externals/LibOVR/CMakeLists.txt | 15 ++ interface/src/Application.cpp | 37 ++- .../Basic2DWindowOpenGLDisplayPlugin.cpp | 4 +- .../Basic2DWindowOpenGLDisplayPlugin.h | 4 +- .../src/display-plugins/DisplayPlugin.cpp | 12 +- .../src/display-plugins/DisplayPlugin.h | 15 +- .../MainWindowOpenGLDisplayPlugin.cpp | 9 - .../MainWindowOpenGLDisplayPlugin.h | 13 - .../oculus/OculusBaseDisplayPlugin.cpp | 82 ------ .../oculus/OculusBaseDisplayPlugin.h | 28 --- ...playPlugin.cpp => OculusDisplayPlugin.cpp} | 238 +++++++++++++----- .../oculus/OculusDisplayPlugin.h | 78 ++++++ .../display-plugins/oculus/OculusHelpers.h | 13 +- ...ugin.cpp => OculusLegacyDisplayPlugin.cpp} | 121 ++++++--- .../oculus/OculusLegacyDisplayPlugin.h | 63 +++++ .../oculus/Oculus_0_5_DisplayPlugin.h | 37 --- .../oculus/Oculus_0_6_DisplayPlugin.h | 41 --- .../openvr/OpenVrDisplayPlugin.cpp | 12 +- .../openvr/OpenVrDisplayPlugin.h | 5 +- .../stereo/StereoDisplayPlugin.cpp | 12 +- .../stereo/StereoDisplayPlugin.h | 7 +- 21 files changed, 465 insertions(+), 381 deletions(-) delete mode 100644 libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.cpp delete mode 100644 libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.h delete mode 100644 libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp delete mode 100644 libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h rename libraries/display-plugins/src/display-plugins/oculus/{Oculus_0_6_DisplayPlugin.cpp => OculusDisplayPlugin.cpp} (59%) create mode 100644 libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h rename libraries/display-plugins/src/display-plugins/oculus/{Oculus_0_5_DisplayPlugin.cpp => OculusLegacyDisplayPlugin.cpp} (55%) create mode 100644 libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h delete mode 100644 libraries/display-plugins/src/display-plugins/oculus/Oculus_0_5_DisplayPlugin.h delete mode 100644 libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.h diff --git a/cmake/externals/LibOVR/CMakeLists.txt b/cmake/externals/LibOVR/CMakeLists.txt index b0bf34a594..e03a3af484 100644 --- a/cmake/externals/LibOVR/CMakeLists.txt +++ b/cmake/externals/LibOVR/CMakeLists.txt @@ -5,6 +5,21 @@ set(EXTERNAL_NAME LibOVR) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) +# These are all provided in order to allow easier testing of both +# the legacy display plugin and the new windows only plugin on +# various versions of the SDK, all on windows +# +# 0.5 public +# URL http://static.oculus.com/sdk-downloads/ovr_sdk_win_0.5.0.1.zip +# URL_MD5 d3fc4c02db9be5ff08af4ef4c97b32f9 +# 0.6 public +# URL http://static.oculus.com/sdk-downloads/0.6.0.1/Public/1435190862/ovr_sdk_win_0.6.0.1.zip +# URL_MD5 4b3ef825f9a1d6d3035c9f6820687da9 +# 0.7 alpha +# URL https://s3.amazonaws.com/static.oculus.com/sdk-downloads/0.7.0.0/Public/Alpha/ovr_sdk_win_0.7.0.0_RC1.zip +# URL_MD5 a562bb9d117087b2cf9d86653ea70fd8 + + if (WIN32) ExternalProject_Add( diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 68b2322394..374963e9b6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1058,18 +1058,29 @@ void Application::paintGL() { // Using the latter will cause the camera to wobble with idle animations, // or with changes from the face tracker renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - _myCamera.setPosition(_myAvatar->getDefaultEyePosition()); - _myCamera.setRotation(_myAvatar->getOrientation()); - } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { - _myCamera.setRotation(_myAvatar->getOrientation()); - // https://www.youtube.com/watch?v=pFriRcIwqNU - vec3 boomStick = glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale(); - quat boomRotation = _myAvatar->getOrientation(); - if (!isHMDMode() && Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { - boomRotation = _myCamera.getRotation(); - } - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + boomRotation * boomStick); + if (!getActiveDisplayPlugin()->isHmd()) { + _myCamera.setPosition(_myAvatar->getDefaultEyePosition()); + _myCamera.setRotation(_myAvatar->getHead()->getCameraOrientation()); + } else { + mat4 camMat = _myAvatar->getSensorToWorldMatrix() * _myAvatar->getHMDSensorMatrix(); + _myCamera.setPosition(extractTranslation(camMat)); + _myCamera.setRotation(glm::quat_cast(camMat)); + } + } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { + if (isHMDMode()) { + _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation()); + } else { + _myCamera.setRotation(_myAvatar->getHead()->getOrientation()); + } + if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { + _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + + _myCamera.getRotation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale()); + } else { + _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale()); + } + } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + @@ -1115,7 +1126,7 @@ void Application::paintGL() { // FIXME we don't need to set these every frame, // only when the display plugin changes for_each_eye([&](Eye eye) { - eyeViews[eye] = displayPlugin->getModelview(eye, mat4()); + eyeViews[eye] = displayPlugin->getView(eye, mat4()); eyeProjections[eye] = displayPlugin->getProjection(eye, baseProjection); }); renderArgs._context->setStereoProjections(eyeProjections); @@ -4952,7 +4963,7 @@ mat4 Application::getEyePose(int eye) const { mat4 Application::getEyeOffset(int eye) const { if (isHMDMode()) { mat4 identity; - return getActiveDisplayPlugin()->getModelview((Eye)eye, identity); + return getActiveDisplayPlugin()->getView((Eye)eye, identity); } return mat4(); diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 7667fa1a29..14ba868275 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -30,11 +30,11 @@ void Basic2DWindowOpenGLDisplayPlugin::activate() { CONTAINER->unsetFullscreen(); } }, true, false); - MainWindowOpenGLDisplayPlugin::activate(); + WindowOpenGLDisplayPlugin::activate(); } void Basic2DWindowOpenGLDisplayPlugin::deactivate() { - MainWindowOpenGLDisplayPlugin::deactivate(); + WindowOpenGLDisplayPlugin::deactivate(); } int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval(bool isThrottled) const { diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index 477e214f4e..64edfe3600 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -7,10 +7,10 @@ // #pragma once -#include "MainWindowOpenGLDisplayPlugin.h" +#include "WindowOpenGLDisplayPlugin.h" class QScreen; -class Basic2DWindowOpenGLDisplayPlugin : public MainWindowOpenGLDisplayPlugin { +class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin { Q_OBJECT public: diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp index 8bfe8c20da..cd620d85a4 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp @@ -15,8 +15,8 @@ #include "Basic2DWindowOpenGLDisplayPlugin.h" #include "openvr/OpenVrDisplayPlugin.h" -#include "oculus/Oculus_0_5_DisplayPlugin.h" -#include "oculus/Oculus_0_6_DisplayPlugin.h" +#include "oculus/OculusDisplayPlugin.h" +#include "oculus/OculusLegacyDisplayPlugin.h" // TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class DisplayPluginList getDisplayPlugins() { @@ -32,9 +32,13 @@ DisplayPluginList getDisplayPlugins() { //new InterleavedStereoDisplayPlugin(), // HMDs - new Oculus_0_5_DisplayPlugin(), - new Oculus_0_6_DisplayPlugin(), + + // Windows Oculus SDK + new OculusDisplayPlugin(), + // Mac/Linux Oculus SDK (0.5) + new OculusLegacyDisplayPlugin(), #ifdef Q_OS_WIN + // SteamVR SDK new OpenVrDisplayPlugin(), #endif nullptr diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h index da3bc98135..1a4166c0fa 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h @@ -97,20 +97,16 @@ public: return baseProjection; } - virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const { - return glm::inverse(getEyePose(eye)) * baseModelview; + virtual glm::mat4 getView(Eye eye, const glm::mat4& baseView) const { + return glm::inverse(getEyePose(eye)) * baseView; } // HMD specific methods - // TODO move these into another class + // TODO move these into another class? virtual glm::mat4 getEyePose(Eye eye) const { static const glm::mat4 pose; return pose; } - virtual glm::vec3 getEyeOffset(Eye eye) const { - static const glm::vec3 offset; return offset; - } - virtual glm::mat4 getHeadPose() const { static const glm::mat4 pose; return pose; } @@ -119,11 +115,6 @@ public: virtual void resetSensors() {} virtual float devicePixelRatio() { return 1.0; } - //// The window for the surface, used for event interception. May be null. - //virtual QWindow* getWindow() const = 0; - - //virtual void installEventFilter(QObject* filter) {} - //virtual void removeEventFilter(QObject* filter) {} signals: void recommendedFramebufferSizeChanged(const QSize & size); diff --git a/libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.cpp deleted file mode 100644 index 68fe92c943..0000000000 --- a/libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.cpp +++ /dev/null @@ -1,9 +0,0 @@ - -// -// Created by Bradley Austin Davis on 2015/05/29 -// 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 "MainWindowOpenGLDisplayPlugin.h" diff --git a/libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.h deleted file mode 100644 index 5b28ec7c21..0000000000 --- a/libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015/05/29 -// 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 "WindowOpenGLDisplayPlugin.h" - -class MainWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin { -}; diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp deleted file mode 100644 index 0490e82106..0000000000 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015/05/29 -// 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 "OculusBaseDisplayPlugin.h" - -#include - -#include "OculusHelpers.h" - - -using namespace Oculus; - -OculusBaseDisplayPlugin::OculusBaseDisplayPlugin() : _ipd(OVR_DEFAULT_IPD) { -} - -void OculusBaseDisplayPlugin::activate() { - glm::uvec2 eyeSizes[2]; - ovr_for_each_eye([&](ovrEyeType eye) { - _eyeFovs[eye] = _hmd->MaxEyeFov[eye]; - ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovrHmd_GetRenderDesc(_hmd, eye, _eyeFovs[eye]); - ovrMatrix4f ovrPerspectiveProjection = - ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); - _eyeProjections[eye] = toGlm(ovrPerspectiveProjection); - - ovrPerspectiveProjection = - ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded); - _compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection); - - // We handle the eye offsets slightly differently, using an _ipd in the base class - // _eyeOffsets[eye] = erd.HmdToEyeViewOffset; - _eyeOffsets[eye] = { 0, 0, 0 }; - eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f)); - }); - _ipd = ovrHmd_GetFloat(_hmd, OVR_KEY_IPD, _ipd); - _desiredFramebufferSize = uvec2( - eyeSizes[0].x + eyeSizes[1].x, - std::max(eyeSizes[0].y, eyeSizes[1].y)); - - _frameIndex = 0; - - if (!OVR_SUCCESS(ovrHmd_ConfigureTracking(_hmd, - ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { - qFatal("Could not attach to sensor device"); - } - - MainWindowOpenGLDisplayPlugin::activate(); -} - -uvec2 OculusBaseDisplayPlugin::getRecommendedRenderSize() const { - return _desiredFramebufferSize; -} - -void OculusBaseDisplayPlugin::preRender() { - ovrHmd_GetEyePoses(_hmd, _frameIndex, _eyeOffsets, _eyePoses, nullptr); -} - -glm::mat4 OculusBaseDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const { - return _eyeProjections[eye]; -} - -glm::mat4 OculusBaseDisplayPlugin::getModelview(Eye eye, const glm::mat4& baseModelview) const { - return baseModelview * toGlm(_eyePoses[eye]); -} - -void OculusBaseDisplayPlugin::resetSensors() { - ovrHmd_RecenterPose(_hmd); -} - -glm::mat4 OculusBaseDisplayPlugin::getEyePose(Eye eye) const { - return toGlm(_eyePoses[eye]); -} - -// Should NOT be used for rendering as this will mess up timewarp. Use the getModelview() method above for -// any use of head poses for rendering, ensuring you use the correct eye -glm::mat4 OculusBaseDisplayPlugin::getHeadPose() const { - ovrTrackingState state = ovrHmd_GetTrackingState(_hmd, 0.0f); - return toGlm(state.HeadPose.ThePose); -} diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h deleted file mode 100644 index 401ee6579a..0000000000 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015/05/29 -// 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 "../MainWindowOpenGLDisplayPlugin.h" - -class OculusBaseDisplayPlugin : public MainWindowOpenGLDisplayPlugin { -public: - OculusBaseDisplayPlugin(); - // Stereo specific methods - virtual bool isHmd() const override { return true; } - virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; - virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const override; - virtual void activate() override; - virtual void preRender() override; - virtual glm::uvec2 getRecommendedRenderSize() const override; - virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); } - virtual void resetSensors() override; - virtual glm::mat4 getEyePose(Eye eye) const override; - virtual glm::mat4 getHeadPose() const override; -protected: - float _ipd; -}; diff --git a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp similarity index 59% rename from libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.cpp rename to libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp index 6ed8977f47..84d67f038a 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp @@ -5,7 +5,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "Oculus_0_6_DisplayPlugin.h" +#include "OculusDisplayPlugin.h" #include @@ -15,9 +15,7 @@ #include #include #include - -#include - +#include #include #include @@ -27,18 +25,34 @@ #include #include +#include #include "OculusHelpers.h" -using namespace Oculus; -#if (OVR_MAJOR_VERSION == 6) -SwapFboPtr _sceneFbo; -MirrorFboPtr _mirrorFbo; -ovrLayerEyeFov _sceneLayer; +#if (OVR_MAJOR_VERSION == 6) +#define ovr_Create ovrHmd_Create +#define ovr_CreateSwapTextureSetGL ovrHmd_CreateSwapTextureSetGL +#define ovr_CreateMirrorTextureGL ovrHmd_CreateMirrorTextureGL +#define ovr_Destroy ovrHmd_Destroy +#define ovr_DestroySwapTextureSet ovrHmd_DestroySwapTextureSet +#define ovr_DestroyMirrorTexture ovrHmd_DestroyMirrorTexture +#define ovr_GetFloat ovrHmd_GetFloat +#define ovr_GetFovTextureSize ovrHmd_GetFovTextureSize +#define ovr_GetFrameTiming ovrHmd_GetFrameTiming +#define ovr_GetTrackingState ovrHmd_GetTrackingState +#define ovr_GetRenderDesc ovrHmd_GetRenderDesc +#define ovr_RecenterPose ovrHmd_RecenterPose +#define ovr_SubmitFrame ovrHmd_SubmitFrame +#define ovr_ConfigureTracking ovrHmd_ConfigureTracking + +#define ovr_GetHmdDesc(X) *X +#endif + +#if (OVR_MAJOR_VERSION >= 6) // A base class for FBO wrappers that need to use the Oculus C -// API to manage textures via ovrHmd_CreateSwapTextureSetGL, -// ovrHmd_CreateMirrorTextureGL, etc +// API to manage textures via ovr_CreateSwapTextureSetGL, +// ovr_CreateMirrorTextureGL, etc template struct RiftFramebufferWrapper : public FramebufferWrapper { ovrHmd hmd; @@ -73,7 +87,7 @@ struct SwapFramebufferWrapper : public RiftFramebufferWrapper { virtual ~MirrorFramebufferWrapper() { if (color) { - ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color); + ovr_DestroyMirrorTexture(hmd, (ovrTexture*)color); color = nullptr; } } @@ -135,10 +149,10 @@ struct MirrorFramebufferWrapper : public RiftFramebufferWrapper { private: void initColor() override { if (color) { - ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color); + ovr_DestroyMirrorTexture(hmd, (ovrTexture*)color); color = nullptr; } - ovrResult result = ovrHmd_CreateMirrorTextureGL(hmd, GL_RGBA, size.x, size.y, (ovrTexture**)&color); + ovrResult result = ovr_CreateMirrorTextureGL(hmd, GL_RGBA, size.x, size.y, (ovrTexture**)&color); Q_ASSERT(OVR_SUCCESS(result)); } @@ -149,52 +163,128 @@ private: } }; + #endif +const QString OculusDisplayPlugin::NAME("Oculus Rift"); -const QString Oculus_0_6_DisplayPlugin::NAME("Oculus Rift"); +uvec2 OculusDisplayPlugin::getRecommendedRenderSize() const { + return _desiredFramebufferSize; +} -const QString & Oculus_0_6_DisplayPlugin::getName() const { +void OculusDisplayPlugin::preRender() { +#if (OVR_MAJOR_VERSION >= 6) + ovrFrameTiming ftiming = ovr_GetFrameTiming(_hmd, _frameIndex); + _trackingState = ovr_GetTrackingState(_hmd, ftiming.DisplayMidpointSeconds); + ovr_CalcEyePoses(_trackingState.HeadPose.ThePose, _eyeOffsets, _eyePoses); +#endif +} + +glm::mat4 OculusDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const { + return _eyeProjections[eye]; +} + +void OculusDisplayPlugin::resetSensors() { +#if (OVR_MAJOR_VERSION >= 6) + ovr_RecenterPose(_hmd); +#endif +} + +glm::mat4 OculusDisplayPlugin::getEyePose(Eye eye) const { + return toGlm(_eyePoses[eye]); +} + +glm::mat4 OculusDisplayPlugin::getHeadPose() const { + return toGlm(_trackingState.HeadPose.ThePose); +} + +const QString & OculusDisplayPlugin::getName() const { return NAME; } -bool Oculus_0_6_DisplayPlugin::isSupported() const { -#if (OVR_MAJOR_VERSION == 6) - if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { - return false; - } - bool result = false; - if (ovrHmd_Detect() > 0) { - result = true; - } - ovr_Shutdown(); - return result; +bool OculusDisplayPlugin::isSupported() const { +#if (OVR_MAJOR_VERSION >= 6) + return true; #else return false; #endif } +void OculusDisplayPlugin::init() { + if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { + qFatal("Could not init OVR"); + } +} -#if (OVR_MAJOR_VERSION == 6) -ovrLayerEyeFov& getSceneLayer() { +void OculusDisplayPlugin::deinit() { + ovr_Shutdown(); +} + +#if (OVR_MAJOR_VERSION >= 6) +ovrLayerEyeFov& OculusDisplayPlugin::getSceneLayer() { return _sceneLayer; } #endif //static gpu::TexturePointer _texture; -void Oculus_0_6_DisplayPlugin::activate() { -#if (OVR_MAJOR_VERSION == 6) +void OculusDisplayPlugin::activate() { +#if (OVR_MAJOR_VERSION >= 6) if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { Q_ASSERT(false); qFatal("Failed to Initialize SDK"); } - if (!OVR_SUCCESS(ovrHmd_Create(0, &_hmd))) { + +// CONTAINER->getPrimarySurface()->makeCurrent(); +#if (OVR_MAJOR_VERSION == 6) + if (!OVR_SUCCESS(ovr_Create(0, &_hmd))) { +#elif (OVR_MAJOR_VERSION == 7) + if (!OVR_SUCCESS(ovr_Create(&_hmd, &_luid))) { +#endif Q_ASSERT(false); qFatal("Failed to acquire HMD"); } - OculusBaseDisplayPlugin::activate(); + _hmdDesc = ovr_GetHmdDesc(_hmd); + + _ipd = ovr_GetFloat(_hmd, OVR_KEY_IPD, _ipd); + + glm::uvec2 eyeSizes[2]; + ovr_for_each_eye([&](ovrEyeType eye) { + _eyeFovs[eye] = _hmdDesc.DefaultEyeFov[eye]; + ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovr_GetRenderDesc(_hmd, eye, _eyeFovs[eye]); + ovrMatrix4f ovrPerspectiveProjection = + ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); + _eyeProjections[eye] = toGlm(ovrPerspectiveProjection); + + ovrPerspectiveProjection = + ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded); + _compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection); + + _eyeOffsets[eye] = erd.HmdToEyeViewOffset; + eyeSizes[eye] = toGlm(ovr_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f)); + }); + ovrFovPort combined = _eyeFovs[Left]; + combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan); + combined.RightTan = std::max(_eyeFovs[Left].RightTan, _eyeFovs[Right].RightTan); + ovrMatrix4f ovrPerspectiveProjection = + ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); + _eyeProjections[Mono] = toGlm(ovrPerspectiveProjection); + + + + _desiredFramebufferSize = uvec2( + eyeSizes[0].x + eyeSizes[1].x, + std::max(eyeSizes[0].y, eyeSizes[1].y)); + + _frameIndex = 0; + + if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd, + ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { + qFatal("Could not attach to sensor device"); + } + + WindowOpenGLDisplayPlugin::activate(); // Parent class relies on our _hmd intialization, so it must come after that. ovrLayerEyeFov& sceneLayer = getSceneLayer(); @@ -203,7 +293,7 @@ void Oculus_0_6_DisplayPlugin::activate() { sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; ovr_for_each_eye([&](ovrEyeType eye) { ovrFovPort & fov = sceneLayer.Fov[eye] = _eyeRenderDescs[eye].Fov; - ovrSizei & size = sceneLayer.Viewport[eye].Size = ovrHmd_GetFovTextureSize(_hmd, eye, fov, 1.0f); + ovrSizei & size = sceneLayer.Viewport[eye].Size = ovr_GetFovTextureSize(_hmd, eye, fov, 1.0f); sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 }; }); // We're rendering both eyes to the same texture, so only one of the @@ -214,17 +304,16 @@ void Oculus_0_6_DisplayPlugin::activate() { PerformanceTimer::setActive(true); - if (!OVR_SUCCESS(ovrHmd_ConfigureTracking(_hmd, + if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd, ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { qFatal("Could not attach to sensor device"); } #endif } -void Oculus_0_6_DisplayPlugin::customizeContext() { -#if (OVR_MAJOR_VERSION == 6) - OculusBaseDisplayPlugin::customizeContext(); - +void OculusDisplayPlugin::customizeContext() { + WindowOpenGLDisplayPlugin::customizeContext(); +#if (OVR_MAJOR_VERSION >= 6) //_texture = DependencyManager::get()-> // getImageTexture(PathUtils::resourcesPath() + "/images/cube_texture.png"); uvec2 mirrorSize = toGlm(_window->geometry().size()); @@ -236,24 +325,29 @@ void Oculus_0_6_DisplayPlugin::customizeContext() { #endif } -void Oculus_0_6_DisplayPlugin::deactivate() { -#if (OVR_MAJOR_VERSION == 6) +void OculusDisplayPlugin::deactivate() { +#if (OVR_MAJOR_VERSION >= 6) makeCurrent(); _sceneFbo.reset(); _mirrorFbo.reset(); doneCurrent(); PerformanceTimer::setActive(false); - OculusBaseDisplayPlugin::deactivate(); + WindowOpenGLDisplayPlugin::deactivate(); - ovrHmd_Destroy(_hmd); + ovr_Destroy(_hmd); _hmd = nullptr; ovr_Shutdown(); #endif } -void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { -#if (OVR_MAJOR_VERSION == 6) +void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { + static bool inDisplay = false; + if (inDisplay) { + return; + } + inDisplay = true; +#if (OVR_MAJOR_VERSION >= 6) using namespace oglplus; // Need to make sure only the display plugin is responsible for // controlling vsync @@ -263,6 +357,7 @@ void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc auto size = _sceneFbo->size; Context::Viewport(size.x, size.y); glBindTexture(GL_TEXTURE_2D, finalTexture); + GLenum err = glGetError(); drawUnitQuad(); }); @@ -280,17 +375,25 @@ void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc the UI visible in the output window (unlikely). This should be done before _sceneFbo->Increment or we're be using the wrong texture */ - //_sceneFbo->Bound(GL_READ_FRAMEBUFFER, [&] { - // glBlitFramebuffer( - // 0, 0, _sceneFbo->size.x, _sceneFbo->size.y, - // 0, 0, windowSize.x, _mirrorFbo.y, - // GL_COLOR_BUFFER_BIT, GL_NEAREST); - //}); + _sceneFbo->Bound(Framebuffer::Target::Read, [&] { + glBlitFramebuffer( + 0, 0, _sceneFbo->size.x, _sceneFbo->size.y, + 0, 0, windowSize.x, windowSize.y, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + }); { PerformanceTimer("OculusSubmit"); + ovrViewScaleDesc viewScaleDesc; + viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f; + viewScaleDesc.HmdToEyeViewOffset[0] = _eyeOffsets[0]; + viewScaleDesc.HmdToEyeViewOffset[1] = _eyeOffsets[1]; + ovrLayerHeader* layers = &sceneLayer.Header; - ovrResult result = ovrHmd_SubmitFrame(_hmd, _frameIndex, nullptr, &layers, 1); + ovrResult result = ovr_SubmitFrame(_hmd, 0, &viewScaleDesc, &layers, 1); + if (!OVR_SUCCESS(result)) { + qDebug() << result; + } } _sceneFbo->Increment(); @@ -299,21 +402,22 @@ void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc will contain the post-distorted and fully composited scene regardless of how many layers we send. */ - auto mirrorSize = _mirrorFbo->size; - _mirrorFbo->Bound(Framebuffer::Target::Read, [&] { - Context::BlitFramebuffer( - 0, mirrorSize.y, mirrorSize.x, 0, - 0, 0, windowSize.x, windowSize.y, - BufferSelectBit::ColorBuffer, BlitFilter::Nearest); - }); + //auto mirrorSize = _mirrorFbo->size; + //_mirrorFbo->Bound(Framebuffer::Target::Read, [&] { + // Context::BlitFramebuffer( + // 0, mirrorSize.y, mirrorSize.x, 0, + // 0, 0, windowSize.x, windowSize.y, + // BufferSelectBit::ColorBuffer, BlitFilter::Nearest); + //}); ++_frameIndex; #endif + inDisplay = false; } // Pass input events on to the application -bool Oculus_0_6_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { -#if (OVR_MAJOR_VERSION == 6) +bool OculusDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { +#if (OVR_MAJOR_VERSION >= 6) if (event->type() == QEvent::Resize) { QResizeEvent* resizeEvent = static_cast(event); qDebug() << resizeEvent->size().width() << " x " << resizeEvent->size().height(); @@ -323,7 +427,7 @@ bool Oculus_0_6_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { doneCurrent(); } #endif - return OculusBaseDisplayPlugin::eventFilter(receiver, event); + return WindowOpenGLDisplayPlugin::eventFilter(receiver, event); } /* @@ -331,8 +435,8 @@ bool Oculus_0_6_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { However, it should only be done if we can reliably disable v-sync on the mirror surface, otherwise the swapbuffer delay will interefere with the framerate of the headset */ -void Oculus_0_6_DisplayPlugin::finishFrame() { - swapBuffers(); +void OculusDisplayPlugin::finishFrame() { + //swapBuffers(); doneCurrent(); }; diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h new file mode 100644 index 0000000000..75173fd2bd --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h @@ -0,0 +1,78 @@ +// +// Created by Bradley Austin Davis on 2015/05/29 +// 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 "../WindowOpenGLDisplayPlugin.h" + +#include + +#include + +class OffscreenGlCanvas; +struct SwapFramebufferWrapper; +struct MirrorFramebufferWrapper; + +using SwapFboPtr = QSharedPointer; +using MirrorFboPtr = QSharedPointer; + +class OculusDisplayPlugin : public WindowOpenGLDisplayPlugin { +public: + virtual bool isSupported() const override; + virtual const QString & getName() const override; + + virtual void init() override; + virtual void deinit() override; + + virtual void activate() override; + virtual void deactivate() override; + + virtual bool eventFilter(QObject* receiver, QEvent* event) override; + + // Stereo specific methods + virtual bool isHmd() const override { return true; } + virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; + virtual glm::uvec2 getRecommendedRenderSize() const override; + virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); } + virtual void resetSensors() override; + virtual glm::mat4 getEyePose(Eye eye) const override; + virtual glm::mat4 getHeadPose() const override; + +protected: + virtual void preRender() override; + virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; + virtual void customizeContext() override; + // Do not perform swap in finish + virtual void finishFrame() override; + +private: + static const QString NAME; + + ovrHmd _hmd; + float _ipd{ OVR_DEFAULT_IPD }; + unsigned int _frameIndex; + ovrEyeRenderDesc _eyeRenderDescs[2]; + ovrPosef _eyePoses[2]; + ovrVector3f _eyeOffsets[2]; + ovrFovPort _eyeFovs[2]; + mat4 _eyeProjections[3]; + mat4 _compositeEyeProjections[2]; + uvec2 _desiredFramebufferSize; + ovrTrackingState _trackingState; + +#if (OVR_MAJOR_VERSION >= 6) + ovrLayerEyeFov& getSceneLayer(); + ovrHmdDesc _hmdDesc; + SwapFboPtr _sceneFbo; + MirrorFboPtr _mirrorFbo; + ovrLayerEyeFov _sceneLayer; +#endif +#if (OVR_MAJOR_VERSION == 7) + ovrGraphicsLuid _luid; +#endif +}; + diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.h b/libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.h index 31b7c246af..df0a6c5228 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.h +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.h @@ -7,7 +7,7 @@ // #pragma once -#include +#include #include #include #include @@ -79,14 +79,3 @@ inline ovrQuatf ovrFromGlm(const glm::quat & q) { return{ q.x, q.y, q.z, q.w }; } -namespace Oculus { - extern ovrHmd _hmd; - extern unsigned int _frameIndex; - extern ovrEyeRenderDesc _eyeRenderDescs[2]; - extern ovrPosef _eyePoses[2]; - extern ovrVector3f _eyeOffsets[2]; - extern ovrFovPort _eyeFovs[2]; - extern mat4 _eyeProjections[2]; - extern mat4 _compositeEyeProjections[2]; - extern uvec2 _desiredFramebufferSize; -} diff --git a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_5_DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp similarity index 55% rename from libraries/display-plugins/src/display-plugins/oculus/Oculus_0_5_DisplayPlugin.cpp rename to libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp index 86ee3b41f2..2c5af1a52b 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_5_DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp @@ -5,7 +5,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "Oculus_0_5_DisplayPlugin.h" +#include "OculusLegacyDisplayPlugin.h" #include @@ -19,34 +19,66 @@ #include #include - -#include - #include #include +#include #include "plugins/PluginContainer.h" #include "OculusHelpers.h" -using namespace Oculus; -ovrTexture _eyeTextures[2]; -int _hmdScreen{ -1 }; -bool _hswDismissed{ false }; - -DisplayPlugin* makeOculusDisplayPlugin() { - return new Oculus_0_5_DisplayPlugin(); -} - using namespace oglplus; -const QString Oculus_0_5_DisplayPlugin::NAME("Oculus Rift (0.5)"); +const QString OculusLegacyDisplayPlugin::NAME("Oculus Rift (0.5)"); -const QString & Oculus_0_5_DisplayPlugin::getName() const { +const QString & OculusLegacyDisplayPlugin::getName() const { return NAME; } +OculusLegacyDisplayPlugin::OculusLegacyDisplayPlugin() : _ipd(OVR_DEFAULT_IPD) { +} -bool Oculus_0_5_DisplayPlugin::isSupported() const { +uvec2 OculusLegacyDisplayPlugin::getRecommendedRenderSize() const { + return _desiredFramebufferSize; +} + +void OculusLegacyDisplayPlugin::preRender() { +#if (OVR_MAJOR_VERSION == 5) + ovrHmd_GetEyePoses(_hmd, _frameIndex, _eyeOffsets, _eyePoses, &_trackingState); + ovrHmd_BeginFrame(_hmd, _frameIndex); +#endif + WindowOpenGLDisplayPlugin::preRender(); +} + +glm::mat4 OculusLegacyDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const { + return _eyeProjections[eye]; +} + +void OculusLegacyDisplayPlugin::resetSensors() { +#if (OVR_MAJOR_VERSION == 5) + ovrHmd_RecenterPose(_hmd); +#endif +} + +glm::mat4 OculusLegacyDisplayPlugin::getEyePose(Eye eye) const { +#if (OVR_MAJOR_VERSION == 5) + return toGlm(_eyePoses[eye]); +#else + return WindowOpenGLDisplayPlugin::getEyePose(eye); +#endif +} + +// Should NOT be used for rendering as this will mess up timewarp. Use the getModelview() method above for +// any use of head poses for rendering, ensuring you use the correct eye +glm::mat4 OculusLegacyDisplayPlugin::getHeadPose() const { +#if (OVR_MAJOR_VERSION == 5) + return toGlm(_trackingState.HeadPose.ThePose); +#else + return WindowOpenGLDisplayPlugin::getHeadPose(); +#endif +} + + +bool OculusLegacyDisplayPlugin::isSupported() const { #if (OVR_MAJOR_VERSION == 5) if (!ovr_Initialize(nullptr)) { return false; @@ -77,7 +109,7 @@ bool Oculus_0_5_DisplayPlugin::isSupported() const { #endif } -void Oculus_0_5_DisplayPlugin::activate() { +void OculusLegacyDisplayPlugin::activate() { #if (OVR_MAJOR_VERSION == 5) if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { Q_ASSERT(false); @@ -89,7 +121,34 @@ void Oculus_0_5_DisplayPlugin::activate() { qFatal("Failed to acquire HMD"); } - OculusBaseDisplayPlugin::activate(); + glm::uvec2 eyeSizes[2]; + ovr_for_each_eye([&](ovrEyeType eye) { + _eyeFovs[eye] = _hmd->MaxEyeFov[eye]; + ovrEyeRenderDesc erd = _eyeRenderDescs[eye] = ovrHmd_GetRenderDesc(_hmd, eye, _eyeFovs[eye]); + ovrMatrix4f ovrPerspectiveProjection = + ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); + _eyeProjections[eye] = toGlm(ovrPerspectiveProjection); + + ovrPerspectiveProjection = + ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded); + _compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection); + + _eyeOffsets[eye] = erd.HmdToEyeViewOffset; + eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f)); + }); + _desiredFramebufferSize = uvec2( + eyeSizes[0].x + eyeSizes[1].x, + std::max(eyeSizes[0].y, eyeSizes[1].y)); + + _frameIndex = 0; + + if (!OVR_SUCCESS(ovrHmd_ConfigureTracking(_hmd, + ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { + qFatal("Could not attach to sensor device"); + } + + WindowOpenGLDisplayPlugin::activate(); + int screen = getHmdScreen(); if (screen != -1) { CONTAINER->setFullscreen(qApp->screens()[screen]); @@ -118,17 +177,16 @@ void Oculus_0_5_DisplayPlugin::activate() { } }); - ovrEyeRenderDesc _eyeRenderDescs[ovrEye_Count]; ovrBool result = ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs); Q_ASSERT(result); #endif } -void Oculus_0_5_DisplayPlugin::deactivate() { +void OculusLegacyDisplayPlugin::deactivate() { #if (OVR_MAJOR_VERSION == 5) _window->removeEventFilter(this); - OculusBaseDisplayPlugin::deactivate(); + WindowOpenGLDisplayPlugin::deactivate(); QScreen* riftScreen = nullptr; if (_hmdScreen >= 0) { @@ -142,18 +200,11 @@ void Oculus_0_5_DisplayPlugin::deactivate() { #endif } -void Oculus_0_5_DisplayPlugin::preRender() { -#if (OVR_MAJOR_VERSION == 5) - OculusBaseDisplayPlugin::preRender(); - ovrHmd_BeginFrame(_hmd, _frameIndex); -#endif -} - -void Oculus_0_5_DisplayPlugin::preDisplay() { +void OculusLegacyDisplayPlugin::preDisplay() { _window->makeCurrent(); } -void Oculus_0_5_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { +void OculusLegacyDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { ++_frameIndex; #if (OVR_MAJOR_VERSION == 5) ovr_for_each_eye([&](ovrEyeType eye) { @@ -164,7 +215,7 @@ void Oculus_0_5_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc } // Pass input events on to the application -bool Oculus_0_5_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { +bool OculusLegacyDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { #if (OVR_MAJOR_VERSION == 5) if (!_hswDismissed && (event->type() == QEvent::KeyPress)) { static ovrHSWDisplayState hswState; @@ -176,17 +227,19 @@ bool Oculus_0_5_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { } } #endif - return OculusBaseDisplayPlugin::eventFilter(receiver, event); + return WindowOpenGLDisplayPlugin::eventFilter(receiver, event); } // FIXME mirroring tot he main window is diffucult on OSX because it requires that we // trigger a swap, which causes the client to wait for the v-sync of the main screen running // at 60 Hz. This would introduce judder. Perhaps we can push mirroring to a separate // thread -void Oculus_0_5_DisplayPlugin::finishFrame() { +// FIXME If we move to the 'batch rendering on a different thread' we can possibly do this. +// however, we need to make sure it doesn't block the event handling. +void OculusLegacyDisplayPlugin::finishFrame() { _window->doneCurrent(); }; -int Oculus_0_5_DisplayPlugin::getHmdScreen() const { +int OculusLegacyDisplayPlugin::getHmdScreen() const { return _hmdScreen; } diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h new file mode 100644 index 0000000000..ce91289cb0 --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h @@ -0,0 +1,63 @@ +// +// Created by Bradley Austin Davis on 2015/05/29 +// 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 "../WindowOpenGLDisplayPlugin.h" + +#include + +#include + +class OculusLegacyDisplayPlugin : public WindowOpenGLDisplayPlugin { +public: + OculusLegacyDisplayPlugin(); + virtual bool isSupported() const override; + virtual const QString & getName() const override; + + virtual void activate() override; + virtual void deactivate() override; + + virtual bool eventFilter(QObject* receiver, QEvent* event) override; + virtual int getHmdScreen() const override; + + // Stereo specific methods + virtual bool isHmd() const override { return true; } + virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; + virtual glm::uvec2 getRecommendedRenderSize() const override; + virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); } + virtual void resetSensors() override; + virtual glm::mat4 getEyePose(Eye eye) const override; + virtual glm::mat4 getHeadPose() const override; + +protected: + virtual void preRender() override; + virtual void preDisplay() override; + virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; + // Do not perform swap in finish + virtual void finishFrame() override; + +private: + static const QString NAME; + + float _ipd{ OVR_DEFAULT_IPD }; + ovrHmd _hmd; + unsigned int _frameIndex; + ovrTrackingState _trackingState; + ovrEyeRenderDesc _eyeRenderDescs[2]; + ovrPosef _eyePoses[2]; + ovrVector3f _eyeOffsets[2]; + ovrFovPort _eyeFovs[2]; + mat4 _eyeProjections[2]; + mat4 _compositeEyeProjections[2]; + uvec2 _desiredFramebufferSize; + ovrTexture _eyeTextures[2]; + mutable int _hmdScreen{ -1 }; + bool _hswDismissed{ false }; +}; + + diff --git a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_5_DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_5_DisplayPlugin.h deleted file mode 100644 index b539d07fb0..0000000000 --- a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_5_DisplayPlugin.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015/05/29 -// 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 "OculusBaseDisplayPlugin.h" - -#include - -class Oculus_0_5_DisplayPlugin : public OculusBaseDisplayPlugin { -public: - virtual bool isSupported() const override; - virtual const QString & getName() const override; - - virtual void activate() override; - virtual void deactivate() override; - - virtual bool eventFilter(QObject* receiver, QEvent* event) override; - - virtual int getHmdScreen() const override; - -protected: - virtual void preRender() override; - virtual void preDisplay() override; - virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; - // Do not perform swap in finish - virtual void finishFrame() override; - -private: - static const QString NAME; -}; - - diff --git a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.h deleted file mode 100644 index 0fde5e76b3..0000000000 --- a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015/05/29 -// 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 "OculusBaseDisplayPlugin.h" - -#include - -class OffscreenGlCanvas; -struct SwapFramebufferWrapper; -struct MirrorFramebufferWrapper; - -using SwapFboPtr = QSharedPointer; -using MirrorFboPtr = QSharedPointer; - -class Oculus_0_6_DisplayPlugin : public OculusBaseDisplayPlugin { -public: - virtual bool isSupported() const override; - virtual const QString & getName() const override; - - virtual void activate() override; - virtual void deactivate() override; - - - virtual bool eventFilter(QObject* receiver, QEvent* event) override; - -protected: - virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; - virtual void customizeContext() override; - // Do not perform swap in finish - virtual void finishFrame() override; - -private: - static const QString NAME; -}; - diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp index 181546d428..1a5aa2f437 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp @@ -128,7 +128,7 @@ void OpenVrDisplayPlugin::activate() { delete[] buffer; } Q_ASSERT(unSize <= 1); - MainWindowOpenGLDisplayPlugin::activate(); + WindowOpenGLDisplayPlugin::activate(); } void OpenVrDisplayPlugin::deactivate() { @@ -141,7 +141,7 @@ void OpenVrDisplayPlugin::deactivate() { _hmd = nullptr; } _compositor = nullptr; - MainWindowOpenGLDisplayPlugin::deactivate(); + WindowOpenGLDisplayPlugin::deactivate(); } uvec2 OpenVrDisplayPlugin::getRecommendedRenderSize() const { @@ -152,16 +152,12 @@ mat4 OpenVrDisplayPlugin::getProjection(Eye eye, const mat4& baseProjection) con return _eyesData[eye]._projectionMatrix; } -glm::mat4 OpenVrDisplayPlugin::getModelview(Eye eye, const mat4& baseModelview) const { - return baseModelview * getEyePose(eye); -} - void OpenVrDisplayPlugin::resetSensors() { _sensorResetMat = glm::inverse(cancelOutRollAndPitch(_trackedDevicePoseMat4[0])); } glm::mat4 OpenVrDisplayPlugin::getEyePose(Eye eye) const { - return getHeadPose() * _eyesData[eye]._eyeOffset; + return _eyesData[eye]._eyeOffset * getHeadPose(); } glm::mat4 OpenVrDisplayPlugin::getHeadPose() const { @@ -169,7 +165,7 @@ glm::mat4 OpenVrDisplayPlugin::getHeadPose() const { } void OpenVrDisplayPlugin::customizeContext() { - MainWindowOpenGLDisplayPlugin::customizeContext(); + WindowOpenGLDisplayPlugin::customizeContext(); } void OpenVrDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h index 608a869341..afe024e72b 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h @@ -11,9 +11,9 @@ #if defined(Q_OS_WIN) -#include "../MainWindowOpenGLDisplayPlugin.h" +#include "../WindowOpenGLDisplayPlugin.h" -class OpenVrDisplayPlugin : public MainWindowOpenGLDisplayPlugin { +class OpenVrDisplayPlugin : public WindowOpenGLDisplayPlugin { public: virtual bool isSupported() const override; virtual const QString & getName() const override; @@ -27,7 +27,6 @@ public: // Stereo specific methods virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; - virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const override; virtual void resetSensors() override; virtual glm::mat4 getEyePose(Eye eye) const override; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index 3d5f73bfa8..104d74097c 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -42,12 +42,12 @@ glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProje return glm::translate(baseProjection, vec3(frustumshift, 0, 0)); } -glm::mat4 StereoDisplayPlugin::getModelview(Eye eye, const glm::mat4& baseModelview) const { +glm::mat4 StereoDisplayPlugin::getEyePose(Eye eye) const { float modelviewShift = HALF_DEFAULT_IPD; if (eye == Left) { modelviewShift = -modelviewShift; } - return baseModelview * glm::translate(mat4(), vec3(modelviewShift, 0, 0)); + return glm::translate(mat4(), vec3(modelviewShift, 0, 0)); } void StereoDisplayPlugin::activate() { @@ -57,11 +57,3 @@ void StereoDisplayPlugin::activate() { //CONTAINER->setFullscreen(qApp->primaryScreen()); // FIXME Add menu items } - -glm::vec3 StereoDisplayPlugin::getEyeOffset(Eye eye) const { - glm::vec3 result(_ipd / 2.0f, 0, 0); - if (eye == Eye::Right) { - result *= -1.0f; - } - return result; -} diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h index 77b1141aed..b0f0414de2 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h @@ -7,9 +7,9 @@ // #pragma once -#include "../MainWindowOpenGLDisplayPlugin.h" +#include "../WindowOpenGLDisplayPlugin.h" -class StereoDisplayPlugin : public MainWindowOpenGLDisplayPlugin { +class StereoDisplayPlugin : public WindowOpenGLDisplayPlugin { Q_OBJECT public: StereoDisplayPlugin(); @@ -19,8 +19,7 @@ public: virtual void activate() override; virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; - virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const override; - virtual glm::vec3 getEyeOffset(Eye eye) const override; + virtual glm::mat4 getEyePose(Eye eye) const override; protected: float _ipd{ 0.064f }; From 62e8ec3fdfc47dea34dd9adb62c0307570d6a591 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Aug 2015 15:09:52 -0700 Subject: [PATCH 062/103] add fullscreen menu item and rename side by side stereo plugin --- .../Basic2DWindowOpenGLDisplayPlugin.cpp | 4 ++-- .../stereo/SideBySideStereoDisplayPlugin.cpp | 24 ++++++++++++++++++- .../stereo/SideBySideStereoDisplayPlugin.h | 6 ++++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 14ba868275..018a09ff7e 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -13,8 +13,8 @@ const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display"); -const QString MENU_PATH = "Display"; -const QString FULLSCREEN = "Fullscreen"; +static const QString MENU_PATH = "Display"; +static const QString FULLSCREEN = "Fullscreen"; const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const { return NAME; diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp index 72ef5249a8..829385b209 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp @@ -10,14 +10,19 @@ #include #include +#include #include #include #include #include +#include -const QString SideBySideStereoDisplayPlugin::NAME("Debug Stereo Display"); +const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo"); + +static const QString MENU_PATH = "Display"; +static const QString FULLSCREEN = "Fullscreen"; const QString & SideBySideStereoDisplayPlugin::getName() const { return NAME; @@ -26,3 +31,20 @@ const QString & SideBySideStereoDisplayPlugin::getName() const { SideBySideStereoDisplayPlugin::SideBySideStereoDisplayPlugin() { } +void SideBySideStereoDisplayPlugin::activate() { + CONTAINER->addMenu(MENU_PATH); + CONTAINER->addMenuItem(MENU_PATH, FULLSCREEN, + [this](bool clicked) { + if (clicked) { + CONTAINER->setFullscreen(getFullscreenTarget()); + } else { + CONTAINER->unsetFullscreen(); + } + }, true, false); + StereoDisplayPlugin::activate(); +} + +// FIXME target the screen the window is currently on +QScreen* SideBySideStereoDisplayPlugin::getFullscreenTarget() { + return qApp->primaryScreen(); +} diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h index ead9ea7dc4..3a764d9f4e 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h @@ -9,11 +9,15 @@ #include "StereoDisplayPlugin.h" +class QScreen; + class SideBySideStereoDisplayPlugin : public StereoDisplayPlugin { Q_OBJECT public: SideBySideStereoDisplayPlugin(); - virtual const QString & getName() const override; + virtual const QString& getName() const override; + virtual void activate() override; private: + QScreen* getFullscreenTarget(); static const QString NAME; }; From 476e5edb3210bb76d43e9498526f468317a9459f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 20 Aug 2015 15:47:31 -0700 Subject: [PATCH 063/103] Fixing HMD wobble --- interface/src/Application.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 374963e9b6..c3773c3b73 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1123,10 +1123,18 @@ void Application::paintGL() { mat4 eyeViews[2]; mat4 eyeProjections[2]; auto baseProjection = renderArgs._viewFrustum->getProjection(); - // FIXME we don't need to set these every frame, - // only when the display plugin changes + // FIXME we probably don't need to set the projection matrix every frame, + // only when the display plugin changes (or in non-HMD modes when the user + // changes the FOV manually, which right now I don't think they can. for_each_eye([&](Eye eye) { - eyeViews[eye] = displayPlugin->getView(eye, mat4()); + // For providing the stereo eye views, the HMD head pose has already been + // applied to the avatar, so we need to get the difference between the head + // pose applied to the avatar and the per eye pose, and use THAT as + // the per-eye stereo matrix adjustment. + mat4 eyePose = displayPlugin->getEyePose(eye); + mat4 headPose = displayPlugin->getHeadPose(); + mat4 eyeView = glm::inverse(eyePose) * headPose; + eyeViews[eye] = eyeView; eyeProjections[eye] = displayPlugin->getProjection(eye, baseProjection); }); renderArgs._context->setStereoProjections(eyeProjections); From cc037c8828f2a6b84e0bf90e0e24ca280d2ef5a6 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 20 Aug 2015 16:20:34 -0700 Subject: [PATCH 064/103] Fix culling calculations in HMD with new stereo mechanism --- interface/src/Application.cpp | 11 ++++++++++- .../oculus/OculusLegacyDisplayPlugin.cpp | 7 +++++++ .../oculus/OculusLegacyDisplayPlugin.h | 2 +- .../display-plugins/openvr/OpenVrDisplayPlugin.cpp | 4 ++++ .../display-plugins/stereo/StereoDisplayPlugin.cpp | 2 ++ 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c3773c3b73..0c3bbd8d34 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1118,7 +1118,16 @@ void Application::paintGL() { renderArgs._viewport = gpu::Vec4i(0, 0, size.width(), size.height()); if (displayPlugin->isStereo()) { - //_myCamera.setProjection(displayPlugin->getProjection(Mono, _myCamera.getProjection())); + // Stereo modes will typically have a larger projection matrix overall, + // so we ask for the 'mono' projection matrix, which for stereo and HMD + // plugins will imply the combined projection for both eyes. + // + // This is properly implemented for the Oculus plugins, but for OpenVR + // and Stereo displays I'm not sure how to get / calculate it, so we're + // just relying on the left FOV in each case and hoping that the + // overall culling margin of error doesn't cause popping in the + // right eye. There are FIXMEs in the relevant plugins + _myCamera.setProjection(displayPlugin->getProjection(Mono, _myCamera.getProjection())); renderArgs._context->enableStereo(true); mat4 eyeViews[2]; mat4 eyeProjections[2]; diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp index 2c5af1a52b..e823f456ce 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp @@ -136,6 +136,13 @@ void OculusLegacyDisplayPlugin::activate() { _eyeOffsets[eye] = erd.HmdToEyeViewOffset; eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f)); }); + ovrFovPort combined = _eyeFovs[Left]; + combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan); + combined.RightTan = std::max(_eyeFovs[Left].RightTan, _eyeFovs[Right].RightTan); + ovrMatrix4f ovrPerspectiveProjection = + ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); + _eyeProjections[Mono] = toGlm(ovrPerspectiveProjection); + _desiredFramebufferSize = uvec2( eyeSizes[0].x + eyeSizes[1].x, std::max(eyeSizes[0].y, eyeSizes[1].y)); diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h index ce91289cb0..219b6c54b3 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h @@ -52,7 +52,7 @@ private: ovrPosef _eyePoses[2]; ovrVector3f _eyeOffsets[2]; ovrFovPort _eyeFovs[2]; - mat4 _eyeProjections[2]; + mat4 _eyeProjections[3]; mat4 _compositeEyeProjections[2]; uvec2 _desiredFramebufferSize; ovrTexture _eyeTextures[2]; diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp index 1a5aa2f437..1e3e7699f4 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp @@ -149,6 +149,10 @@ uvec2 OpenVrDisplayPlugin::getRecommendedRenderSize() const { } mat4 OpenVrDisplayPlugin::getProjection(Eye eye, const mat4& baseProjection) const { + // FIXME hack to ensure that we don't crash trying to get the combined matrix + if (eye == Mono) { + eye = Left; + } return _eyesData[eye]._projectionMatrix; } diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index 104d74097c..20613d6a2c 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -29,6 +29,8 @@ const float DEFAULT_IPD = 0.064f; const float HALF_DEFAULT_IPD = DEFAULT_IPD / 2.0f; glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const { + // FIXME check for mono eye and provide a combined matrix, needed for proper + // culling // Refer to http://www.nvidia.com/content/gtc-2010/pdfs/2010_gtc2010.pdf on creating // stereo projection matrices. Do NOT use "toe-in", use translation. From 75aff2f281053e3bc0e30a5ee6fc6090739cdf31 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Aug 2015 16:39:23 -0700 Subject: [PATCH 065/103] fix atmosphere rendering --- libraries/render-utils/src/Environment.cpp | 24 +++++++++++++++------- libraries/render-utils/src/Environment.h | 4 ++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/libraries/render-utils/src/Environment.cpp b/libraries/render-utils/src/Environment.cpp index 9cce508946..605f67f957 100644 --- a/libraries/render-utils/src/Environment.cpp +++ b/libraries/render-utils/src/Environment.cpp @@ -92,17 +92,17 @@ void Environment::resetToDefault() { _data[QUuid()][0]; } -void Environment::renderAtmospheres(gpu::Batch& batch, ViewFrustum& camera) { +void Environment::renderAtmospheres(gpu::Batch& batch, ViewFrustum& viewFrustum) { // get the lock for the duration of the call QMutexLocker locker(&_mutex); if (_environmentIsOverridden) { - renderAtmosphere(batch, camera, _overrideData); + renderAtmosphere(batch, viewFrustum, _overrideData); } else { foreach (const ServerData& serverData, _data) { // TODO: do something about EnvironmentData foreach (const EnvironmentData& environmentData, serverData) { - renderAtmosphere(batch, camera, environmentData); + renderAtmosphere(batch, viewFrustum, environmentData); } } } @@ -196,15 +196,25 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3 return found; } -void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data) { +void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& viewFrustum, const EnvironmentData& data) { glm::vec3 center = data.getAtmosphereCenter(); + // transform the model transform to the center of our atmosphere Transform transform; transform.setTranslation(center); batch.setModelTransform(transform); - - glm::vec3 relativeCameraPos = camera.getPosition() - center; + + // Make sure our view and projection transforms are correct for our viewFrustum + Transform viewTransform; + viewFrustum.evalViewTransform(viewTransform); + batch.setViewTransform(viewTransform); + + glm::mat4 projMat; + viewFrustum.evalProjectionMatrix(projMat); + batch.setProjectionTransform(projMat); + + glm::vec3 relativeCameraPos = viewFrustum.getPosition() - center; float height = glm::length(relativeCameraPos); // use the appropriate shader depending on whether we're inside or outside @@ -212,11 +222,11 @@ void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const if (height < data.getAtmosphereOuterRadius()) { batch.setPipeline(_skyFromAtmosphereProgram); locations = _skyFromAtmosphereUniformLocations; - } else { 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 diff --git a/libraries/render-utils/src/Environment.h b/libraries/render-utils/src/Environment.h index 5b10f2f1a9..af5a3c3df5 100644 --- a/libraries/render-utils/src/Environment.h +++ b/libraries/render-utils/src/Environment.h @@ -29,7 +29,7 @@ public: void init(); void resetToDefault(); - void renderAtmospheres(gpu::Batch& batch, ViewFrustum& camera); + void renderAtmospheres(gpu::Batch& batch, ViewFrustum& viewFrustum); void override(const EnvironmentData& overrideData) { _overrideData = overrideData; _environmentIsOverridden = true; } void endOverride() { _environmentIsOverridden = false; } @@ -41,7 +41,7 @@ private: bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration); // NOTE: Deprecated - void renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data); + void renderAtmosphere(gpu::Batch& batch, ViewFrustum& viewFrustum, const EnvironmentData& data); bool _initialized; From 1e074ab81c20cd2ef3bf60cb271988a238a9e6aa Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 21 Aug 2015 00:28:43 -0700 Subject: [PATCH 066/103] Remove superfluous clear --- interface/src/Application.cpp | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0c3bbd8d34..301362aca0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1097,26 +1097,11 @@ void Application::paintGL() { // Primary rendering pass auto framebufferCache = DependencyManager::get(); - QSize size = framebufferCache->getFrameBufferSize(); + const QSize size = framebufferCache->getFrameBufferSize(); { PROFILE_RANGE(__FUNCTION__ "/mainRender"); // Viewport is assigned to the size of the framebuffer - QSize size = DependencyManager::get()->getFrameBufferSize(); - renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); - - doInBatch(&renderArgs, [&](gpu::Batch& batch) { - auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); - batch.setFramebuffer(primaryFbo); - // clear the normal and specular buffers - batch.clearFramebuffer( - gpu::Framebuffer::BUFFER_COLOR0 | - gpu::Framebuffer::BUFFER_COLOR1 | - gpu::Framebuffer::BUFFER_COLOR2 | - gpu::Framebuffer::BUFFER_DEPTH, - vec4(vec3(0), 1), 1.0, 0.0); - }); - - renderArgs._viewport = gpu::Vec4i(0, 0, size.width(), size.height()); + renderArgs._viewport = ivec4(0, 0, size.width(), size.height()); if (displayPlugin->isStereo()) { // Stereo modes will typically have a larger projection matrix overall, // so we ask for the 'mono' projection matrix, which for stereo and HMD @@ -1185,7 +1170,6 @@ void Application::paintGL() { PROFILE_RANGE(__FUNCTION__ "/pluginOutput"); auto primaryFbo = framebufferCache->getPrimaryFramebuffer(); GLuint finalTexture = gpu::GLBackend::getTextureID(primaryFbo->getRenderBuffer(0)); - uvec2 finalSize = toGlm(size); // Ensure the rendering context commands are completed when rendering GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); // Ensure the sync object is flushed to the driver thread before releasing the context @@ -1201,7 +1185,7 @@ void Application::paintGL() { { PROFILE_RANGE(__FUNCTION__ "/pluginDisplay"); - displayPlugin->display(finalTexture, finalSize); + displayPlugin->display(finalTexture, toGlm(size)); } { From a0552050b08681e939afb36e316499f2a5833d9c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Aug 2015 08:23:18 -0700 Subject: [PATCH 067/103] set vsync on startup --- interface/src/Application.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b9bafbab1a..aa18e602bf 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -737,6 +737,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : }); connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); + + setVSyncEnabled(); // make sure VSync is set properly at startup } void Application::aboutToQuit() { From 270463fc4e5ac72648869ec45cca793421d02d91 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Aug 2015 09:29:25 -0700 Subject: [PATCH 068/103] restore new oculus detect code from master --- .../src/display-plugins/oculus/OculusDisplayPlugin.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp index 84d67f038a..ff218987ec 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp @@ -204,7 +204,15 @@ const QString & OculusDisplayPlugin::getName() const { bool OculusDisplayPlugin::isSupported() const { #if (OVR_MAJOR_VERSION >= 6) - return true; + if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { + return false; + } + bool result = false; + if (ovrHmd_Detect() > 0) { + result = true; + } + ovr_Shutdown(); + return result; #else return false; #endif From b8b0917d9db1c38d58ae4a5be6829f9c1755ae49 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 21 Aug 2015 10:01:11 -0700 Subject: [PATCH 069/103] Attempting to fix judder issues --- interface/src/Application.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 301362aca0..d3b1ce2318 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -993,6 +993,8 @@ void Application::paintGL() { auto displayPlugin = getActiveDisplayPlugin(); displayPlugin->preRender(); _offscreenContext->makeCurrent(); + // update the avatar with a fresh HMD pose + _myAvatar->updateFromHMDSensorMatrix(getHMDSensorPose()); auto lodManager = DependencyManager::get(); @@ -2683,9 +2685,6 @@ void Application::update(float deltaTime) { updateLOD(); updateMouseRay(); // check what's under the mouse and update the mouse voxel - // update the avatar with a fresh HMD pose - _myAvatar->updateFromHMDSensorMatrix(getHMDSensorPose()); - { PerformanceTimer perfTimer("devices"); DeviceTracker::updateAll(); From 55fd847636cbc99a438ecaef7cb5b0d4ee69ed0e Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 21 Aug 2015 19:07:33 +0200 Subject: [PATCH 070/103] removed unused code, renamed UnitTest.ready() to UnitTest.done() --- examples/libraries/unitTest.js | 2 +- examples/utilities/diagnostics/testWebSocket.js | 6 +++--- libraries/script-engine/src/WebSocketClass.cpp | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/libraries/unitTest.js b/examples/libraries/unitTest.js index d65cd780bd..7d5234933f 100644 --- a/examples/libraries/unitTest.js +++ b/examples/libraries/unitTest.js @@ -173,7 +173,7 @@ UnitTest.prototype.raises = function(func, message) { throw new UnthrownException(message); } -UnitTest.prototype.ready = function() { +UnitTest.prototype.done = function() { if (this.timeout !== undefined) { Script.clearTimeout(this.timeoutTimer); this.successCallback(this); diff --git a/examples/utilities/diagnostics/testWebSocket.js b/examples/utilities/diagnostics/testWebSocket.js index 03b9b6b405..af915c3790 100644 --- a/examples/utilities/diagnostics/testWebSocket.js +++ b/examples/utilities/diagnostics/testWebSocket.js @@ -35,7 +35,7 @@ unitTests.addTest("Test default WebSocket values", function(finished) { }); webSocket.onclose = this.registerCallbackFunction(function(event) { _this.assertEquals(webSocket.CLOSED, webSocket.readyState, "readyState should be CLOSED"); - _this.ready(); + _this.done(); }); this.assertEquals(webSocket.CONNECTING, webSocket.readyState, "readyState should be CONNECTING"); this.assertEquals("blob", webSocket.binaryType, "binaryType should be 'blob'"); @@ -55,7 +55,7 @@ unitTests.addTest("Test WebSocket invalid URL", function(finished) { webSocket.onclose = this.registerCallbackFunction(function(event) { _this.assertEquals(true, hadError, "hadError should be true"); _this.assertEquals(webSocket.CLOSED, webSocket.readyState, "readyState should be CLOSED"); - _this.ready(); + _this.done(); }); this.assertEquals(webSocket.CONNECTING, webSocket.readyState, "readyState should be CONNECTING"); this.assertEquals(WEBSOCKET_INVALID_URL, webSocket.url, "url should be '" + WEBSOCKET_INVALID_URL + "'"); @@ -78,7 +78,7 @@ unitTests.addTest("Test WebSocketServer with three clients", function(finished) if (respondedClients === NUMBER_OF_CLIENTS) { webSocketServer.close(); _this.assertEquals(false, webSocketServer.listening, "listening should be false"); - _this.ready(); + _this.done(); } }); newClient.send(JSON.stringify({message: TEST_MESSAGE, client: connectedClients})); diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 654b4746a8..0245da538d 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -39,7 +39,6 @@ void WebSocketClass::initialize() { QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { QString url; - QScriptValue callee = context->callee(); if (context->argumentCount() == 1) { url = context->argument(0).toString(); } From e581a57320495e3a5fc6b04ab5671428eafda9cc Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 21 Aug 2015 19:13:57 +0200 Subject: [PATCH 071/103] WebSocket constructor could allow more then one parameter now, only the first one is used though. --- libraries/script-engine/src/WebSocketClass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 0245da538d..27cb436998 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -39,7 +39,7 @@ void WebSocketClass::initialize() { QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { QString url; - if (context->argumentCount() == 1) { + if (context->argumentCount() > 0) { url = context->argument(0).toString(); } return engine->newQObject(new WebSocketClass(engine, url)); From c7346209fb40aba7902d76c73e4bc503e47aa69b Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 21 Aug 2015 19:44:43 +0200 Subject: [PATCH 072/103] fix memory leak --- libraries/script-engine/src/WebSocketClass.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 27cb436998..52059d1290 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -34,7 +34,7 @@ void WebSocketClass::initialize() { connect(_webSocket, &QWebSocket::disconnected, this, &WebSocketClass::handleOnClose); connect(_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketClass::handleOnMessage); connect(_webSocket, &QWebSocket::connected, this, &WebSocketClass::handleOnOpen); - _binaryType = "blob"; + _binaryType = QStringLiteral("blob"); } QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { @@ -46,7 +46,7 @@ QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* } WebSocketClass::~WebSocketClass() { - + _webSocket->deleteLater(); } void WebSocketClass::send(QScriptValue message) { From d3a68331339f89ed6c939a6b0aec49204b429cae Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 21 Aug 2015 20:20:50 +0200 Subject: [PATCH 073/103] delete WebSockets on ScriptEngine finish --- libraries/script-engine/src/WebSocketClass.cpp | 10 ++++++---- libraries/script-engine/src/WebSocketServerClass.cpp | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 52059d1290..7c7670fcce 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -42,7 +42,9 @@ QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* if (context->argumentCount() > 0) { url = context->argument(0).toString(); } - return engine->newQObject(new WebSocketClass(engine, url)); + auto webSocketClass = new WebSocketClass(engine, url); + connect(static_cast(engine), &ScriptEngine::finished, webSocketClass, &QObject::deleteLater); + return engine->newQObject(webSocketClass); } WebSocketClass::~WebSocketClass() { @@ -54,7 +56,7 @@ void WebSocketClass::send(QScriptValue message) { } void WebSocketClass::close() { - this->close(QWebSocketProtocol::CloseCode::CloseCodeNormal); + this->close(QWebSocketProtocol::CloseCodeNormal); } void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode) { @@ -67,7 +69,7 @@ void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode, QString reas void WebSocketClass::handleOnClose() { bool hasError = false; - if (_webSocket->error() != QAbstractSocket::SocketError::UnknownSocketError) { + if (_webSocket->error() != QAbstractSocket::UnknownSocketError) { hasError = true; if (_onErrorEvent.isFunction()) { _onErrorEvent.call(); @@ -76,7 +78,7 @@ void WebSocketClass::handleOnClose() { if (_onCloseEvent.isFunction()) { QScriptValueList args; QScriptValue arg = _engine->newObject(); - arg.setProperty("code", hasError ? QWebSocketProtocol::CloseCode::CloseCodeAbnormalDisconnection : _webSocket->closeCode()); + arg.setProperty("code", hasError ? QWebSocketProtocol::CloseCodeAbnormalDisconnection : _webSocket->closeCode()); arg.setProperty("reason", _webSocket->closeReason()); arg.setProperty("wasClean", !hasError); args << arg; diff --git a/libraries/script-engine/src/WebSocketServerClass.cpp b/libraries/script-engine/src/WebSocketServerClass.cpp index dbe92510e4..045b39d800 100644 --- a/libraries/script-engine/src/WebSocketServerClass.cpp +++ b/libraries/script-engine/src/WebSocketServerClass.cpp @@ -49,6 +49,7 @@ WebSocketServerClass::~WebSocketServerClass() { if (_webSocketServer.isListening()) { close(); } + _clients.empty(); } void WebSocketServerClass::onNewConnection() { From 6ee22055fa6b5c3e1b40e4aee451522a7334b9a1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 21 Aug 2015 12:48:08 -0700 Subject: [PATCH 074/103] More temporary logging to track down cause of assert --- interface/src/avatar/Avatar.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 19f84018f8..6d6120f3ba 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -748,11 +748,12 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa qDebug() << "ASSERT because isinf(scale)"; } qDebug() << "textPosition =" << textPosition; + qDebug() << "projMat =" << projMat; + qDebug() << "viewMat =" << viewMat; + qDebug() << "viewProj =" << viewProj; qDebug() << "windowSizeY =" << windowSizeY; - qDebug() << "p1.y =" << p1.y; - qDebug() << "p1.w =" << p1.w; - qDebug() << "p0.y =" << p0.y; - qDebug() << "p0.w =" << p0.w; + qDebug() << "p1 =" << p1; + qDebug() << "p0 =" << p0; qDebug() << "qApp->getDevicePixelRatio() =" << qApp->getDevicePixelRatio(); qDebug() << "fontSize =" << fontSize; qDebug() << "pixelHeight =" << pixelHeight; From d89ecc1e420cd01b0bf501bbb38d22b6b0686f3c Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 21 Aug 2015 22:43:49 +0200 Subject: [PATCH 075/103] Move WebSocketServer to be accessable from Assignment-Client:Agent only. --- assignment-client/src/Agent.cpp | 4 + .../utilities/diagnostics/testWebSocket.js | 73 ++++++++++--------- libraries/script-engine/src/ScriptEngine.cpp | 4 - 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index d656464c10..4c3958c878 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -24,6 +24,7 @@ #include #include +#include #include // TODO: consider moving to scriptengine.h #include "avatars/ScriptableAvatar.h" @@ -184,6 +185,9 @@ void Agent::run() { _scriptEngine.registerGlobalObject("SoundCache", DependencyManager::get().data()); + QScriptValue webSocketServerConstructorValue = _scriptEngine.newFunction(WebSocketServerClass::constructor); + _scriptEngine.globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); + auto entityScriptingInterface = DependencyManager::get(); _scriptEngine.registerGlobalObject("EntityViewer", &_entityViewer); diff --git a/examples/utilities/diagnostics/testWebSocket.js b/examples/utilities/diagnostics/testWebSocket.js index af915c3790..4cf6c3682b 100644 --- a/examples/utilities/diagnostics/testWebSocket.js +++ b/examples/utilities/diagnostics/testWebSocket.js @@ -61,40 +61,43 @@ unitTests.addTest("Test WebSocket invalid URL", function(finished) { this.assertEquals(WEBSOCKET_INVALID_URL, webSocket.url, "url should be '" + WEBSOCKET_INVALID_URL + "'"); }, UNITTEST_TIMEOUT); - -unitTests.addTest("Test WebSocketServer with three clients", function(finished) { - var _this = this; - const NUMBER_OF_CLIENTS = 3; - var connectedClients = 0; - var respondedClients = 0; - var webSocketServer = new WebSocketServer(); - _this.assertEquals(true, webSocketServer.listening, "listening should be true"); - webSocketServer.newConnection.connect(this.registerCallbackFunction(function(newClient) { - connectedClients++; - newClient.onmessage = _this.registerCallbackFunction(function(event) { - var data = JSON.parse(event.data); - _this.assertEquals(TEST_MESSAGE, data.message, "data.message should be '" + TEST_MESSAGE + "'"); - respondedClients++; - if (respondedClients === NUMBER_OF_CLIENTS) { - webSocketServer.close(); - _this.assertEquals(false, webSocketServer.listening, "listening should be false"); - _this.done(); - } - }); - newClient.send(JSON.stringify({message: TEST_MESSAGE, client: connectedClients})); - })); - var newSocket1 = new WebSocket(webSocketServer.url); - newSocket1.onmessage = this.registerCallbackFunction(function(event) { - newSocket1.send(event.data); - }); - var newSocket2 = new WebSocket(webSocketServer.url); - newSocket2.onmessage = this.registerCallbackFunction(function(event) { - newSocket2.send(event.data); - }); - var newSocket3 = new WebSocket(webSocketServer.url); - newSocket3.onmessage = this.registerCallbackFunction(function(event) { - newSocket3.send(event.data); - }); -}, UNITTEST_TIMEOUT); +if (this.WebSocketServer === undefined) { + print("Skipping WebSocketServer tests."); +} else { + unitTests.addTest("Test WebSocketServer with three clients", function(finished) { + var _this = this; + const NUMBER_OF_CLIENTS = 3; + var connectedClients = 0; + var respondedClients = 0; + var webSocketServer = new WebSocketServer(); + _this.assertEquals(true, webSocketServer.listening, "listening should be true"); + webSocketServer.newConnection.connect(this.registerCallbackFunction(function(newClient) { + connectedClients++; + newClient.onmessage = _this.registerCallbackFunction(function(event) { + var data = JSON.parse(event.data); + _this.assertEquals(TEST_MESSAGE, data.message, "data.message should be '" + TEST_MESSAGE + "'"); + respondedClients++; + if (respondedClients === NUMBER_OF_CLIENTS) { + webSocketServer.close(); + _this.assertEquals(false, webSocketServer.listening, "listening should be false"); + _this.done(); + } + }); + newClient.send(JSON.stringify({message: TEST_MESSAGE, client: connectedClients})); + })); + var newSocket1 = new WebSocket(webSocketServer.url); + newSocket1.onmessage = this.registerCallbackFunction(function(event) { + newSocket1.send(event.data); + }); + var newSocket2 = new WebSocket(webSocketServer.url); + newSocket2.onmessage = this.registerCallbackFunction(function(event) { + newSocket2.send(event.data); + }); + var newSocket3 = new WebSocket(webSocketServer.url); + newSocket3.onmessage = this.registerCallbackFunction(function(event) { + newSocket3.send(event.data); + }); + }, UNITTEST_TIMEOUT); +} unitTests.run(); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index c629529382..32a2a5b6f0 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -39,7 +39,6 @@ #include "TypedArrays.h" #include "XMLHttpRequestClass.h" #include "WebSocketClass.h" -#include "WebSocketServerClass.h" #include "SceneScriptingInterface.h" @@ -348,9 +347,6 @@ void ScriptEngine::init() { QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor); globalObject().setProperty("WebSocket", webSocketConstructorValue); - QScriptValue webSocketServerConstructorValue = newFunction(WebSocketServerClass::constructor); - globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); - QScriptValue printConstructorValue = newFunction(debugPrint); globalObject().setProperty("print", printConstructorValue); From 176d7372adf72fbc3c7d61af52534ee52e9bc297 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 21 Aug 2015 23:03:26 +0200 Subject: [PATCH 076/103] add QWebSockets to assignment-client project (cmake) --- assignment-client/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 315eeb6b83..dc20763953 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME assignment-client) -setup_hifi_project(Core Gui Network Script Widgets) +setup_hifi_project(Core Gui Network Script Widgets WebSockets) add_dependency_external_projects(glm) find_package(GLM REQUIRED) From 220ad189a04128e1926182b6170e1f46d1cbd6bf Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Aug 2015 14:16:34 -0700 Subject: [PATCH 077/103] force the HTTPManager to listen on IPv4 --- .../embedded-webserver/src/HTTPManager.cpp | 27 ++++++------------- .../embedded-webserver/src/HTTPManager.h | 7 ----- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 72436fc55e..946da00a34 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -29,10 +29,14 @@ HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestH _requestHandler(requestHandler), _port(port) { - bindSocket(); - _isListeningTimer = new QTimer(this); - connect(_isListeningTimer, &QTimer::timeout, this, &HTTPManager::isTcpServerListening); - _isListeningTimer->start(SOCKET_CHECK_INTERVAL_IN_MS); + qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); + + if (listen(QHostAddress::AnyIPv4, _port)) { + qCDebug(embeddedwebserver) << "TCP socket is listening on" << serverAddress() << "and port" << serverPort(); + } else { + qCritical() << "Failed to open HTTP server socket:" << errorString() << " - forcing exit."; + QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); + } } void HTTPManager::incomingConnection(qintptr socketDescriptor) { @@ -162,18 +166,3 @@ bool HTTPManager::requestHandledByRequestHandler(HTTPConnection* connection, con return _requestHandler && _requestHandler->handleHTTPRequest(connection, url); } -void HTTPManager::isTcpServerListening() { - if (!isListening()) { - qCWarning(embeddedwebserver) << "Socket on port " << QString::number(_port) << " is no longer listening"; - bindSocket(); - } -} - -bool HTTPManager::bindSocket() { - qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); - if (!listen(QHostAddress::Any, _port)) { - qCritical() << "Failed to open HTTP server socket:" << errorString() << " can't continue"; - QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); - } - return true; -} \ No newline at end of file diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index 6375b10205..8e1780a631 100644 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -37,12 +37,6 @@ public: bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); -private slots: - void isTcpServerListening(); - -private: - bool bindSocket(); - protected: /// Accepts all pending connections virtual void incomingConnection(qintptr socketDescriptor); @@ -50,7 +44,6 @@ protected: QString _documentRoot; HTTPRequestHandler* _requestHandler; - QTimer* _isListeningTimer; const quint16 _port; }; From ad0df3a47c8b1801be37c1aa12f768459694289a Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 21 Aug 2015 14:21:49 -0700 Subject: [PATCH 078/103] Make sixense work in dev builds (if you put the library in the documented place). --- libraries/input-plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/input-plugins/CMakeLists.txt b/libraries/input-plugins/CMakeLists.txt index 4428327deb..5d6dae7ad1 100644 --- a/libraries/input-plugins/CMakeLists.txt +++ b/libraries/input-plugins/CMakeLists.txt @@ -6,7 +6,7 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) string(TOLOWER ${EXTERNAL} ${EXTERNAL}_LOWERCASE) - set(${${EXTERNAL}_UPPERCASE}_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/${${EXTERNAL}_LOWERCASE}") + set(${${EXTERNAL}_UPPERCASE}_ROOT_DIR "${CMAKE_SOURCE_DIR}/interface/external/${${EXTERNAL}_LOWERCASE}") endif () endforeach() From 34b3fb1c0c7e2230b55f8f9afee889ecf9be5512 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Aug 2015 14:27:17 -0700 Subject: [PATCH 079/103] Revert "force the HTTPManager to listen on IPv4" This reverts commit 220ad189a04128e1926182b6170e1f46d1cbd6bf. --- .../embedded-webserver/src/HTTPManager.cpp | 27 +++++++++++++------ .../embedded-webserver/src/HTTPManager.h | 7 +++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 946da00a34..72436fc55e 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -29,14 +29,10 @@ HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestH _requestHandler(requestHandler), _port(port) { - qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); - - if (listen(QHostAddress::AnyIPv4, _port)) { - qCDebug(embeddedwebserver) << "TCP socket is listening on" << serverAddress() << "and port" << serverPort(); - } else { - qCritical() << "Failed to open HTTP server socket:" << errorString() << " - forcing exit."; - QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); - } + bindSocket(); + _isListeningTimer = new QTimer(this); + connect(_isListeningTimer, &QTimer::timeout, this, &HTTPManager::isTcpServerListening); + _isListeningTimer->start(SOCKET_CHECK_INTERVAL_IN_MS); } void HTTPManager::incomingConnection(qintptr socketDescriptor) { @@ -166,3 +162,18 @@ bool HTTPManager::requestHandledByRequestHandler(HTTPConnection* connection, con return _requestHandler && _requestHandler->handleHTTPRequest(connection, url); } +void HTTPManager::isTcpServerListening() { + if (!isListening()) { + qCWarning(embeddedwebserver) << "Socket on port " << QString::number(_port) << " is no longer listening"; + bindSocket(); + } +} + +bool HTTPManager::bindSocket() { + qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); + if (!listen(QHostAddress::Any, _port)) { + qCritical() << "Failed to open HTTP server socket:" << errorString() << " can't continue"; + QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); + } + return true; +} \ No newline at end of file diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index 8e1780a631..6375b10205 100644 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -37,6 +37,12 @@ public: bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); +private slots: + void isTcpServerListening(); + +private: + bool bindSocket(); + protected: /// Accepts all pending connections virtual void incomingConnection(qintptr socketDescriptor); @@ -44,6 +50,7 @@ protected: QString _documentRoot; HTTPRequestHandler* _requestHandler; + QTimer* _isListeningTimer; const quint16 _port; }; From 3e4a05a57bf7284537065288605f8ad597f6b226 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Aug 2015 14:30:41 -0700 Subject: [PATCH 080/103] leave the listen check but queue Application quit --- .../embedded-webserver/src/HTTPManager.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 72436fc55e..4498d619cd 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -20,7 +20,6 @@ #include "EmbeddedWebserverLogging.h" #include "HTTPManager.h" -const int SOCKET_ERROR_EXIT_CODE = 2; const int SOCKET_CHECK_INTERVAL_IN_MS = 30000; HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : @@ -30,6 +29,7 @@ HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestH _port(port) { bindSocket(); + _isListeningTimer = new QTimer(this); connect(_isListeningTimer, &QTimer::timeout, this, &HTTPManager::isTcpServerListening); _isListeningTimer->start(SOCKET_CHECK_INTERVAL_IN_MS); @@ -171,9 +171,16 @@ void HTTPManager::isTcpServerListening() { bool HTTPManager::bindSocket() { qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); - if (!listen(QHostAddress::Any, _port)) { + + if (listen(QHostAddress::AnyIPv4, _port)) { + qCDebug(embeddedwebserver) << "TCP socket is listening on" << serverAddress() << "and port" << serverPort(); + + return true; + } else { qCritical() << "Failed to open HTTP server socket:" << errorString() << " can't continue"; - QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); + QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection); + + return false; } - return true; -} \ No newline at end of file + +} From 0fbbfb77d1d66df91e5130613e5fec1b6606129b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Aug 2015 14:42:03 -0700 Subject: [PATCH 081/103] exit with a code to indicate error --- libraries/embedded-webserver/src/HTTPManager.cpp | 10 +++++++--- libraries/embedded-webserver/src/HTTPManager.h | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 4498d619cd..525caf8f3c 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -20,6 +20,7 @@ #include "EmbeddedWebserverLogging.h" #include "HTTPManager.h" +const int SOCKET_ERROR_EXIT_CODE = 2; const int SOCKET_CHECK_INTERVAL_IN_MS = 30000; HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : @@ -172,15 +173,18 @@ void HTTPManager::isTcpServerListening() { bool HTTPManager::bindSocket() { qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); - if (listen(QHostAddress::AnyIPv4, _port)) { + if (false) { qCDebug(embeddedwebserver) << "TCP socket is listening on" << serverAddress() << "and port" << serverPort(); return true; } else { qCritical() << "Failed to open HTTP server socket:" << errorString() << " can't continue"; - QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "queuedExit", Qt::QueuedConnection); return false; } - +} + +void HTTPManager::queuedExit() { + QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); } diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index 6375b10205..03498fbe8d 100644 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -39,6 +39,7 @@ public: private slots: void isTcpServerListening(); + void queuedExit(); private: bool bindSocket(); From 4b95d54955f0264b826cf6a3ca4cc9c7dc29ad84 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Aug 2015 14:55:24 -0700 Subject: [PATCH 082/103] remove the explicit failure test --- libraries/embedded-webserver/src/HTTPManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 525caf8f3c..1f1dfa1735 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -173,7 +173,7 @@ void HTTPManager::isTcpServerListening() { bool HTTPManager::bindSocket() { qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); - if (false) { + if (listen(QHostAddress::AnyIPv4, _port)) { qCDebug(embeddedwebserver) << "TCP socket is listening on" << serverAddress() << "and port" << serverPort(); return true; From b579b996c34a0bd67c8517ed97c8d9e9aa77eae7 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 21 Aug 2015 15:07:39 -0700 Subject: [PATCH 083/103] There was a hidden (and inappropriate) dependency on face model loading. --- interface/src/avatar/Avatar.cpp | 9 ++++----- interface/src/avatar/MyAvatar.cpp | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 19f84018f8..59ae933030 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -469,8 +469,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { * (1.0f - ((float)(now - getHead()->getLookingAtMeStarted())) / (LOOKING_AT_ME_DURATION * (float)USECS_PER_SECOND)); if (alpha > 0.0f) { - QSharedPointer geometry = getHead()->getFaceModel().getGeometry(); - if (geometry) { + QSharedPointer geometry = _skeletonModel.getGeometry(); + if (geometry && geometry->isLoaded()) { const float DEFAULT_EYE_DIAMETER = 0.048f; // Typical human eye const float RADIUS_INCREMENT = 0.005f; Transform transform; @@ -597,13 +597,12 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, floa if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { // render the billboard until both models are loaded renderBillboard(renderArgs); - return; + } else { + getHead()->render(renderArgs, 1.0f, renderFrustum); } getHand()->render(renderArgs, false); } - - getHead()->render(renderArgs, 1.0f, renderFrustum); getHead()->renderLookAts(renderArgs); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index eada41eb29..1fec536082 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1141,7 +1141,7 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName, const g void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel) { - if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { + if (!_skeletonModel.isRenderable()) { return; // wait until all models are loaded } From b7009b46318404484e6b8479a8167ee84f90eadf Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 21 Aug 2015 16:09:06 -0700 Subject: [PATCH 084/103] Fix for animation resources The problem was that the invokeMethod between the AnimationReader thread and the main thread was failing, because FBXGeometry* wasn't a registered meta type. So, I ended up normalizing the AnimationReader class to be more like GeometryReader, in that it uses singles and slots to communicate success and failure, rather then invokeMethod. --- examples/libraries/progressDialog.js | 4 +- libraries/animation/src/AnimationCache.cpp | 71 +++++++++++++-------- libraries/animation/src/AnimationCache.h | 24 ++++++- libraries/animation/src/AnimationHandle.cpp | 1 + libraries/fbx/src/FBXReader.cpp | 8 ++- libraries/render-utils/src/GeometryCache.h | 9 --- 6 files changed, 77 insertions(+), 40 deletions(-) diff --git a/examples/libraries/progressDialog.js b/examples/libraries/progressDialog.js index 7d3b1f88e2..4cb2644004 100644 --- a/examples/libraries/progressDialog.js +++ b/examples/libraries/progressDialog.js @@ -8,6 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/"; progressDialog = (function () { var that = {}, @@ -142,4 +144,4 @@ progressDialog = (function () { that.cleanup = cleanup; return that; -}()); \ No newline at end of file +}()); diff --git a/libraries/animation/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp index 7f3f393a8b..c9514782ee 100644 --- a/libraries/animation/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -39,35 +39,44 @@ QSharedPointer AnimationCache::createResource(const QUrl& url, const Q return QSharedPointer(new Animation(url), &Resource::allReferencesCleared); } -Animation::Animation(const QUrl& url) : Resource(url) {} - -class AnimationReader : public QRunnable { -public: - - AnimationReader(const QWeakPointer& animation, QNetworkReply* reply); - - virtual void run(); - -private: - - QWeakPointer _animation; - QNetworkReply* _reply; -}; - -AnimationReader::AnimationReader(const QWeakPointer& animation, QNetworkReply* reply) : - _animation(animation), +AnimationReader::AnimationReader(const QUrl& url, QNetworkReply* reply) : + _url(url), _reply(reply) { } void AnimationReader::run() { - QSharedPointer animation = _animation.toStrongRef(); - if (!animation.isNull()) { - QMetaObject::invokeMethod(animation.data(), "setGeometry", - Q_ARG(FBXGeometry*, readFBX(_reply->readAll(), QVariantHash(), _reply->property("url").toString()))); + try { + if (!_reply) { + throw QString("Reply is NULL ?!"); + } + QString urlname = _url.path().toLower(); + bool urlValid = true; + urlValid &= !urlname.isEmpty(); + urlValid &= !_url.path().isEmpty(); + urlValid &= _url.path().toLower().endsWith(".fbx"); + + if (urlValid) { + // Let's read the binaries from the network + FBXGeometry* fbxgeo = nullptr; + if (_url.path().toLower().endsWith(".fbx")) { + fbxgeo = readFBX(_reply, QVariantHash(), _url.path()); + } else { + QString errorStr("usupported format"); + emit onError(299, errorStr); + } + emit onSuccess(fbxgeo); + } else { + throw QString("url is invalid"); + } + + } catch (const QString& error) { + emit onError(299, error); } _reply->deleteLater(); } +Animation::Animation(const QUrl& url) : Resource(url) {} + bool Animation::isLoaded() const { return _loaded && _geometry; } @@ -100,17 +109,27 @@ const QVector& Animation::getFramesReference() const { return _geometry->animationFrames; } -void Animation::setGeometry(FBXGeometry* geometry) { +void Animation::downloadFinished(QNetworkReply* reply) { + // parse the animation/fbx file on a background thread. + AnimationReader* animationReader = new AnimationReader(reply->url(), reply); + connect(animationReader, SIGNAL(onSuccess(FBXGeometry*)), SLOT(animationParseSuccess(FBXGeometry*))); + connect(animationReader, SIGNAL(onError(int, QString)), SLOT(animationParseError(int, QString))); + QThreadPool::globalInstance()->start(animationReader); +} + +void Animation::animationParseSuccess(FBXGeometry* geometry) { + + qCDebug(animation) << "Animation parse success" << _url.toDisplayString(); + _geometry.reset(geometry); finishedLoading(true); } -void Animation::downloadFinished(QNetworkReply* reply) { - // send the reader off to the thread pool - QThreadPool::globalInstance()->start(new AnimationReader(_self, reply)); +void Animation::animationParseError(int error, QString str) { + qCCritical(animation) << "Animation failure parsing " << _url.toDisplayString() << "code =" << error << str; + emit failed(QNetworkReply::UnknownContentError); } - AnimationDetails::AnimationDetails() : role(), url(), fps(0.0f), priority(0.0f), loop(false), hold(false), startAutomatically(false), firstFrame(0.0f), lastFrame(0.0f), running(false), frameIndex(0.0f) diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index 3ff5957fa2..af07eda9a4 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -12,6 +12,7 @@ #ifndef hifi_AnimationCache_h #define hifi_AnimationCache_h +#include #include #include @@ -64,16 +65,33 @@ public: const QVector& getFramesReference() const; protected: - - Q_INVOKABLE void setGeometry(FBXGeometry* geometry); - virtual void downloadFinished(QNetworkReply* reply); +protected slots: + void animationParseSuccess(FBXGeometry* geometry); + void animationParseError(int error, QString str); + private: std::unique_ptr _geometry; }; +/// Reads geometry in a worker thread. +class AnimationReader : public QObject, public QRunnable { + Q_OBJECT + +public: + AnimationReader(const QUrl& url, QNetworkReply* reply); + virtual void run(); + +signals: + void onSuccess(FBXGeometry* geometry); + void onError(int error, QString str); + +private: + QUrl _url; + QNetworkReply* _reply; +}; class AnimationDetails { public: diff --git a/libraries/animation/src/AnimationHandle.cpp b/libraries/animation/src/AnimationHandle.cpp index ebf0e29b97..1f4c886a06 100644 --- a/libraries/animation/src/AnimationHandle.cpp +++ b/libraries/animation/src/AnimationHandle.cpp @@ -15,6 +15,7 @@ void AnimationHandle::setURL(const QUrl& url) { if (_url != url) { _animation = DependencyManager::get()->getAnimation(_url = url); + _animation->ensureLoading(); QObject::connect(_animation.data(), &Resource::onRefresh, this, &AnimationHandle::clearJoints); _jointMappings.clear(); } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index f0d13f8792..320b93ff06 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -182,7 +182,7 @@ QString FBXGeometry::getModelNameOfMesh(int meshIndex) const { static int fbxGeometryMetaTypeId = qRegisterMetaType(); static int fbxAnimationFrameMetaTypeId = qRegisterMetaType(); -static int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType >(); +static int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType>(); template int streamSize() { return sizeof(T); @@ -1858,12 +1858,18 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping foreach (const FBXNode& subobject, object.children) { if (subobject.name == "RelativeFilename") { QByteArray filename = subobject.properties.at(0).toByteArray(); + qCDebug(modelformat) << "AJT: RelativeFilename, filename =" << filename; filename = fileOnUrl(filename, url); + qCDebug(modelformat) << "AJT: url = " << url; + qCDebug(modelformat) << "AJT: filename2 = " << filename; + textureFilenames.insert(getID(object.properties), filename); } else if (subobject.name == "TextureName") { // trim the name from the timestamp QString name = QString(subobject.properties.at(0).toByteArray()); + qCDebug(modelformat) << "AJT: TextureName, name =" << name; name = name.left(name.indexOf('[')); + qCDebug(modelformat) << "AJT: name2 =" << name; textureNames.insert(getID(object.properties), name); } else if (subobject.name == "Texture_Alpha_Source") { tex.assign(tex.alphaSource, subobject.properties.at(0).value()); diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 774df1561c..0b692d81f7 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -21,8 +21,6 @@ #include "FBXReader.h" #include "OBJReader.h" -#include - #include #include @@ -383,20 +381,13 @@ protected: /// Reads geometry in a worker thread. class GeometryReader : public QObject, public QRunnable { Q_OBJECT - public: - GeometryReader(const QUrl& url, QNetworkReply* reply, const QVariantHash& mapping); - virtual void run(); - signals: void onSuccess(FBXGeometry* geometry); void onError(int error, QString str); - private: - - QWeakPointer _geometry; QUrl _url; QNetworkReply* _reply; QVariantHash _mapping; From 518cf3be1504234eb0dc22906876e292b2186f57 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Fri, 21 Aug 2015 16:28:38 -0700 Subject: [PATCH 085/103] Improve the light attenuation formulae --- libraries/model/src/model/Light.cpp | 11 +++++++++-- libraries/model/src/model/Light.h | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libraries/model/src/model/Light.cpp b/libraries/model/src/model/Light.cpp index 525f68e640..823be727a4 100755 --- a/libraries/model/src/model/Light.cpp +++ b/libraries/model/src/model/Light.cpp @@ -58,10 +58,12 @@ const Vec3& Light::getDirection() const { void Light::setColor(const Color& color) { editSchema()._color = color; + updateLightRadius(); } void Light::setIntensity(float intensity) { editSchema()._intensity = intensity; + updateLightRadius(); } void Light::setAmbientIntensity(float intensity) { @@ -72,9 +74,14 @@ void Light::setMaximumRadius(float radius) { if (radius <= 0.f) { radius = 1.0f; } + editSchema()._attenuation.w = radius; + updateLightRadius(); +} + +void Light::updateLightRadius() { float CutOffIntensityRatio = 0.05f; - float surfaceRadius = radius / (sqrtf(1.0f / CutOffIntensityRatio) - 1.0f); - editSchema()._attenuation = Vec4(surfaceRadius, 1.0f/surfaceRadius, CutOffIntensityRatio, radius); + float surfaceRadius = getMaximumRadius() / (sqrtf((getIntensity() * std::max(std::max(getColor().x, getColor().y), getColor().z)) / CutOffIntensityRatio) - 1.0f); + editSchema()._attenuation = Vec4(surfaceRadius, 1.0f/surfaceRadius, CutOffIntensityRatio, getMaximumRadius()); } #include diff --git a/libraries/model/src/model/Light.h b/libraries/model/src/model/Light.h index 1ed07a942c..3fbaba75bf 100755 --- a/libraries/model/src/model/Light.h +++ b/libraries/model/src/model/Light.h @@ -127,6 +127,8 @@ protected: const Schema& getSchema() const { return _schemaBuffer.get(); } Schema& editSchema() { return _schemaBuffer.edit(); } + + void updateLightRadius(); }; typedef std::shared_ptr< Light > LightPointer; From f512205f6f22b26eb7a864e2eeb27c1fee4d7468 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 22 Aug 2015 01:30:07 +0200 Subject: [PATCH 086/103] add ability to include scripts from a relative path in assignment-client (for persistent scripts only) --- assignment-client/src/Agent.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 4c3958c878..300976f81c 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -181,6 +181,10 @@ void Agent::run() { // register ourselves to the script engine _scriptEngine.registerGlobalObject("Agent", this); + if (!_payload.isEmpty()) { + _scriptEngine.setParentURL(_payload); + } + _scriptEngine.init(); // must be done before we set up the viewers _scriptEngine.registerGlobalObject("SoundCache", DependencyManager::get().data()); From 9edba451eb989f68d9190887566dc610a0524027 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 22 Aug 2015 01:33:22 +0200 Subject: [PATCH 087/103] - remove webSockets from List after disconnect. - leave it up to the QtScript to delete the WebSocket / WebSocketServer objects --- libraries/script-engine/src/WebSocketClass.cpp | 6 ++---- libraries/script-engine/src/WebSocketClass.h | 2 ++ libraries/script-engine/src/WebSocketServerClass.cpp | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 7c7670fcce..8341d65d81 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -42,9 +42,7 @@ QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* if (context->argumentCount() > 0) { url = context->argument(0).toString(); } - auto webSocketClass = new WebSocketClass(engine, url); - connect(static_cast(engine), &ScriptEngine::finished, webSocketClass, &QObject::deleteLater); - return engine->newQObject(webSocketClass); + return engine->newQObject(new WebSocketClass(engine, url), QScriptEngine::ScriptOwnership); } WebSocketClass::~WebSocketClass() { @@ -111,7 +109,7 @@ void qWSCloseCodeFromScriptValue(const QScriptValue &object, QWebSocketProtocol: } QScriptValue webSocketToScriptValue(QScriptEngine* engine, WebSocketClass* const &in) { - return engine->newQObject(in); + return engine->newQObject(in, QScriptEngine::ScriptOwnership); } void webSocketFromScriptValue(const QScriptValue &object, WebSocketClass* &out) { diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index e053257e3d..b87d578843 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -50,6 +50,8 @@ public: CLOSED }; + QWebSocket* getWebSocket() { return _webSocket; } + int getConnecting() const { return CONNECTING; }; int getOpen() const { return OPEN; }; int getClosing() const { return CLOSING; }; diff --git a/libraries/script-engine/src/WebSocketServerClass.cpp b/libraries/script-engine/src/WebSocketServerClass.cpp index 045b39d800..3b3a02a7c9 100644 --- a/libraries/script-engine/src/WebSocketServerClass.cpp +++ b/libraries/script-engine/src/WebSocketServerClass.cpp @@ -40,9 +40,7 @@ QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptE serverName = serverNameOption.toString(); } } - auto webSocketServerClass = new WebSocketServerClass(engine, serverName, port); - connect(static_cast(engine), &ScriptEngine::finished, webSocketServerClass, &QObject::deleteLater); - return engine->newQObject(webSocketServerClass); + return engine->newQObject(new WebSocketServerClass(engine, serverName, port), QScriptEngine::ScriptOwnership); } WebSocketServerClass::~WebSocketServerClass() { @@ -55,6 +53,9 @@ WebSocketServerClass::~WebSocketServerClass() { void WebSocketServerClass::onNewConnection() { WebSocketClass* newClient = new WebSocketClass(_engine, _webSocketServer.nextPendingConnection()); _clients << newClient; + connect(newClient->getWebSocket(), &QWebSocket::disconnected, [newClient, this]() { + _clients.removeOne(newClient); + }); emit newConnection(newClient); } From 5e31d423b9a563c559c32c15423f7165ae83c4e9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 21 Aug 2015 16:55:08 -0700 Subject: [PATCH 088/103] Removed some debugging statements --- libraries/fbx/src/FBXReader.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 320b93ff06..f0d13f8792 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -182,7 +182,7 @@ QString FBXGeometry::getModelNameOfMesh(int meshIndex) const { static int fbxGeometryMetaTypeId = qRegisterMetaType(); static int fbxAnimationFrameMetaTypeId = qRegisterMetaType(); -static int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType>(); +static int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType >(); template int streamSize() { return sizeof(T); @@ -1858,18 +1858,12 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping foreach (const FBXNode& subobject, object.children) { if (subobject.name == "RelativeFilename") { QByteArray filename = subobject.properties.at(0).toByteArray(); - qCDebug(modelformat) << "AJT: RelativeFilename, filename =" << filename; filename = fileOnUrl(filename, url); - qCDebug(modelformat) << "AJT: url = " << url; - qCDebug(modelformat) << "AJT: filename2 = " << filename; - textureFilenames.insert(getID(object.properties), filename); } else if (subobject.name == "TextureName") { // trim the name from the timestamp QString name = QString(subobject.properties.at(0).toByteArray()); - qCDebug(modelformat) << "AJT: TextureName, name =" << name; name = name.left(name.indexOf('[')); - qCDebug(modelformat) << "AJT: name2 =" << name; textureNames.insert(getID(object.properties), name); } else if (subobject.name == "Texture_Alpha_Source") { tex.assign(tex.alphaSource, subobject.properties.at(0).value()); From 0e255f4f3ee785b6c35799423e0dad447e2291a1 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 22 Aug 2015 01:59:44 +0200 Subject: [PATCH 089/103] properly exposed WebSocketClass::ReadyState to the ScriptEngine --- libraries/script-engine/src/ScriptEngine.cpp | 1 + .../script-engine/src/WebSocketClass.cpp | 8 ++++++ libraries/script-engine/src/WebSocketClass.h | 25 +++++++++++-------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 32a2a5b6f0..b42daa710a 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -359,6 +359,7 @@ void ScriptEngine::init() { qScriptRegisterMetaType(this, animationDetailsToScriptValue, animationDetailsFromScriptValue); qScriptRegisterMetaType(this, webSocketToScriptValue, webSocketFromScriptValue); qScriptRegisterMetaType(this, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue); + qScriptRegisterMetaType(this, wscReadyStateToScriptValue, wscReadyStateFromScriptValue); registerGlobalObject("Script", this); registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 8341d65d81..fd923965cf 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -115,3 +115,11 @@ QScriptValue webSocketToScriptValue(QScriptEngine* engine, WebSocketClass* const void webSocketFromScriptValue(const QScriptValue &object, WebSocketClass* &out) { out = qobject_cast(object.toQObject()); } + +QScriptValue wscReadyStateToScriptValue(QScriptEngine* engine, const WebSocketClass::ReadyState& readyState) { + return readyState; +} + +void wscReadyStateFromScriptValue(const QScriptValue& object, WebSocketClass::ReadyState& readyState) { + readyState = (WebSocketClass::ReadyState)object.toUInt16(); +} diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index b87d578843..9f14b44ba0 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -28,13 +28,13 @@ class WebSocketClass : public QObject { Q_PROPERTY(QScriptValue onopen READ getOnOpen WRITE setOnOpen) Q_PROPERTY(QString protocol READ getProtocol) - Q_PROPERTY(uint readyState READ getReadyState) + Q_PROPERTY(WebSocketClass::ReadyState readyState READ getReadyState) Q_PROPERTY(QString url READ getURL) - Q_PROPERTY(int CONNECTING READ getConnecting CONSTANT) - Q_PROPERTY(int OPEN READ getOpen CONSTANT) - Q_PROPERTY(int CLOSING READ getClosing CONSTANT) - Q_PROPERTY(int CLOSED READ getClosed CONSTANT) + Q_PROPERTY(WebSocketClass::ReadyState CONNECTING READ getConnecting CONSTANT) + Q_PROPERTY(WebSocketClass::ReadyState OPEN READ getOpen CONSTANT) + Q_PROPERTY(WebSocketClass::ReadyState CLOSING READ getClosing CONSTANT) + Q_PROPERTY(WebSocketClass::ReadyState CLOSED READ getClosed CONSTANT) public: WebSocketClass(QScriptEngine* engine, QString url); @@ -52,10 +52,10 @@ public: QWebSocket* getWebSocket() { return _webSocket; } - int getConnecting() const { return CONNECTING; }; - int getOpen() const { return OPEN; }; - int getClosing() const { return CLOSING; }; - int getClosed() const { return CLOSED; }; + ReadyState getConnecting() const { return CONNECTING; }; + ReadyState getOpen() const { return OPEN; }; + ReadyState getClosing() const { return CLOSING; }; + ReadyState getClosed() const { return CLOSED; }; void setBinaryType(QString binaryType) { _binaryType = binaryType; } QString getBinaryType() { return _binaryType; } @@ -70,7 +70,7 @@ public: QString getURL() { return _webSocket->requestUrl().toDisplayString(); } - uint getReadyState() { + ReadyState getReadyState() { switch (_webSocket->state()) { case QAbstractSocket::SocketState::HostLookupState: case QAbstractSocket::SocketState::ConnectingState: @@ -125,10 +125,15 @@ private slots: }; Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode); +Q_DECLARE_METATYPE(WebSocketClass::ReadyState); + QScriptValue qWSCloseCodeToScriptValue(QScriptEngine* engine, const QWebSocketProtocol::CloseCode& closeCode); void qWSCloseCodeFromScriptValue(const QScriptValue& object, QWebSocketProtocol::CloseCode& closeCode); QScriptValue webSocketToScriptValue(QScriptEngine* engine, WebSocketClass* const &in); void webSocketFromScriptValue(const QScriptValue &object, WebSocketClass* &out); +QScriptValue wscReadyStateToScriptValue(QScriptEngine* engine, const WebSocketClass::ReadyState& readyState); +void wscReadyStateFromScriptValue(const QScriptValue& object, WebSocketClass::ReadyState& readyState); + #endif // hifi_WebSocketClass_h From d84cf1859c25e9e94d2613da9367f0d53ffd38a8 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 22 Aug 2015 02:18:21 +0200 Subject: [PATCH 090/103] hook up the WebSocket error event directly --- libraries/script-engine/src/WebSocketClass.cpp | 16 +++++++++------- libraries/script-engine/src/WebSocketClass.h | 1 + 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index fd923965cf..c844dc3582 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -34,6 +34,8 @@ void WebSocketClass::initialize() { connect(_webSocket, &QWebSocket::disconnected, this, &WebSocketClass::handleOnClose); connect(_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketClass::handleOnMessage); connect(_webSocket, &QWebSocket::connected, this, &WebSocketClass::handleOnOpen); + connect(_webSocket, static_cast(&QWebSocket::error), this, + &WebSocketClass::handleOnError); _binaryType = QStringLiteral("blob"); } @@ -66,13 +68,7 @@ void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode, QString reas } void WebSocketClass::handleOnClose() { - bool hasError = false; - if (_webSocket->error() != QAbstractSocket::UnknownSocketError) { - hasError = true; - if (_onErrorEvent.isFunction()) { - _onErrorEvent.call(); - } - } + bool hasError = (_webSocket->error() != QAbstractSocket::UnknownSocketError); if (_onCloseEvent.isFunction()) { QScriptValueList args; QScriptValue arg = _engine->newObject(); @@ -84,6 +80,12 @@ void WebSocketClass::handleOnClose() { } } +void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) { + if (_onErrorEvent.isFunction()) { + _onErrorEvent.call(); + } +} + void WebSocketClass::handleOnMessage(const QString& message) { if (_onMessageEvent.isFunction()) { QScriptValueList args; diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index 9f14b44ba0..8ba8ecf362 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -119,6 +119,7 @@ private: private slots: void handleOnClose(); + void handleOnError(QAbstractSocket::SocketError error); void handleOnMessage(const QString& message); void handleOnOpen(); From 67e945b8847a833f8ad9ac0800984ecafc40ee98 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 22 Aug 2015 03:32:53 +0200 Subject: [PATCH 091/103] fix unit test --- examples/utilities/diagnostics/testWebSocket.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/utilities/diagnostics/testWebSocket.js b/examples/utilities/diagnostics/testWebSocket.js index 4cf6c3682b..c356fd99a8 100644 --- a/examples/utilities/diagnostics/testWebSocket.js +++ b/examples/utilities/diagnostics/testWebSocket.js @@ -51,11 +51,10 @@ unitTests.addTest("Test WebSocket invalid URL", function(finished) { var hadError = false; webSocket.onerror = this.registerCallbackFunction(function() { hadError = true; + _this.done(); }); webSocket.onclose = this.registerCallbackFunction(function(event) { - _this.assertEquals(true, hadError, "hadError should be true"); _this.assertEquals(webSocket.CLOSED, webSocket.readyState, "readyState should be CLOSED"); - _this.done(); }); this.assertEquals(webSocket.CONNECTING, webSocket.readyState, "readyState should be CONNECTING"); this.assertEquals(WEBSOCKET_INVALID_URL, webSocket.url, "url should be '" + WEBSOCKET_INVALID_URL + "'"); From 04e7084743a1c50fac9b1040c6b3878b91aa9601 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 21 Aug 2015 21:43:21 -0700 Subject: [PATCH 092/103] Resource fix for textures in FBX files. The baseTexturePath url for textures in FBXGeometry should default to the same base url as the fbx file itself. This error was introduced in my recent refactoring. Textures embedded in FBXGeometries should be un-affected by this change. --- libraries/render-utils/src/GeometryCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index f48ceb9b62..c778376e92 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1723,7 +1723,7 @@ void GeometryReader::run() { NetworkGeometry::NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl) : _url(url), _mapping(mapping), - _textureBaseUrl(textureBaseUrl) { + _textureBaseUrl(textureBaseUrl.isValid() ? textureBaseUrl : url) { if (delayLoad) { _state = DelayState; From 80d3ae68c23c99fbd6b1922a8463343716040a93 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 21 Aug 2015 22:11:53 -0700 Subject: [PATCH 093/103] =?UTF-8?q?Removed=20redundant=20ends=20with=20?= =?UTF-8?q?=E2=80=98fbx=E2=80=99=20check.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libraries/animation/src/AnimationCache.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/animation/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp index c9514782ee..e7a4fb50a3 100644 --- a/libraries/animation/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -53,10 +53,9 @@ void AnimationReader::run() { bool urlValid = true; urlValid &= !urlname.isEmpty(); urlValid &= !_url.path().isEmpty(); - urlValid &= _url.path().toLower().endsWith(".fbx"); if (urlValid) { - // Let's read the binaries from the network + // Parse the FBX directly from the QNetworkReply FBXGeometry* fbxgeo = nullptr; if (_url.path().toLower().endsWith(".fbx")) { fbxgeo = readFBX(_reply, QVariantHash(), _url.path()); From d3135dcfa6167052e6a34520e0f5e64db7a4ba0f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 20 Aug 2015 17:04:53 -0700 Subject: [PATCH 094/103] Stereo rendering work --- interface/src/Application.cpp | 156 +++-------------- interface/src/Application.h | 26 +-- interface/src/Menu.cpp | 4 +- interface/src/PluginContainerProxy.cpp | 161 ++++++++++++++++++ interface/src/PluginContainerProxy.h | 30 ++++ .../Basic2DWindowOpenGLDisplayPlugin.cpp | 1 - .../src/display-plugins/DisplayPlugin.cpp | 8 +- .../src/display-plugins/DisplayPlugin.h | 2 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 2 +- .../src/display-plugins/OpenGLDisplayPlugin.h | 1 + .../WindowOpenGLDisplayPlugin.cpp | 5 + .../WindowOpenGLDisplayPlugin.h | 1 + .../stereo/InterleavedStereoDisplayPlugin.cpp | 53 +++++- .../stereo/InterleavedStereoDisplayPlugin.h | 3 + .../stereo/SideBySideStereoDisplayPlugin.cpp | 23 +-- .../stereo/SideBySideStereoDisplayPlugin.h | 3 +- .../stereo/StereoDisplayPlugin.cpp | 44 ++++- .../stereo/StereoDisplayPlugin.h | 2 + libraries/plugins/src/plugins/Forward.h | 23 +++ libraries/plugins/src/plugins/Plugin.cpp | 7 + libraries/plugins/src/plugins/Plugin.h | 9 +- .../plugins/src/plugins/PluginContainer.h | 7 +- .../plugins/src/plugins/PluginManager.cpp | 8 +- libraries/plugins/src/plugins/PluginManager.h | 20 +-- 24 files changed, 397 insertions(+), 202 deletions(-) create mode 100644 interface/src/PluginContainerProxy.cpp create mode 100644 interface/src/PluginContainerProxy.h create mode 100644 libraries/plugins/src/plugins/Forward.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 50d60b4cb3..0f3282582f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -58,6 +58,8 @@ #include #include #include +#include +#include #include #include @@ -148,6 +150,8 @@ #include "ui/overlays/Cube3DOverlay.h" +#include "PluginContainerProxy.h" + // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. #if defined(Q_OS_WIN) @@ -301,7 +305,7 @@ bool setupEssentials(int& argc, char** argv) { // continuing to overburden Application.cpp Cube3DOverlay* _keyboardFocusHighlight{ nullptr }; int _keyboardFocusHighlightID{ -1 }; - +PluginContainer* _pluginContainer; Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : QApplication(argc, argv), @@ -351,7 +355,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _applicationOverlay() { setInstance(this); - Plugin::setContainer(this); + + _pluginContainer = new PluginContainerProxy(); + Plugin::setContainer(_pluginContainer); #ifdef Q_OS_WIN installNativeEventFilter(&MyNativeEventFilter::getInstance()); #endif @@ -1262,6 +1268,7 @@ void Application::resizeGL() { // Possible change in aspect ratio loadViewFrustum(_myCamera, _viewFrustum); float fov = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES); + // FIXME the aspect ratio for stereo displays is incorrect based on this. float aspectRatio = aspect(_renderResolution); _myCamera.setProjection(glm::perspective(fov, aspectRatio, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); } @@ -1421,7 +1428,15 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_Enter: case Qt::Key_Return: - Menu::getInstance()->triggerOption(MenuOption::AddressBar); + if (isOption) { + if (_window->isFullScreen()) { + _pluginContainer->unsetFullscreen(); + } else { + _pluginContainer->setFullscreen(nullptr); + } + } else { + Menu::getInstance()->triggerOption(MenuOption::AddressBar); + } break; case Qt::Key_B: @@ -4617,10 +4632,6 @@ void Application::checkSkeleton() { } } -bool Application::isForeground() { - return _isForeground && !getWindow()->isMinimized(); -} - void Application::activeChanged(Qt::ApplicationState state) { switch (state) { case Qt::ApplicationActive: @@ -4726,6 +4737,10 @@ const DisplayPlugin * Application::getActiveDisplayPlugin() const { return ((Application*)this)->getActiveDisplayPlugin(); } +bool _activatingDisplayPlugin{ false }; +QVector> _currentDisplayPluginActions; +QVector> _currentInputPluginActions; + static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) { auto menu = Menu::getInstance(); @@ -4747,9 +4762,6 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti Q_ASSERT(menu->menuItemExists(MenuOption::OutputMenu, name)); } -static QVector> _currentDisplayPluginActions; -static bool _activatingDisplayPlugin{false}; - void Application::updateDisplayMode() { auto menu = Menu::getInstance(); auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); @@ -4816,7 +4828,7 @@ void Application::updateDisplayMode() { // Only show the hmd tools after the correct plugin has // been activated so that it's UI is setup correctly if (newPluginWantsHMDTools) { - showDisplayPluginsTools(); + _pluginContainer->showDisplayPluginsTools(); } if (oldDisplayPlugin) { @@ -4834,9 +4846,6 @@ void Application::updateDisplayMode() { Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin"); } -static QVector> _currentInputPluginActions; - - static void addInputPluginToMenu(InputPluginPointer inputPlugin, bool active = false) { auto menu = Menu::getInstance(); QString name = inputPlugin->getName(); @@ -4910,42 +4919,6 @@ void Application::updateInputModes() { //} } -void Application::addMenu(const QString& menuName) { - Menu::getInstance()->addMenu(menuName); -} - -void Application::removeMenu(const QString& menuName) { - Menu::getInstance()->removeMenu(menuName); -} - -void Application::addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable, bool checked, const QString& groupName) { - auto menu = Menu::getInstance(); - MenuWrapper* parentItem = menu->getMenu(path); - QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name); - connect(action, &QAction::triggered, [=] { - onClicked(action->isChecked()); - }); - action->setCheckable(checkable); - action->setChecked(checked); - if (_activatingDisplayPlugin) { - _currentDisplayPluginActions.push_back({ path, name }); - } else { - _currentInputPluginActions.push_back({ path, name }); - } -} - -void Application::removeMenuItem(const QString& menuName, const QString& menuItem) { - Menu::getInstance()->removeMenuItem(menuName, menuItem); -} - -bool Application::isOptionChecked(const QString& name) { - return Menu::getInstance()->isOptionChecked(name); -} - -void Application::setIsOptionChecked(const QString& path, bool checked) { - Menu::getInstance()->setIsOptionChecked(path, checked); -} - mat4 Application::getEyeProjection(int eye) const { if (isHMDMode()) { return getActiveDisplayPlugin()->getProjection((Eye)eye, _viewFrustum.getProjection()); @@ -4978,89 +4951,6 @@ mat4 Application::getHMDSensorPose() const { return mat4(); } -// FIXME there is a bug in the fullscreen setting, where leaving -// fullscreen does not restore the window frame, making it difficult -// or impossible to move or size the window. -// Additionally, setting fullscreen isn't hiding the menu on windows -// make it useless for stereoscopic modes. -void Application::setFullscreen(const QScreen* target) { - if (!_window->isFullScreen()) { - _savedGeometry = _window->geometry(); - } -#ifdef Q_OS_MAC - _window->setGeometry(target->availableGeometry()); -#endif - _window->windowHandle()->setScreen((QScreen*)target); - _window->showFullScreen(); - -#ifndef Q_OS_MAC - // also hide the QMainWindow's menuBar - QMenuBar* menuBar = _window->menuBar(); - if (menuBar) { - menuBar->setVisible(false); - } -#endif -} - -void Application::unsetFullscreen(const QScreen* avoid) { - _window->showNormal(); - - QRect targetGeometry = _savedGeometry; - if (avoid != nullptr) { - QRect avoidGeometry = avoid->geometry(); - if (avoidGeometry.contains(targetGeometry.topLeft())) { - QScreen* newTarget = primaryScreen(); - if (newTarget == avoid) { - foreach(auto screen, screens()) { - if (screen != avoid) { - newTarget = screen; - break; - } - } - } - targetGeometry = newTarget->availableGeometry(); - } - } -#ifdef Q_OS_MAC - QTimer* timer = new QTimer(); - timer->singleShot(2000, [=] { - _window->setGeometry(targetGeometry); - timer->deleteLater(); - }); -#else - _window->setGeometry(targetGeometry); -#endif - -#ifndef Q_OS_MAC - // also show the QMainWindow's menuBar - QMenuBar* menuBar = _window->menuBar(); - if (menuBar) { - menuBar->setVisible(true); - } -#endif -} - - -void Application::showDisplayPluginsTools() { - DependencyManager::get()->hmdTools(true); -} - -QGLWidget* Application::getPrimarySurface() { - return _glWidget; -} - -void Application::setActiveDisplayPlugin(const QString& pluginName) { - auto menu = Menu::getInstance(); - foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { - QString name = displayPlugin->getName(); - QAction* action = menu->getActionForOption(name); - if (pluginName == name) { - action->setChecked(true); - } - } - updateDisplayMode(); -} - void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index) { PalmData* palm; bool foundHand = false; diff --git a/interface/src/Application.h b/interface/src/Application.h index a1765109ce..6394aa12d0 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -26,18 +26,18 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include #include #include -#include -#include #include #include "AudioClient.h" @@ -50,7 +50,6 @@ #include "Stars.h" #include "avatar/Avatar.h" #include "avatar/MyAvatar.h" -#include #include "scripting/ControllerScriptingInterface.h" #include "scripting/DialogsManagerScriptingInterface.h" #include "scripting/WebWindowClass.h" @@ -132,7 +131,7 @@ class Application; typedef bool (Application::* AcceptURLMethod)(const QString &); -class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface, PluginContainer { +class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface { Q_OBJECT friend class OctreePacketProcessor; @@ -280,23 +279,10 @@ public: virtual void endOverrideEnvironmentData() { _environment.endOverride(); } virtual qreal getDevicePixelRatio(); - // Plugin container support - virtual void addMenu(const QString& menuName); - virtual void removeMenu(const QString& menuName); - virtual void addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable, bool checked, const QString& groupName); - virtual void removeMenuItem(const QString& menuName, const QString& menuItem); - virtual bool isOptionChecked(const QString& name); - virtual void setIsOptionChecked(const QString& path, bool checked); - virtual void setFullscreen(const QScreen* target) override; - virtual void unsetFullscreen(const QScreen* avoid) override; - virtual void showDisplayPluginsTools() override; - virtual QGLWidget* getPrimarySurface() override; - virtual bool isForeground() override; - void setActiveDisplayPlugin(const QString& pluginName); - DisplayPlugin * getActiveDisplayPlugin(); - const DisplayPlugin * getActiveDisplayPlugin() const; + DisplayPlugin* getActiveDisplayPlugin(); + const DisplayPlugin* getActiveDisplayPlugin() const; public: @@ -691,6 +677,8 @@ private: int _simsPerSecondReport = 0; quint64 _lastSimsPerSecondUpdate = 0; bool _isForeground = true; // starts out assumed to be in foreground + + friend class PluginContainerProxy; }; #endif // hifi_Application_h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 11bc38c85e..f99b3ba579 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -16,11 +16,13 @@ #include #include #include +#include #include #include #include #include + #include "Application.h" #include "AccountManager.h" #include "audio/AudioScope.h" @@ -220,7 +222,7 @@ Menu::Menu() { addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0, qApp, SLOT(packageModel())); - MenuWrapper* displayMenu = addMenu("Display"); + MenuWrapper* displayMenu = addMenu(DisplayPlugin::MENU_PATH); { MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu); QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu); diff --git a/interface/src/PluginContainerProxy.cpp b/interface/src/PluginContainerProxy.cpp new file mode 100644 index 0000000000..e172dbbd9e --- /dev/null +++ b/interface/src/PluginContainerProxy.cpp @@ -0,0 +1,161 @@ +#include "PluginContainerProxy.h" + +#include +#include + +#include +#include +#include + +#include "Application.h" +#include "MainWindow.h" +#include "GLCanvas.h" +#include "ui/DialogsManager.h" + +PluginContainerProxy::PluginContainerProxy() { + Plugin::setContainer(this); +} + +bool PluginContainerProxy::isForeground() { + return qApp->_isForeground && !qApp->getWindow()->isMinimized(); +} + +void PluginContainerProxy::addMenu(const QString& menuName) { + Menu::getInstance()->addMenu(menuName); +} + +void PluginContainerProxy::removeMenu(const QString& menuName) { + Menu::getInstance()->removeMenu(menuName); +} + +extern bool _activatingDisplayPlugin; +extern QVector> _currentDisplayPluginActions; +extern QVector> _currentInputPluginActions; +std::map _exclusiveGroups; + +QAction* PluginContainerProxy::addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable, bool checked, const QString& groupName) { + auto menu = Menu::getInstance(); + MenuWrapper* parentItem = menu->getMenu(path); + QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name); + if (!groupName.isEmpty()) { + QActionGroup* group{ nullptr }; + if (!_exclusiveGroups.count(groupName)) { + group = _exclusiveGroups[groupName] = new QActionGroup(menu); + group->setExclusive(true); + } else { + group = _exclusiveGroups[groupName]; + } + group->addAction(action); + } + connect(action, &QAction::triggered, [=] { + onClicked(action->isChecked()); + }); + action->setCheckable(checkable); + action->setChecked(checked); + if (_activatingDisplayPlugin) { + _currentDisplayPluginActions.push_back({ path, name }); + } else { + _currentInputPluginActions.push_back({ path, name }); + } + return action; +} + +void PluginContainerProxy::removeMenuItem(const QString& menuName, const QString& menuItem) { + Menu::getInstance()->removeMenuItem(menuName, menuItem); +} + +bool PluginContainerProxy::isOptionChecked(const QString& name) { + return Menu::getInstance()->isOptionChecked(name); +} + +void PluginContainerProxy::setIsOptionChecked(const QString& path, bool checked) { + Menu::getInstance()->setIsOptionChecked(path, checked); +} + +// FIXME there is a bug in the fullscreen setting, where leaving +// fullscreen does not restore the window frame, making it difficult +// or impossible to move or size the window. +// Additionally, setting fullscreen isn't hiding the menu on windows +// make it useless for stereoscopic modes. +void PluginContainerProxy::setFullscreen(const QScreen* target, bool hideMenu) { + auto _window = qApp->_window; + if (!_window->isFullScreen()) { + _savedGeometry = _window->geometry(); + } + if (nullptr == target) { + // FIXME target the screen where the window currently is + target = qApp->primaryScreen(); + } + + _window->setGeometry(target->availableGeometry()); + _window->windowHandle()->setScreen((QScreen*)target); + _window->showFullScreen(); + +#ifndef Q_OS_MAC + // also hide the QMainWindow's menuBar + QMenuBar* menuBar = _window->menuBar(); + if (menuBar && hideMenu) { + menuBar->setVisible(false); + } +#endif +} + +void PluginContainerProxy::unsetFullscreen(const QScreen* avoid) { + auto _window = qApp->_window; + _window->showNormal(); + + QRect targetGeometry = _savedGeometry; + if (avoid != nullptr) { + QRect avoidGeometry = avoid->geometry(); + if (avoidGeometry.contains(targetGeometry.topLeft())) { + QScreen* newTarget = qApp->primaryScreen(); + if (newTarget == avoid) { + foreach(auto screen, qApp->screens()) { + if (screen != avoid) { + newTarget = screen; + break; + } + } + } + targetGeometry = newTarget->availableGeometry(); + } + } +#ifdef Q_OS_MAC + QTimer* timer = new QTimer(); + timer->singleShot(2000, [=] { + _window->setGeometry(targetGeometry); + timer->deleteLater(); + }); +#else + _window->setGeometry(targetGeometry); +#endif + +#ifndef Q_OS_MAC + // also show the QMainWindow's menuBar + QMenuBar* menuBar = _window->menuBar(); + if (menuBar) { + menuBar->setVisible(true); + } +#endif +} + + +void PluginContainerProxy::showDisplayPluginsTools() { + DependencyManager::get()->hmdTools(true); +} + +QGLWidget* PluginContainerProxy::getPrimarySurface() { + return qApp->_glWidget; +} + +void Application::setActiveDisplayPlugin(const QString& pluginName) { + auto menu = Menu::getInstance(); + foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { + QString name = displayPlugin->getName(); + QAction* action = menu->getActionForOption(name); + if (pluginName == name) { + action->setChecked(true); + } + } + updateDisplayMode(); +} diff --git a/interface/src/PluginContainerProxy.h b/interface/src/PluginContainerProxy.h new file mode 100644 index 0000000000..e01cabc3b8 --- /dev/null +++ b/interface/src/PluginContainerProxy.h @@ -0,0 +1,30 @@ +#pragma once +#ifndef hifi_PluginContainerProxy_h +#define hifi_PluginContainerProxy_h + +#include +#include + +#include +#include + +class PluginContainerProxy : public QObject, PluginContainer { + Q_OBJECT + PluginContainerProxy(); + virtual void addMenu(const QString& menuName) override; + virtual void removeMenu(const QString& menuName) override; + virtual QAction* addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override; + virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override; + virtual bool isOptionChecked(const QString& name) override; + virtual void setIsOptionChecked(const QString& path, bool checked); + virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = true) override; + virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override; + virtual void showDisplayPluginsTools() override; + virtual QGLWidget* getPrimarySurface() override; + virtual bool isForeground() override; + QRect _savedGeometry{ 10, 120, 800, 600 }; + + friend class Application; +}; + +#endif \ No newline at end of file diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 018a09ff7e..da8deefa74 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -13,7 +13,6 @@ const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display"); -static const QString MENU_PATH = "Display"; static const QString FULLSCREEN = "Fullscreen"; const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const { diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp index cd620d85a4..5840b3cbba 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp @@ -18,6 +18,8 @@ #include "oculus/OculusDisplayPlugin.h" #include "oculus/OculusLegacyDisplayPlugin.h" +const QString DisplayPlugin::MENU_PATH{ "Display" }; + // TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class DisplayPluginList getDisplayPlugins() { DisplayPlugin* PLUGIN_POOL[] = { @@ -27,9 +29,11 @@ DisplayPluginList getDisplayPlugins() { #endif // Stereo modes - // FIXME fix stereo display plugins + + // SBS left/right new SideBySideStereoDisplayPlugin(), - //new InterleavedStereoDisplayPlugin(), + // Interleaved left/right + new InterleavedStereoDisplayPlugin(), // HMDs diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h index 1a4166c0fa..8ee2d698a6 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h @@ -115,7 +115,7 @@ public: virtual void resetSensors() {} virtual float devicePixelRatio() { return 1.0; } - + static const QString MENU_PATH; signals: void recommendedFramebufferSizeChanged(const QSize & size); void requestRender(); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 7269c9410c..0409899739 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -105,7 +105,7 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { void OpenGLDisplayPlugin::display( GLuint finalTexture, const glm::uvec2& sceneSize) { using namespace oglplus; - uvec2 size = getRecommendedRenderSize(); + uvec2 size = getSurfaceSize(); Context::Viewport(size.x, size.y); glBindTexture(GL_TEXTURE_2D, finalTexture); drawUnitQuad(); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 9b6b92d8d4..3152500232 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -33,6 +33,7 @@ public: protected: virtual void customizeContext(); virtual void drawUnitQuad(); + virtual glm::uvec2 getSurfaceSize() const = 0; virtual void makeCurrent() = 0; virtual void doneCurrent() = 0; virtual void swapBuffers() = 0; diff --git a/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.cpp index 658aa2c767..ffea6605af 100644 --- a/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.cpp @@ -16,6 +16,10 @@ WindowOpenGLDisplayPlugin::WindowOpenGLDisplayPlugin() { } glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedRenderSize() const { + return getSurfaceSize(); +} + +glm::uvec2 WindowOpenGLDisplayPlugin::getSurfaceSize() const { uvec2 result; if (_window) { result = toGlm(_window->geometry().size() * _window->devicePixelRatio()); @@ -23,6 +27,7 @@ glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedRenderSize() const { return result; } + glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedUiSize() const { uvec2 result; if (_window) { diff --git a/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.h index c75cf1484c..fc7691fc56 100644 --- a/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.h @@ -21,6 +21,7 @@ public: virtual void deactivate() override; protected: + virtual glm::uvec2 getSurfaceSize() const override final; virtual void makeCurrent() override; virtual void doneCurrent() override; virtual void swapBuffers() override; diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp index 7d017f714d..23bc176d65 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp @@ -17,6 +17,42 @@ #include +static const char * INTERLEAVED_TEXTURED_VS = R"VS(#version 410 core +#pragma line __LINE__ + +in vec3 Position; +in vec2 TexCoord; + +out vec2 vTexCoord; + +void main() { + gl_Position = vec4(Position, 1); + vTexCoord = TexCoord; +} + +)VS"; + +static const char * INTERLEAVED_TEXTURED_FS = R"FS(#version 410 core +#pragma line __LINE__ + +uniform sampler2D sampler; +uniform ivec2 textureSize; + +in vec2 vTexCoord; +out vec4 FragColor; + +void main() { + ivec2 texCoord = ivec2(floor(vTexCoord * textureSize)); + texCoord.x /= 2; + int row = int(floor(gl_FragCoord.y)); + if (row % 2 > 0) { + texCoord.x += (textureSize.x / 2); + } + FragColor = texelFetch(sampler, texCoord, 0); //texture(sampler, texCoord); +} + +)FS"; + const QString InterleavedStereoDisplayPlugin::NAME("Interleaved Stereo Display"); const QString & InterleavedStereoDisplayPlugin::getName() const { @@ -29,5 +65,20 @@ InterleavedStereoDisplayPlugin::InterleavedStereoDisplayPlugin() { void InterleavedStereoDisplayPlugin::customizeContext() { StereoDisplayPlugin::customizeContext(); // Set up the stencil buffers? Or use a custom shader? - + compileProgram(_program, INTERLEAVED_TEXTURED_VS, INTERLEAVED_TEXTURED_FS); } + +glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const { + uvec2 result = WindowOpenGLDisplayPlugin::getRecommendedRenderSize(); + result.x *= 2; + result.y /= 2; + return result; +} + +void InterleavedStereoDisplayPlugin::display( + GLuint finalTexture, const glm::uvec2& sceneSize) { + using namespace oglplus; + _program->Bind(); + Uniform(*_program, "textureSize").SetValue(sceneSize); + WindowOpenGLDisplayPlugin::display(finalTexture, sceneSize); +} \ No newline at end of file diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h index 1dd38efed5..3044d91247 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h @@ -18,6 +18,9 @@ public: // initialize OpenGL context settings needed by the plugin virtual void customizeContext() override; + virtual glm::uvec2 getRecommendedRenderSize() const override; + void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; + private: static const QString NAME; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp index 829385b209..1bdb02fd26 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp @@ -21,9 +21,6 @@ const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo"); -static const QString MENU_PATH = "Display"; -static const QString FULLSCREEN = "Fullscreen"; - const QString & SideBySideStereoDisplayPlugin::getName() const { return NAME; } @@ -31,20 +28,10 @@ const QString & SideBySideStereoDisplayPlugin::getName() const { SideBySideStereoDisplayPlugin::SideBySideStereoDisplayPlugin() { } -void SideBySideStereoDisplayPlugin::activate() { - CONTAINER->addMenu(MENU_PATH); - CONTAINER->addMenuItem(MENU_PATH, FULLSCREEN, - [this](bool clicked) { - if (clicked) { - CONTAINER->setFullscreen(getFullscreenTarget()); - } else { - CONTAINER->unsetFullscreen(); - } - }, true, false); - StereoDisplayPlugin::activate(); +glm::uvec2 SideBySideStereoDisplayPlugin::getRecommendedRenderSize() const { + uvec2 result = WindowOpenGLDisplayPlugin::getRecommendedRenderSize(); + result.x *= 2; + return result; } -// FIXME target the screen the window is currently on -QScreen* SideBySideStereoDisplayPlugin::getFullscreenTarget() { - return qApp->primaryScreen(); -} + diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h index 3a764d9f4e..9f8440227f 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h @@ -16,8 +16,7 @@ class SideBySideStereoDisplayPlugin : public StereoDisplayPlugin { public: SideBySideStereoDisplayPlugin(); virtual const QString& getName() const override; - virtual void activate() override; + virtual glm::uvec2 getRecommendedRenderSize() const override; private: - QScreen* getFullscreenTarget(); static const QString NAME; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index 20613d6a2c..43c3b727c4 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -10,11 +10,14 @@ #include #include +#include #include #include #include #include +#include +#include StereoDisplayPlugin::StereoDisplayPlugin() { } @@ -29,11 +32,14 @@ const float DEFAULT_IPD = 0.064f; const float HALF_DEFAULT_IPD = DEFAULT_IPD / 2.0f; glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const { - // FIXME check for mono eye and provide a combined matrix, needed for proper - // culling // Refer to http://www.nvidia.com/content/gtc-2010/pdfs/2010_gtc2010.pdf on creating // stereo projection matrices. Do NOT use "toe-in", use translation. + if (eye == Mono) { + // FIXME provide a combined matrix, needed for proper culling + return baseProjection; + } + float nearZ = DEFAULT_NEAR_CLIP; // near clipping plane float screenZ = 0.25f; // screen projection plane // FIXME verify this is the right calculation @@ -52,10 +58,36 @@ glm::mat4 StereoDisplayPlugin::getEyePose(Eye eye) const { return glm::translate(mat4(), vec3(modelviewShift, 0, 0)); } +std::vector _screenActions; void StereoDisplayPlugin::activate() { + auto screens = qApp->screens(); + _screenActions.resize(screens.size()); + for (int i = 0; i < screens.size(); ++i) { + auto screen = screens.at(i); + QString name = QString("Screen %1: %2").arg(i + 1).arg(screen->name()); + bool checked = false; + if (screen == qApp->primaryScreen()) { + checked = true; + } + auto action = CONTAINER->addMenuItem(MENU_PATH, name, + [this](bool clicked) { updateScreen(); }, true, checked, "Screens"); + _screenActions[i] = action; + } + CONTAINER->setFullscreen(qApp->primaryScreen()); WindowOpenGLDisplayPlugin::activate(); - // FIXME there is a bug in the fullscreen setting, see - // Application::setFullscreen - //CONTAINER->setFullscreen(qApp->primaryScreen()); - // FIXME Add menu items +} + +void StereoDisplayPlugin::updateScreen() { + for (int i = 0; i < _screenActions.size(); ++i) { + if (_screenActions[i]->isChecked()) { + CONTAINER->setFullscreen(qApp->screens().at(i)); + break; + } + } +} + +void StereoDisplayPlugin::deactivate() { + _screenActions.clear(); + CONTAINER->unsetFullscreen(); + WindowOpenGLDisplayPlugin::deactivate(); } diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h index b0f0414de2..2009c32e1c 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h @@ -17,10 +17,12 @@ public: virtual bool isSupported() const override final; virtual void activate() override; + virtual void deactivate() override; virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; virtual glm::mat4 getEyePose(Eye eye) const override; protected: + void updateScreen(); float _ipd{ 0.064f }; }; diff --git a/libraries/plugins/src/plugins/Forward.h b/libraries/plugins/src/plugins/Forward.h new file mode 100644 index 0000000000..1a9298d13c --- /dev/null +++ b/libraries/plugins/src/plugins/Forward.h @@ -0,0 +1,23 @@ +// +// Created by Bradley Austin Davis on 2015/08/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 +#include + +class DisplayPlugin; +class InputPlugin; +class Plugin; +class PluginContainer; +class PluginManager; + +using DisplayPluginPointer = QSharedPointer; +using DisplayPluginList = QVector; +using InputPluginPointer = QSharedPointer; +using InputPluginList = QVector; diff --git a/libraries/plugins/src/plugins/Plugin.cpp b/libraries/plugins/src/plugins/Plugin.cpp index e0cacda474..ffcc682ebd 100644 --- a/libraries/plugins/src/plugins/Plugin.cpp +++ b/libraries/plugins/src/plugins/Plugin.cpp @@ -1,3 +1,10 @@ +// +// Created by Bradley Austin Davis on 2015/08/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 "Plugin.h" PluginContainer* Plugin::CONTAINER{ nullptr }; diff --git a/libraries/plugins/src/plugins/Plugin.h b/libraries/plugins/src/plugins/Plugin.h index a2edbc8236..eac355b47d 100644 --- a/libraries/plugins/src/plugins/Plugin.h +++ b/libraries/plugins/src/plugins/Plugin.h @@ -1,9 +1,16 @@ +// +// Created by Bradley Austin Davis on 2015/08/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 -class PluginContainer; +#include "Forward.h" class Plugin : public QObject { public: diff --git a/libraries/plugins/src/plugins/PluginContainer.h b/libraries/plugins/src/plugins/PluginContainer.h index 7f6346a181..c2cba23392 100644 --- a/libraries/plugins/src/plugins/PluginContainer.h +++ b/libraries/plugins/src/plugins/PluginContainer.h @@ -7,9 +7,10 @@ // #pragma once -#include #include +#include +class QAction; class QGLWidget; class QScreen; @@ -18,11 +19,11 @@ public: PluginContainer(); virtual void addMenu(const QString& menuName) = 0; virtual void removeMenu(const QString& menuName) = 0; - virtual void addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") = 0; + virtual QAction* addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") = 0; virtual void removeMenuItem(const QString& menuName, const QString& menuItem) = 0; virtual bool isOptionChecked(const QString& name) = 0; virtual void setIsOptionChecked(const QString& path, bool checked) = 0; - virtual void setFullscreen(const QScreen* targetScreen) = 0; + virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = false) = 0; virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) = 0; virtual void showDisplayPluginsTools() = 0; virtual QGLWidget* getPrimarySurface() = 0; diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp index ffee9905a0..3a71700c9e 100644 --- a/libraries/plugins/src/plugins/PluginManager.cpp +++ b/libraries/plugins/src/plugins/PluginManager.cpp @@ -1,7 +1,13 @@ +// +// Created by Bradley Austin Davis on 2015/08/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 "PluginManager.h" #include - PluginManager* PluginManager::getInstance() { static PluginManager _manager; return &_manager; diff --git a/libraries/plugins/src/plugins/PluginManager.h b/libraries/plugins/src/plugins/PluginManager.h index 88dba3366e..b7ec453814 100644 --- a/libraries/plugins/src/plugins/PluginManager.h +++ b/libraries/plugins/src/plugins/PluginManager.h @@ -1,17 +1,13 @@ +// +// Created by Bradley Austin Davis on 2015/08/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 "Plugin.h" -#include -#include -#include - -class DisplayPlugin; -class InputPlugin; - -using DisplayPluginPointer = QSharedPointer; -using DisplayPluginList = QVector; -using InputPluginPointer = QSharedPointer; -using InputPluginList = QVector; +#include "Forward.h" class PluginManager : public QObject { public: From 1f08444369c400d7a1a84ebb91d4faa439bb475a Mon Sep 17 00:00:00 2001 From: Nex Pro Date: Sun, 23 Aug 2015 16:21:20 +0100 Subject: [PATCH 095/103] Modified the world box to render axes in negative space Also Modified renderDashedLine to accept params for dash len and gap len so we can display negative axes as dashed lines with 500mm dashes and gaps. --- interface/src/Util.cpp | 18 +++++++++++++++++- libraries/render-utils/src/GeometryCache.cpp | 20 ++++++++++---------- libraries/render-utils/src/GeometryCache.h | 11 ++++++----- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 3a01367fc7..cb986e06d5 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -35,17 +35,33 @@ using namespace std; void renderWorldBox(gpu::Batch& batch) { auto geometryCache = DependencyManager::get(); - // Show edge of world + // Show center of world static const glm::vec3 red(1.0f, 0.0f, 0.0f); static const glm::vec3 green(0.0f, 1.0f, 0.0f); static const glm::vec3 blue(0.0f, 0.0f, 1.0f); static const glm::vec3 grey(0.5f, 0.5f, 0.5f); + static const glm::vec4 DASHED_RED(1.0f, 0.0f, 0.0f, 1.0f); + static const glm::vec4 DASHED_GREEN(0.0f, 1.0f, 0.0f, 1.0f); + static const glm::vec4 DASHED_BLUE(0.0f, 0.0f, 1.0f, 1.0f); + static const float DASH_LENGTH = 0.5; + static const float GAP_LENGTH = 0.5; auto transform = Transform{}; + batch.setModelTransform(transform); + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(TREE_SCALE, 0.0f, 0.0f), red); + geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-TREE_SCALE, 0.0f, 0.0f), DASHED_RED, + DASH_LENGTH, GAP_LENGTH); + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, TREE_SCALE, 0.0f), green); + geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, -TREE_SCALE, 0.0f), DASHED_GREEN, + DASH_LENGTH, GAP_LENGTH); + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, TREE_SCALE), blue); + geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, -TREE_SCALE), DASHED_BLUE, + DASH_LENGTH, GAP_LENGTH); + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, TREE_SCALE), glm::vec3(TREE_SCALE, 0.0f, TREE_SCALE), grey); geometryCache->renderLine(batch, glm::vec3(TREE_SCALE, 0.0f, TREE_SCALE), glm::vec3(TREE_SCALE, 0.0f, 0.0f), grey); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 31b030f75a..6801ee8cdc 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -64,7 +64,7 @@ void GeometryCache::renderSphere(gpu::Batch& batch, float radius, int slices, in int vertices = slices * (stacks - 1) + 2; int indices = slices * (stacks - 1) * NUM_VERTICES_PER_TRIANGULATED_QUAD; - + if ((registered && (!_registeredSphereVertices.contains(id) || _lastRegisteredSphereVertices[id] != radiusKey)) || (!registered && !_sphereVertices.contains(radiusKey))) { @@ -74,7 +74,7 @@ void GeometryCache::renderSphere(gpu::Batch& batch, float radius, int slices, in qCDebug(renderutils) << "renderSphere()... RELEASING REGISTERED VERTICES BUFFER"; #endif } - + auto verticesBuffer = std::make_shared(); if (registered) { _registeredSphereVertices[id] = verticesBuffer; @@ -128,7 +128,7 @@ void GeometryCache::renderSphere(gpu::Batch& batch, float radius, int slices, in qCDebug(renderutils) << "renderSphere()... REUSING PREVIOUSLY REGISTERED VERTICES BUFFER"; } #endif - + if ((registered && (!_registeredSphereIndices.contains(id) || _lastRegisteredSphereIndices[id] != slicesStacksKey)) || (!registered && !_sphereIndices.contains(slicesStacksKey))) { @@ -1321,7 +1321,9 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } -void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id) { +void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, + const float dash_length, const float gap_length, int id) { + bool registered = (id != UNKNOWN_ID); Vec3PairVec2Pair key(Vec3Pair(start, end), Vec2Pair(glm::vec2(color.x, color.y), glm::vec2(color.z, color.w))); BatchItemDetails& details = registered ? _registeredDashedLines[id] : _dashedLines[key]; @@ -1345,16 +1347,14 @@ void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, ((int(color.w * 255.0f) & 0xFF) << 24); // draw each line segment with appropriate gaps - const float DASH_LENGTH = 0.05f; - const float GAP_LENGTH = 0.025f; - const float SEGMENT_LENGTH = DASH_LENGTH + GAP_LENGTH; + const float SEGMENT_LENGTH = dash_length + gap_length; float length = glm::distance(start, end); float segmentCount = length / SEGMENT_LENGTH; int segmentCountFloor = (int)glm::floor(segmentCount); glm::vec3 segmentVector = (end - start) / segmentCount; - glm::vec3 dashVector = segmentVector / SEGMENT_LENGTH * DASH_LENGTH; - glm::vec3 gapVector = segmentVector / SEGMENT_LENGTH * GAP_LENGTH; + glm::vec3 dashVector = segmentVector / SEGMENT_LENGTH * dash_length; + glm::vec3 gapVector = segmentVector / SEGMENT_LENGTH * gap_length; const int FLOATS_PER_VERTEX = 3; details.vertices = (segmentCountFloor + 1) * 2; @@ -1388,7 +1388,7 @@ void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, *(vertex++) = point.y; *(vertex++) = point.z; *(colorDataAt++) = compactColor; - + for (int i = 0; i < segmentCountFloor; i++) { point += dashVector; *(vertex++) = point.x; diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index f70eae6380..2545822c07 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -179,7 +179,8 @@ public: void renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color1, const glm::vec4& color2, int id = UNKNOWN_ID); - void renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id = UNKNOWN_ID); + void renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, + const float dash_length = 0.05f, const float gap_length = 0.025f, int id = UNKNOWN_ID); void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec3& color, int id = UNKNOWN_ID) { renderLine(batch, p1, p2, glm::vec4(color, 1.0f), id); } @@ -189,9 +190,9 @@ public: void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, - const glm::vec3& color1, const glm::vec3& color2, int id = UNKNOWN_ID) + const glm::vec3& color1, const glm::vec3& color2, int id = UNKNOWN_ID) { renderLine(batch, p1, p2, glm::vec4(color1, 1.0f), glm::vec4(color2, 1.0f), id); } - + void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec4& color1, const glm::vec4& color2, int id = UNKNOWN_ID); @@ -216,7 +217,7 @@ protected: private: GeometryCache(); virtual ~GeometryCache(); - + typedef QPair IntPair; typedef QPair VerticesIndices; @@ -247,7 +248,7 @@ private: ~BatchItemDetails(); void clear(); }; - + QHash _coneVBOs; int _nextID; From 3e8970cc1ef5ca55f553f99049dd328b950a565c Mon Sep 17 00:00:00 2001 From: Nex Pro Date: Sun, 23 Aug 2015 16:49:39 +0100 Subject: [PATCH 096/103] corrected declaration of float constants DASH_LENGTH and GAP_LENGTH as per style guide. --- interface/src/Util.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 217a38e7b9..fa07864e75 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -44,8 +44,8 @@ void renderWorldBox(gpu::Batch& batch) { static const glm::vec4 DASHED_RED(1.0f, 0.0f, 0.0f, 1.0f); static const glm::vec4 DASHED_GREEN(0.0f, 1.0f, 0.0f, 1.0f); static const glm::vec4 DASHED_BLUE(0.0f, 0.0f, 1.0f, 1.0f); - static const float DASH_LENGTH = 0.5; - static const float GAP_LENGTH = 0.5; + static const float DASH_LENGTH = 1.0; + static const float GAP_LENGTH = 1.0; auto transform = Transform{}; batch.setModelTransform(transform); From 0ef2def56eb823fec277f34a9dc4680131054eef Mon Sep 17 00:00:00 2001 From: Nex Pro Date: Sun, 23 Aug 2015 16:52:12 +0100 Subject: [PATCH 097/103] changed default DASH_LENGTH and GAP_LENGTH to 1.0f --- interface/src/Util.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index fa07864e75..695a8be5e7 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -44,8 +44,8 @@ void renderWorldBox(gpu::Batch& batch) { static const glm::vec4 DASHED_RED(1.0f, 0.0f, 0.0f, 1.0f); static const glm::vec4 DASHED_GREEN(0.0f, 1.0f, 0.0f, 1.0f); static const glm::vec4 DASHED_BLUE(0.0f, 0.0f, 1.0f, 1.0f); - static const float DASH_LENGTH = 1.0; - static const float GAP_LENGTH = 1.0; + static const float DASH_LENGTH = 1.0f; + static const float GAP_LENGTH = 1.0f; auto transform = Transform{}; batch.setModelTransform(transform); From 748625399e8ed37e46e1801b5984ea107862d77f Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sun, 23 Aug 2015 17:59:58 +0200 Subject: [PATCH 098/103] "View -> World Axes" menu option --- interface/src/Application.cpp | 2 +- interface/src/Menu.cpp | 2 ++ interface/src/Menu.h | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index aa18e602bf..ec8a6b87f9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3340,7 +3340,7 @@ namespace render { template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); } template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); } template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) { - if (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { + if (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) { PerformanceTimer perfTimer("worldBox"); auto& batch = *args->_batch; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 11bc38c85e..af80320157 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -294,6 +294,8 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::StandingHMDSensorMode, 0, false, avatar, SLOT(updateStandingHMDModeFromMenu())); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::WorldAxes); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats); addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::SHIFT | Qt::Key_L, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 278da363d1..4bd1e7f664 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -294,6 +294,7 @@ namespace MenuOption { const QString VisibleToEveryone = "Everyone"; const QString VisibleToFriends = "Friends"; const QString VisibleToNoOne = "No one"; + const QString WorldAxes = "World Axes"; } #endif // hifi_Menu_h From 53a63fed792253e0bdb5622940ee5ace6fb41944 Mon Sep 17 00:00:00 2001 From: Nex Pro Date: Sun, 23 Aug 2015 17:16:23 +0100 Subject: [PATCH 099/103] Changed constants red, green, blue and grey to uppercase as per style guide. --- interface/src/Util.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 695a8be5e7..4aeb0f6ac3 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -36,10 +36,10 @@ void renderWorldBox(gpu::Batch& batch) { auto geometryCache = DependencyManager::get(); // Show center of world - static const glm::vec3 red(1.0f, 0.0f, 0.0f); - static const glm::vec3 green(0.0f, 1.0f, 0.0f); - static const glm::vec3 blue(0.0f, 0.0f, 1.0f); - static const glm::vec3 grey(0.5f, 0.5f, 0.5f); + static const glm::vec3 RED(1.0f, 0.0f, 0.0f); + static const glm::vec3 GREEN(0.0f, 1.0f, 0.0f); + static const glm::vec3 BLUE(0.0f, 0.0f, 1.0f); + static const glm::vec3 GREY(0.5f, 0.5f, 0.5f); static const glm::vec4 DASHED_RED(1.0f, 0.0f, 0.0f, 1.0f); static const glm::vec4 DASHED_GREEN(0.0f, 1.0f, 0.0f, 1.0f); @@ -50,42 +50,42 @@ void renderWorldBox(gpu::Batch& batch) { batch.setModelTransform(transform); - geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(HALF_TREE_SCALE, 0.0f, 0.0f), red); + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(HALF_TREE_SCALE, 0.0f, 0.0f), RED); geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-HALF_TREE_SCALE, 0.0f, 0.0f), DASHED_RED, DASH_LENGTH, GAP_LENGTH); - geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, HALF_TREE_SCALE, 0.0f), green); + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, HALF_TREE_SCALE, 0.0f), GREEN); geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, -HALF_TREE_SCALE, 0.0f), DASHED_GREEN, DASH_LENGTH, GAP_LENGTH); - geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, HALF_TREE_SCALE), blue); + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, HALF_TREE_SCALE), BLUE); geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, -HALF_TREE_SCALE), DASHED_BLUE, DASH_LENGTH, GAP_LENGTH); - geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), grey); - geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, 0.0f), grey); + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY); + geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, 0.0f), GREY); // Draw meter markers along the 3 axis to help with measuring things const float MARKER_DISTANCE = 1.0f; const float MARKER_RADIUS = 0.05f; - geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, red); + geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, RED); transform.setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, 0.0f)); batch.setModelTransform(transform); - geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, red); + geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, RED); transform.setTranslation(glm::vec3(0.0f, MARKER_DISTANCE, 0.0f)); batch.setModelTransform(transform); - geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, green); + geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, GREEN); transform.setTranslation(glm::vec3(0.0f, 0.0f, MARKER_DISTANCE)); batch.setModelTransform(transform); - geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, blue); + geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, BLUE); transform.setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, MARKER_DISTANCE)); batch.setModelTransform(transform); - geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, grey); + geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, GREY); } // Return a random vector of average length 1 From b7237fb5ca6eeaf8986839369549d0b08f9cff6e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 23 Aug 2015 09:32:58 -0700 Subject: [PATCH 100/103] Fixing build problem with 3d connexion --- interface/src/devices/3DConnexionClient.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/interface/src/devices/3DConnexionClient.cpp b/interface/src/devices/3DConnexionClient.cpp index ff54804bef..52b4dce68d 100755 --- a/interface/src/devices/3DConnexionClient.cpp +++ b/interface/src/devices/3DConnexionClient.cpp @@ -185,7 +185,8 @@ void ConnexionClient::destroy() { ConnexionData& connexiondata = ConnexionData::getInstance(); int deviceid = connexiondata.getDeviceID(); connexiondata.setDeviceID(0); - Application::getUserInputMapper()->removeDevice(deviceid); + auto userInputMapper = DependencyManager::get(); + userInputMapper->removeDevice(deviceid); } #define LOGITECH_VENDOR_ID 0x46d @@ -289,14 +290,15 @@ unsigned short HidToVirtualKey(unsigned long pid, unsigned short hidKeyCode) { bool ConnexionClient::RawInputEventFilter(void* msg, long* result) { ConnexionData& connexiondata = ConnexionData::getInstance(); + auto userInputMapper = DependencyManager::get(); if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) { - connexiondata.registerToUserInputMapper(*Application::getUserInputMapper()); - connexiondata.assignDefaultInputMapping(*Application::getUserInputMapper()); + connexiondata.registerToUserInputMapper(*userInputMapper); + connexiondata.assignDefaultInputMapping(*userInputMapper); UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); } else if (!Is3dmouseAttached() && connexiondata.getDeviceID() != 0) { int deviceid = connexiondata.getDeviceID(); connexiondata.setDeviceID(0); - Application::getUserInputMapper()->removeDevice(deviceid); + userInputMapper->removeDevice(deviceid); } if (!Is3dmouseAttached()) { From 63f01736653e13ff677f20388976612859b0a88a Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sun, 23 Aug 2015 19:52:29 +0200 Subject: [PATCH 101/103] extend world boundaries --- interface/src/Util.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 4aeb0f6ac3..7ee38d7de0 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -40,6 +40,7 @@ void renderWorldBox(gpu::Batch& batch) { static const glm::vec3 GREEN(0.0f, 1.0f, 0.0f); static const glm::vec3 BLUE(0.0f, 0.0f, 1.0f); static const glm::vec3 GREY(0.5f, 0.5f, 0.5f); + static const glm::vec4 GREY4(0.5f, 0.5f, 0.5f, 1.0f); static const glm::vec4 DASHED_RED(1.0f, 0.0f, 0.0f, 1.0f); static const glm::vec4 DASHED_GREEN(0.0f, 1.0f, 0.0f, 1.0f); @@ -62,8 +63,25 @@ void renderWorldBox(gpu::Batch& batch) { geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, -HALF_TREE_SCALE), DASHED_BLUE, DASH_LENGTH, GAP_LENGTH); - geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY); - geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, 0.0f), GREY); + // X center boundaries + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), glm::vec3(HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), GREY); + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), glm::vec3(-HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY); + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), glm::vec3(HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY); + geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), glm::vec3(HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY); + + // Z center boundaries + geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, -HALF_TREE_SCALE), glm::vec3(0.0f, -HALF_TREE_SCALE, HALF_TREE_SCALE), GREY); + geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, -HALF_TREE_SCALE), glm::vec3(0.0f, HALF_TREE_SCALE, -HALF_TREE_SCALE), GREY); + geometryCache->renderLine(batch, glm::vec3(0.0f, HALF_TREE_SCALE, -HALF_TREE_SCALE), glm::vec3(0.0f, HALF_TREE_SCALE, HALF_TREE_SCALE), GREY); + geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, HALF_TREE_SCALE), glm::vec3(0.0f, HALF_TREE_SCALE, HALF_TREE_SCALE), GREY); + + // Center boundaries + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), glm::vec3(-HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY); + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), GREY); + geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY); + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY); + + geometryCache->renderWireCube(batch, TREE_SCALE, GREY4); // Draw meter markers along the 3 axis to help with measuring things const float MARKER_DISTANCE = 1.0f; From bbd6f25ff7e95a0cc2a9b39ae9da77053b4b20e6 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sun, 23 Aug 2015 20:01:58 +0200 Subject: [PATCH 102/103] style fixes --- interface/src/Util.cpp | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 7ee38d7de0..dad34e9243 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -64,23 +64,35 @@ void renderWorldBox(gpu::Batch& batch) { DASH_LENGTH, GAP_LENGTH); // X center boundaries - geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), glm::vec3(HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), GREY); - geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), glm::vec3(-HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY); - geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), glm::vec3(HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY); - geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), glm::vec3(HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY); + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), + glm::vec3(HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), GREY); + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), + glm::vec3(-HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY); + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), + glm::vec3(HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY); + geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), + glm::vec3(HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY); // Z center boundaries - geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, -HALF_TREE_SCALE), glm::vec3(0.0f, -HALF_TREE_SCALE, HALF_TREE_SCALE), GREY); - geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, -HALF_TREE_SCALE), glm::vec3(0.0f, HALF_TREE_SCALE, -HALF_TREE_SCALE), GREY); - geometryCache->renderLine(batch, glm::vec3(0.0f, HALF_TREE_SCALE, -HALF_TREE_SCALE), glm::vec3(0.0f, HALF_TREE_SCALE, HALF_TREE_SCALE), GREY); - geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, HALF_TREE_SCALE), glm::vec3(0.0f, HALF_TREE_SCALE, HALF_TREE_SCALE), GREY); + geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, -HALF_TREE_SCALE), + glm::vec3(0.0f, -HALF_TREE_SCALE, HALF_TREE_SCALE), GREY); + geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, -HALF_TREE_SCALE), + glm::vec3(0.0f, HALF_TREE_SCALE, -HALF_TREE_SCALE), GREY); + geometryCache->renderLine(batch, glm::vec3(0.0f, HALF_TREE_SCALE, -HALF_TREE_SCALE), + glm::vec3(0.0f, HALF_TREE_SCALE, HALF_TREE_SCALE), GREY); + geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, HALF_TREE_SCALE), + glm::vec3(0.0f, HALF_TREE_SCALE, HALF_TREE_SCALE), GREY); // Center boundaries - geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), glm::vec3(-HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY); - geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), GREY); - geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY); - geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY); - + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), + glm::vec3(-HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY); + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), + glm::vec3(HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), GREY); + geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), + glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY); + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), + glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY); + geometryCache->renderWireCube(batch, TREE_SCALE, GREY4); // Draw meter markers along the 3 axis to help with measuring things From 3f4323e084c69e74ef5d5010b477ee6b06776202 Mon Sep 17 00:00:00 2001 From: Nex Pro Date: Mon, 24 Aug 2015 16:07:07 +0100 Subject: [PATCH 103/103] Added overload for renderDashedLine --- libraries/render-utils/src/GeometryCache.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index d7d9be0d2e..e9fe669aee 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -182,9 +182,13 @@ public: void renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color1, const glm::vec4& color2, int id = UNKNOWN_ID); - + void renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, - const float dash_length = 0.05f, const float gap_length = 0.025f, int id = UNKNOWN_ID); + int id = UNKNOWN_ID) + { renderDashedLine(batch, start, end, color, 0.05f, 0.025f, id); } + + void renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, + const float dash_length, const float gap_length, int id = UNKNOWN_ID); void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec3& color, int id = UNKNOWN_ID) { renderLine(batch, p1, p2, glm::vec4(color, 1.0f), id); } @@ -192,12 +196,11 @@ public: void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec4& color, int id = UNKNOWN_ID) { renderLine(batch, p1, p2, color, color, id); } - - void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, + void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec3& color1, const glm::vec3& color2, int id = UNKNOWN_ID) { renderLine(batch, p1, p2, glm::vec4(color1, 1.0f), glm::vec4(color2, 1.0f), id); } - void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, + void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec4& color1, const glm::vec4& color2, int id = UNKNOWN_ID); void updateVertices(int id, const QVector& points, const glm::vec4& color);