Imroving the roughness/specular highlight nd fixing a bug with the CUbemap texture

This commit is contained in:
samcake 2016-03-01 16:38:02 -08:00
commit 16ac69315f
30 changed files with 457 additions and 245 deletions

View file

@ -55,13 +55,7 @@ Agent::Agent(ReceivedMessage& message) :
{ {
DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender); DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
auto assetClient = DependencyManager::set<AssetClient>(); ResourceManager::init();
QThread* assetThread = new QThread;
assetThread->setObjectName("Asset Thread");
assetClient->moveToThread(assetThread);
connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init);
assetThread->start();
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>(); DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
@ -471,11 +465,7 @@ void Agent::aboutToFinish() {
// our entity tree is going to go away so tell that to the EntityScriptingInterface // our entity tree is going to go away so tell that to the EntityScriptingInterface
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr); DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
// cleanup the AssetClient thread ResourceManager::cleanup();
QThread* assetThread = DependencyManager::get<AssetClient>()->thread();
DependencyManager::destroy<AssetClient>();
assetThread->quit();
assetThread->wait();
// cleanup the AudioInjectorManager (and any still running injectors) // cleanup the AudioInjectorManager (and any still running injectors)
DependencyManager::destroy<AudioInjectorManager>(); DependencyManager::destroy<AudioInjectorManager>();

View file

@ -13,13 +13,30 @@
// //
// Goes into "paused" when the '.' key (and automatically when started in HMD), and normal when pressing any key. // Goes into "paused" when the '.' key (and automatically when started in HMD), and normal when pressing any key.
// See MAIN CONTROL, below, for what "paused" actually does. // See MAIN CONTROL, below, for what "paused" actually does.
var OVERLAY_RATIO = 1920 / 1080; var OVERLAY_WIDTH = 1920;
var OVERLAY_HEIGHT = 1080;
var OVERLAY_RATIO = OVERLAY_WIDTH / OVERLAY_HEIGHT;
var OVERLAY_DATA = { var OVERLAY_DATA = {
width: OVERLAY_WIDTH,
height: OVERLAY_HEIGHT,
imageURL: "http://hifi-content.s3.amazonaws.com/alan/production/images/images/Overlay-Viz-blank.png", imageURL: "http://hifi-content.s3.amazonaws.com/alan/production/images/images/Overlay-Viz-blank.png",
color: {red: 255, green: 255, blue: 255}, color: {red: 255, green: 255, blue: 255},
alpha: 1 alpha: 1
}; };
var lastOverlayPosition = { x: 0, y: 0, z: 0};
var OVERLAY_DATA_HMD = {
position: lastOverlayPosition,
width: OVERLAY_WIDTH,
height: OVERLAY_HEIGHT,
url: "http://hifi-content.s3.amazonaws.com/alan/production/images/images/Overlay-Viz-blank.png",
color: {red: 255, green: 255, blue: 255},
alpha: 1,
scale: 2,
isFacingAvatar: true,
drawInFront: true
};
// ANIMATION // ANIMATION
// We currently don't have play/stopAnimation integrated with the animation graph, but we can get the same effect // We currently don't have play/stopAnimation integrated with the animation graph, but we can get the same effect
// using an animation graph with a state that we turn on and off through the animation var defined with that state. // using an animation graph with a state that we turn on and off through the animation var defined with that state.
@ -64,29 +81,74 @@ function stopAwayAnimation() {
// OVERLAY // OVERLAY
var overlay = Overlays.addOverlay("image", OVERLAY_DATA); var overlay = Overlays.addOverlay("image", OVERLAY_DATA);
var overlayHMD = Overlays.addOverlay("image3d", OVERLAY_DATA_HMD);
function moveCloserToCamera(positionAtHUD) {
// we don't actually want to render at the slerped look at... instead, we want to render
// slightly closer to the camera than that.
var MOVE_CLOSER_TO_CAMERA_BY = -0.25;
var cameraFront = Quat.getFront(Camera.orientation);
var closerToCamera = Vec3.multiply(cameraFront, MOVE_CLOSER_TO_CAMERA_BY); // slightly closer to camera
var slightlyCloserPosition = Vec3.sum(positionAtHUD, closerToCamera);
return slightlyCloserPosition;
}
function showOverlay() { function showOverlay() {
var properties = {visible: true}, var properties = {visible: true};
// Update for current screen size, keeping overlay proportions constant.
screen = Controller.getViewportDimensions(), if (HMD.active) {
screenRatio = screen.x / screen.y; // make sure desktop version is hidden
if (screenRatio < OVERLAY_RATIO) { Overlays.editOverlay(overlay, { visible: false });
properties.width = screen.x;
properties.height = screen.x / OVERLAY_RATIO; lastOverlayPosition = HMD.getHUDLookAtPosition3D();
properties.x = 0; var actualOverlayPositon = moveCloserToCamera(lastOverlayPosition);
properties.y = (screen.y - properties.height) / 2; Overlays.editOverlay(overlayHMD, { visible: true, position: actualOverlayPositon });
} else { } else {
properties.height = screen.y; // make sure HMD is hidden
properties.width = screen.y * OVERLAY_RATIO; Overlays.editOverlay(overlayHMD, { visible: false });
properties.y = 0;
properties.x = (screen.x - properties.width) / 2; // Update for current screen size, keeping overlay proportions constant.
var screen = Controller.getViewportDimensions();
// keep the overlay it's natural size and always center it...
Overlays.editOverlay(overlay, { visible: true,
x: ((screen.x - OVERLAY_WIDTH) / 2),
y: ((screen.y - OVERLAY_HEIGHT) / 2) });
} }
Overlays.editOverlay(overlay, properties);
} }
function hideOverlay() { function hideOverlay() {
Overlays.editOverlay(overlay, {visible: false}); Overlays.editOverlay(overlay, {visible: false});
Overlays.editOverlay(overlayHMD, {visible: false});
} }
hideOverlay(); hideOverlay();
function maybeMoveOverlay() {
if (isAway) {
// if we switched from HMD to Desktop, make sure to hide our HUD overlay and show the
// desktop overlay
if (!HMD.active) {
showOverlay(); // this will also recenter appropriately
}
if (HMD.active) {
// Note: instead of moving it directly to the lookAt, we will move it slightly toward the
// new look at. This will result in a more subtle slerp toward the look at and reduce jerkiness
var EASE_BY_RATIO = 0.1;
var lookAt = HMD.getHUDLookAtPosition3D();
var lookAtChange = Vec3.subtract(lookAt, lastOverlayPosition);
var halfWayBetweenOldAndLookAt = Vec3.multiply(lookAtChange, EASE_BY_RATIO);
var newOverlayPosition = Vec3.sum(lastOverlayPosition, halfWayBetweenOldAndLookAt);
lastOverlayPosition = newOverlayPosition;
var actualOverlayPositon = moveCloserToCamera(lastOverlayPosition);
Overlays.editOverlay(overlayHMD, { visible: true, position: actualOverlayPositon });
// make sure desktop version is hidden
Overlays.editOverlay(overlay, { visible: false });
}
}
}
// MAIN CONTROL // MAIN CONTROL
var wasMuted, isAway; var wasMuted, isAway;
@ -106,6 +168,12 @@ function goAway() {
MyAvatar.setEnableMeshVisible(false); // just for our own display, without changing point of view MyAvatar.setEnableMeshVisible(false); // just for our own display, without changing point of view
playAwayAnimation(); // animation is still seen by others playAwayAnimation(); // animation is still seen by others
showOverlay(); showOverlay();
// tell the Reticle, we want to stop capturing the mouse until we come back
Reticle.allowMouseCapture = false;
if (HMD.active) {
Reticle.visible = false;
}
} }
function goActive() { function goActive() {
if (!isAway) { if (!isAway) {
@ -119,13 +187,20 @@ function goActive() {
MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting. MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting.
stopAwayAnimation(); stopAwayAnimation();
hideOverlay(); hideOverlay();
// tell the Reticle, we are ready to capture the mouse again and it should be visible
Reticle.allowMouseCapture = true;
Reticle.visible = true;
if (HMD.active) {
Reticle.position = HMD.getHUDLookAtPosition2D();
}
} }
function maybeGoActive(event) { function maybeGoActive(event) {
if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it) if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it)
return; return;
} }
if (!isAway && (event.text === '.')) { if (!isAway && (event.text == 'ESC')) {
goAway(); goAway();
} else { } else {
goActive(); goActive();
@ -141,10 +216,8 @@ function maybeGoAway() {
} }
} }
// If the mouse has gone from captured, to non-captured state, // If the mouse has gone from captured, to non-captured state, then it likely means the person is still in the HMD, but
// then it likely means the person is still in the HMD, but has // tabbed away from the application (meaning they don't have mouse control) and they likely want to go into an away state
// tabbed away from the application (meaning they don't have mouse
// control) and they likely want to go into an away state
if (Reticle.mouseCaptured !== wasMouseCaptured) { if (Reticle.mouseCaptured !== wasMouseCaptured) {
wasMouseCaptured = !wasMouseCaptured; wasMouseCaptured = !wasMouseCaptured;
if (!wasMouseCaptured) { if (!wasMouseCaptured) {
@ -153,6 +226,8 @@ function maybeGoAway() {
} }
} }
Script.update.connect(maybeMoveOverlay);
Script.update.connect(maybeGoAway); Script.update.connect(maybeGoAway);
Controller.mousePressEvent.connect(goActive); Controller.mousePressEvent.connect(goActive);
Controller.keyPressEvent.connect(maybeGoActive); Controller.keyPressEvent.connect(maybeGoActive);

View file

@ -35,8 +35,8 @@ var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 50;
Controller.mouseMoveEvent.connect(function(mouseEvent) { Controller.mouseMoveEvent.connect(function(mouseEvent) {
var now = Date.now(); var now = Date.now();
// if the reticle is hidden, show it... // if the reticle is hidden, and we're not in away mode...
if (!Reticle.visible) { if (!Reticle.visible && Reticle.allowMouseCapture) {
Reticle.visible = true; Reticle.visible = true;
if (HMD.active) { if (HMD.active) {
shouldSeekToLookAt = true; shouldSeekToLookAt = true;
@ -44,7 +44,7 @@ Controller.mouseMoveEvent.connect(function(mouseEvent) {
} else { } else {
// even if the reticle is visible, if we're in HMD mode, and the person is moving their mouse quickly (shaking it) // even if the reticle is visible, if we're in HMD mode, and the person is moving their mouse quickly (shaking it)
// then they are probably looking for it, and we should move into seekToLookAt mode // then they are probably looking for it, and we should move into seekToLookAt mode
if (HMD.active && !shouldSeekToLookAt) { if (HMD.active && !shouldSeekToLookAt && Reticle.allowMouseCapture) {
var dx = Reticle.position.x - lastMouseX; var dx = Reticle.position.x - lastMouseX;
var dy = Reticle.position.y - lastMouseY; var dy = Reticle.position.y - lastMouseY;
var dt = Math.max(1, (now - lastMouseMove)); // mSecs since last mouse move var dt = Math.max(1, (now - lastMouseMove)); // mSecs since last mouse move

View file

@ -370,7 +370,6 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<AutoUpdater>(); DependencyManager::set<AutoUpdater>();
DependencyManager::set<PathUtils>(); DependencyManager::set<PathUtils>();
DependencyManager::set<InterfaceActionFactory>(); DependencyManager::set<InterfaceActionFactory>();
DependencyManager::set<AssetClient>();
DependencyManager::set<AudioInjectorManager>(); DependencyManager::set<AudioInjectorManager>();
DependencyManager::set<MessagesClient>(); DependencyManager::set<MessagesClient>();
DependencyManager::set<UserInputMapper>(); DependencyManager::set<UserInputMapper>();
@ -528,13 +527,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
audioThread->start(); audioThread->start();
// Setup AssetClient ResourceManager::init();
auto assetClient = DependencyManager::get<AssetClient>();
QThread* assetThread = new QThread;
assetThread->setObjectName("Asset Thread");
assetClient->moveToThread(assetThread);
connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init);
assetThread->start();
// Setup MessagesClient // Setup MessagesClient
auto messagesClient = DependencyManager::get<MessagesClient>(); auto messagesClient = DependencyManager::get<MessagesClient>();
@ -547,7 +540,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
const DomainHandler& domainHandler = nodeList->getDomainHandler(); const DomainHandler& domainHandler = nodeList->getDomainHandler();
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&))); connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain()));
connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle())); connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle()));
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle())); connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
@ -644,13 +637,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket); connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket);
identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS);
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkDiskCache* cache = new QNetworkDiskCache();
cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE);
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache");
networkAccessManager.setCache(cache);
ResourceCache::setRequestLimit(3); ResourceCache::setRequestLimit(3);
_glWidget = new GLCanvas(); _glWidget = new GLCanvas();
@ -661,15 +647,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_glWidget->setFocusPolicy(Qt::StrongFocus); _glWidget->setFocusPolicy(Qt::StrongFocus);
_glWidget->setFocus(); _glWidget->setFocus();
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
// OSX doesn't seem to provide for hiding the cursor only on the GL widget auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget
_window->setCursor(Qt::BlankCursor);
#else #else
// On windows and linux, hiding the top level cursor also means it's invisible // On windows and linux, hiding the top level cursor also means it's invisible when hovering over the
// when hovering over the window menu, which is a pain, so only hide it for // window menu, which is a pain, so only hide it for the GL surface
// the GL surface auto cursorTarget = _glWidget;
_glWidget->setCursor(Qt::BlankCursor);
#endif #endif
cursorTarget->setCursor(Qt::BlankCursor);
// enable mouse tracking; otherwise, we only get drag events // enable mouse tracking; otherwise, we only get drag events
_glWidget->setMouseTracking(true); _glWidget->setMouseTracking(true);
@ -981,6 +967,29 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_idleTimer->start(0); _idleTimer->start(0);
} }
void Application::checkChangeCursor() {
QMutexLocker locker(&_changeCursorLock);
if (_cursorNeedsChanging) {
#ifdef Q_OS_MAC
auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget
#else
// On windows and linux, hiding the top level cursor also means it's invisible when hovering over the
// window menu, which is a pain, so only hide it for the GL surface
auto cursorTarget = _glWidget;
#endif
cursorTarget->setCursor(_desiredCursor);
_cursorNeedsChanging = false;
}
}
void Application::showCursor(const QCursor& cursor) {
QMutexLocker locker(&_changeCursorLock);
_desiredCursor = cursor;
_cursorNeedsChanging = true;
}
void Application::aboutToQuit() { void Application::aboutToQuit() {
emit beforeAboutToQuit(); emit beforeAboutToQuit();
@ -1062,13 +1071,6 @@ void Application::cleanupBeforeQuit() {
DependencyManager::destroy<OffscreenUi>(); DependencyManager::destroy<OffscreenUi>();
} }
void Application::emptyLocalCache() {
if (auto cache = NetworkAccessManager::getInstance().cache()) {
qDebug() << "DiskCacheEditor::clear(): Clearing disk cache.";
cache->clear();
}
}
Application::~Application() { Application::~Application() {
EntityTreePointer tree = getEntities()->getTree(); EntityTreePointer tree = getEntities()->getTree();
tree->setSimulation(NULL); tree->setSimulation(NULL);
@ -1106,11 +1108,7 @@ Application::~Application() {
DependencyManager::destroy<ScriptCache>(); DependencyManager::destroy<ScriptCache>();
DependencyManager::destroy<SoundCache>(); DependencyManager::destroy<SoundCache>();
// cleanup the AssetClient thread ResourceManager::cleanup();
QThread* assetThread = DependencyManager::get<AssetClient>()->thread();
DependencyManager::destroy<AssetClient>();
assetThread->quit();
assetThread->wait();
QThread* nodeThread = DependencyManager::get<NodeList>()->thread(); QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
@ -2431,6 +2429,9 @@ void Application::idle(uint64_t now) {
return; // bail early, nothing to do here. return; // bail early, nothing to do here.
} }
checkChangeCursor();
Stats::getInstance()->updateStats(); Stats::getInstance()->updateStats();
AvatarInputs::getInstance()->update(); AvatarInputs::getInstance()->update();
@ -3055,7 +3056,7 @@ void Application::reloadResourceCaches() {
_viewFrustum.setOrientation(glm::quat()); _viewFrustum.setOrientation(glm::quat());
queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions); queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
emptyLocalCache(); DependencyManager::get<AssetClient>()->clearCache();
DependencyManager::get<AnimationCache>()->refreshAll(); DependencyManager::get<AnimationCache>()->refreshAll();
DependencyManager::get<ModelCache>()->refreshAll(); DependencyManager::get<ModelCache>()->refreshAll();
@ -3978,13 +3979,8 @@ void Application::handleDomainConnectionDeniedPacket(QSharedPointer<ReceivedMess
AccountManager::getInstance().checkAndSignalForAccessToken(); AccountManager::getInstance().checkAndSignalForAccessToken();
} }
void Application::connectedToDomain(const QString& hostname) { void Application::resettingDomain() {
AccountManager& accountManager = AccountManager::getInstance(); _notifiedPacketVersionMismatchThisDomain = false;
const QUuid& domainID = DependencyManager::get<NodeList>()->getDomainHandler().getUUID();
if (accountManager.isLoggedIn() && !domainID.isNull()) {
_notifiedPacketVersionMismatchThisDomain = false;
}
} }
void Application::nodeAdded(SharedNodePointer node) { void Application::nodeAdded(SharedNodePointer node) {

View file

@ -120,6 +120,8 @@ public:
QSize getDeviceSize() const; QSize getDeviceSize() const;
bool hasFocus() const; bool hasFocus() const;
void showCursor(const QCursor& cursor);
bool isThrottleRendering() const; bool isThrottleRendering() const;
Camera* getCamera() { return &_myCamera; } Camera* getCamera() { return &_myCamera; }
@ -288,7 +290,7 @@ private slots:
void idle(uint64_t now); void idle(uint64_t now);
void aboutToQuit(); void aboutToQuit();
void connectedToDomain(const QString& hostname); void resettingDomain();
void audioMuteToggled(); void audioMuteToggled();
void faceTrackerMuteToggled(); void faceTrackerMuteToggled();
@ -328,8 +330,6 @@ private:
void cleanupBeforeQuit(); void cleanupBeforeQuit();
void emptyLocalCache();
void update(float deltaTime); void update(float deltaTime);
void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue); void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue);
@ -515,6 +515,11 @@ private:
QTimer* _idleTimer { nullptr }; QTimer* _idleTimer { nullptr };
bool _fakedMouseEvent { false }; bool _fakedMouseEvent { false };
void checkChangeCursor();
mutable QMutex _changeCursorLock { QMutex::Recursive };
QCursor _desiredCursor{ Qt::BlankCursor };
bool _cursorNeedsChanging { false };
}; };
#endif // hifi_Application_h #endif // hifi_Application_h

View file

@ -337,9 +337,21 @@ QPointF ApplicationCompositor::getMouseEventPosition(QMouseEvent* event) {
bool ApplicationCompositor::shouldCaptureMouse() const { bool ApplicationCompositor::shouldCaptureMouse() const {
// if we're in HMD mode, and some window of ours is active, but we're not currently showing a popup menu // if we're in HMD mode, and some window of ours is active, but we're not currently showing a popup menu
return qApp->isHMDMode() && QApplication::activeWindow() && !Menu::isSomeSubmenuShown(); return _allowMouseCapture && qApp->isHMDMode() && QApplication::activeWindow() && !Menu::isSomeSubmenuShown();
} }
void ApplicationCompositor::setAllowMouseCapture(bool capture) {
if (qApp->isHMDMode()) {
if (capture) {
qApp->showCursor(Qt::BlankCursor);
} else {
qApp->showCursor(Qt::ArrowCursor);
}
}
_allowMouseCapture = capture;
}
void ApplicationCompositor::handleLeaveEvent() { void ApplicationCompositor::handleLeaveEvent() {
if (shouldCaptureMouse()) { if (shouldCaptureMouse()) {

View file

@ -106,6 +106,9 @@ public:
bool shouldCaptureMouse() const; bool shouldCaptureMouse() const;
bool getAllowMouseCapture() const { return _allowMouseCapture; }
void setAllowMouseCapture(bool capture);
/// if the reticle is pointing to a system overlay (a dialog box for example) then the function returns true otherwise false /// if the reticle is pointing to a system overlay (a dialog box for example) then the function returns true otherwise false
bool getReticleOverDesktop() const; bool getReticleOverDesktop() const;
void setReticleOverDesktop(bool value) { _isOverDesktop = value; } void setReticleOverDesktop(bool value) { _isOverDesktop = value; }
@ -162,6 +165,8 @@ private:
bool _reticleOverQml { false }; bool _reticleOverQml { false };
bool _allowMouseCapture { true };
ReticleInterface* _reticleInterface; ReticleInterface* _reticleInterface;
}; };
@ -173,12 +178,17 @@ class ReticleInterface : public QObject {
Q_PROPERTY(float depth READ getDepth WRITE setDepth) Q_PROPERTY(float depth READ getDepth WRITE setDepth)
Q_PROPERTY(glm::vec2 maximumPosition READ getMaximumPosition) Q_PROPERTY(glm::vec2 maximumPosition READ getMaximumPosition)
Q_PROPERTY(bool mouseCaptured READ isMouseCaptured) Q_PROPERTY(bool mouseCaptured READ isMouseCaptured)
Q_PROPERTY(bool allowMouseCapture READ getAllowMouseCapture WRITE setAllowMouseCapture)
Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay) Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay)
public: public:
ReticleInterface(ApplicationCompositor* outer) : QObject(outer), _compositor(outer) {} ReticleInterface(ApplicationCompositor* outer) : QObject(outer), _compositor(outer) {}
Q_INVOKABLE bool isMouseCaptured() { return _compositor->shouldCaptureMouse(); } Q_INVOKABLE bool isMouseCaptured() { return _compositor->shouldCaptureMouse(); }
Q_INVOKABLE bool getAllowMouseCapture() { return _compositor->getAllowMouseCapture(); }
Q_INVOKABLE void setAllowMouseCapture(bool value) { return _compositor->setAllowMouseCapture(value); }
Q_INVOKABLE bool isPointingAtSystemOverlay() { return !_compositor->getReticleOverDesktop(); } Q_INVOKABLE bool isPointingAtSystemOverlay() { return !_compositor->getReticleOverDesktop(); }
Q_INVOKABLE bool getVisible() { return _compositor->getReticleVisible(); } Q_INVOKABLE bool getVisible() { return _compositor->getReticleVisible(); }

View file

@ -9,23 +9,21 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <functional> #include "DiskCacheEditor.h"
#include <QDebug> #include <QDebug>
#include <QDialog> #include <QDialog>
#include <QGridLayout> #include <QGridLayout>
#include <QPushButton> #include <QPushButton>
#include <QLabel> #include <QLabel>
#include <QNetworkDiskCache> #include <QTimer>
#include <QMessageBox> #include <QMessageBox>
#include <NetworkAccessManager.h> #include <AssetClient.h>
#include "DiskCacheEditor.h"
#include "OffscreenUi.h" #include "OffscreenUi.h"
DiskCacheEditor::DiskCacheEditor(QWidget* parent) : QObject(parent) { DiskCacheEditor::DiskCacheEditor(QWidget* parent) : QObject(parent) {
} }
QWindow* DiskCacheEditor::windowHandle() { QWindow* DiskCacheEditor::windowHandle() {
@ -33,7 +31,6 @@ QWindow* DiskCacheEditor::windowHandle() {
} }
void DiskCacheEditor::toggle() { void DiskCacheEditor::toggle() {
qDebug() << "DiskCacheEditor::toggle()";
if (!_dialog) { if (!_dialog) {
makeDialog(); makeDialog();
} }
@ -88,17 +85,17 @@ void DiskCacheEditor::makeDialog() {
Q_CHECK_PTR(_maxSize); Q_CHECK_PTR(_maxSize);
_maxSize->setAlignment(Qt::AlignLeft); _maxSize->setAlignment(Qt::AlignLeft);
layout->addWidget(_maxSize, 2, 1, 1, 3); layout->addWidget(_maxSize, 2, 1, 1, 3);
refresh(); refresh();
QPushButton* refreshCacheButton = new QPushButton(_dialog); static const int REFRESH_INTERVAL = 100; // msec
Q_CHECK_PTR(refreshCacheButton); _refreshTimer = new QTimer(_dialog);
refreshCacheButton->setText("Refresh"); _refreshTimer->setInterval(REFRESH_INTERVAL);
refreshCacheButton->setToolTip("Reload the cache stats."); _refreshTimer->setSingleShot(false);
connect(refreshCacheButton, SIGNAL(clicked()), SLOT(refresh())); QObject::connect(_refreshTimer.data(), &QTimer::timeout, this, &DiskCacheEditor::refresh);
layout->addWidget(refreshCacheButton, 3, 2); _refreshTimer->start();
QPushButton* clearCacheButton = new QPushButton(_dialog); QPushButton* clearCacheButton = new QPushButton(_dialog);
Q_CHECK_PTR(clearCacheButton); Q_CHECK_PTR(clearCacheButton);
clearCacheButton->setText("Clear"); clearCacheButton->setText("Clear");
@ -108,7 +105,11 @@ void DiskCacheEditor::makeDialog() {
} }
void DiskCacheEditor::refresh() { void DiskCacheEditor::refresh() {
static const std::function<QString(qint64)> stringify = [](qint64 number) { DependencyManager::get<AssetClient>()->cacheInfoRequest(this, "cacheInfoCallback");
}
void DiskCacheEditor::cacheInfoCallback(QString cacheDirectory, qint64 cacheSize, qint64 maximumCacheSize) {
static const auto stringify = [](qint64 number) {
static const QStringList UNITS = QStringList() << "B" << "KB" << "MB" << "GB"; static const QStringList UNITS = QStringList() << "B" << "KB" << "MB" << "GB";
static const qint64 CHUNK = 1024; static const qint64 CHUNK = 1024;
QString unit; QString unit;
@ -122,30 +123,24 @@ void DiskCacheEditor::refresh() {
} }
return QString("%0 %1").arg(number).arg(UNITS[i]); return QString("%0 %1").arg(number).arg(UNITS[i]);
}; };
QNetworkDiskCache* cache = qobject_cast<QNetworkDiskCache*>(NetworkAccessManager::getInstance().cache());
if (_path) { if (_path) {
_path->setText(cache->cacheDirectory()); _path->setText(cacheDirectory);
} }
if (_size) { if (_size) {
_size->setText(stringify(cache->cacheSize())); _size->setText(stringify(cacheSize));
} }
if (_maxSize) { if (_maxSize) {
_maxSize->setText(stringify(cache->maximumCacheSize())); _maxSize->setText(stringify(maximumCacheSize));
} }
} }
void DiskCacheEditor::clear() { void DiskCacheEditor::clear() {
QMessageBox::StandardButton buttonClicked = auto buttonClicked = OffscreenUi::question(_dialog, "Clearing disk cache",
OffscreenUi::question(_dialog, "Clearing disk cache", "You are about to erase all the content of the disk cache, "
"You are about to erase all the content of the disk cache, " "are you sure you want to do that?",
"are you sure you want to do that?", QMessageBox::Ok | QMessageBox::Cancel);
QMessageBox::Ok | QMessageBox::Cancel);
if (buttonClicked == QMessageBox::Ok) { if (buttonClicked == QMessageBox::Ok) {
if (auto cache = NetworkAccessManager::getInstance().cache()) { DependencyManager::get<AssetClient>()->clearCache();
qDebug() << "DiskCacheEditor::clear(): Clearing disk cache.";
cache->clear();
}
} }
refresh();
} }

View file

@ -18,6 +18,7 @@
class QDialog; class QDialog;
class QLabel; class QLabel;
class QWindow; class QWindow;
class QTimer;
class DiskCacheEditor : public QObject { class DiskCacheEditor : public QObject {
Q_OBJECT Q_OBJECT
@ -32,8 +33,9 @@ public slots:
private slots: private slots:
void refresh(); void refresh();
void cacheInfoCallback(QString cacheDirectory, qint64 cacheSize, qint64 maximumCacheSize);
void clear(); void clear();
private: private:
void makeDialog(); void makeDialog();
@ -41,6 +43,7 @@ private:
QPointer<QLabel> _path; QPointer<QLabel> _path;
QPointer<QLabel> _size; QPointer<QLabel> _size;
QPointer<QLabel> _maxSize; QPointer<QLabel> _maxSize;
QPointer<QTimer> _refreshTimer;
}; };
#endif // hifi_DiskCacheEditor_h #endif // hifi_DiskCacheEditor_h

View file

@ -322,7 +322,7 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
_ambientTexture.clear(); _ambientTexture.clear();
} else { } else {
_ambientTexture = textureCache->getTexture(zone->getKeyLightProperties().getAmbientURL(), CUBE_TEXTURE); _ambientTexture = textureCache->getTexture(zone->getKeyLightProperties().getAmbientURL(), CUBE_TEXTURE);
if (_ambientTexture->getGPUTexture()) { if (_ambientTexture && _ambientTexture->isLoaded() && _ambientTexture->getGPUTexture()) {
_pendingAmbientTexture = false; _pendingAmbientTexture = false;
if (_ambientTexture->getGPUTexture()->getIrradiance()) { if (_ambientTexture->getGPUTexture()->getIrradiance()) {
sceneKeyLight->setAmbientSphere(_ambientTexture->getGPUTexture()->getIrradiance()); sceneKeyLight->setAmbientSphere(_ambientTexture->getGPUTexture()->getIrradiance());
@ -356,7 +356,7 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
// Update the Texture of the Skybox with the one pointed by this zone // Update the Texture of the Skybox with the one pointed by this zone
_skyboxTexture = textureCache->getTexture(zone->getSkyboxProperties().getURL(), CUBE_TEXTURE); _skyboxTexture = textureCache->getTexture(zone->getSkyboxProperties().getURL(), CUBE_TEXTURE);
if (_skyboxTexture->getGPUTexture()) { if (_skyboxTexture && _skyboxTexture->isLoaded() && _skyboxTexture->getGPUTexture()) {
auto texture = _skyboxTexture->getGPUTexture(); auto texture = _skyboxTexture->getGPUTexture();
skybox->setCubemap(texture); skybox->setCubemap(texture);
_pendingSkyboxTexture = false; _pendingSkyboxTexture = false;

View file

@ -338,105 +338,84 @@ static NetworkMesh* buildNetworkMesh(const FBXMesh& mesh, const QUrl& textureBas
return networkMesh; return networkMesh;
} }
static NetworkMaterial* buildNetworkMaterial(NetworkGeometry* geometry, const FBXMaterial& material, const QUrl& textureBaseUrl) {
auto textureCache = DependencyManager::get<TextureCache>();
NetworkMaterial* networkMaterial = new NetworkMaterial();
static model::TextureMapPointer setupNetworkTextureMap(NetworkGeometry* geometry, const QUrl& textureBaseUrl,
const FBXTexture& texture, TextureType type,
NetworkTexturePointer& networkTexture, QString& networkTextureName) {
auto textureCache = DependencyManager::get<TextureCache>();
// If content is inline, cache it under the fbx file, not its base url
const auto baseUrl = texture.content.isEmpty() ? textureBaseUrl : QUrl(textureBaseUrl.url() + "/");
const auto filename = baseUrl.resolved(QUrl(texture.filename));
networkTexture = textureCache->getTexture(filename, type, texture.content);
QObject::connect(networkTexture.data(), &NetworkTexture::networkTextureCreated, geometry, &NetworkGeometry::textureLoaded);
networkTextureName = texture.name;
auto map = std::make_shared<model::TextureMap>();
map->setTextureSource(networkTexture->_textureSource);
return map;
}
static NetworkMaterial* buildNetworkMaterial(NetworkGeometry* geometry, const FBXMaterial& material, const QUrl& textureBaseUrl) {
NetworkMaterial* networkMaterial = new NetworkMaterial();
networkMaterial->_material = material._material; networkMaterial->_material = material._material;
if (!material.albedoTexture.filename.isEmpty()) { if (!material.albedoTexture.filename.isEmpty()) {
networkMaterial->albedoTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.albedoTexture.filename)), DEFAULT_TEXTURE, material.albedoTexture.content); auto albedoMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.albedoTexture, DEFAULT_TEXTURE,
QObject::connect(networkMaterial->albedoTexture.data(), &NetworkTexture::networkTextureCreated, networkMaterial->albedoTexture, networkMaterial->albedoTextureName);
geometry, &NetworkGeometry::textureLoaded);
networkMaterial->albedoTextureName = material.albedoTexture.name;
auto albedoMap = model::TextureMapPointer(new model::TextureMap());
albedoMap->setTextureSource(networkMaterial->albedoTexture->_textureSource);
albedoMap->setTextureTransform(material.albedoTexture.transform); albedoMap->setTextureTransform(material.albedoTexture.transform);
material._material->setTextureMap(model::MaterialKey::ALBEDO_MAP, albedoMap); material._material->setTextureMap(model::MaterialKey::ALBEDO_MAP, albedoMap);
} }
if (!material.normalTexture.filename.isEmpty()) { if (!material.normalTexture.filename.isEmpty()) {
networkMaterial->normalTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.normalTexture.filename)), (material.normalTexture.isBumpmap ? BUMP_TEXTURE : NORMAL_TEXTURE), material.normalTexture.content); auto normalMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.normalTexture,
networkMaterial->normalTextureName = material.normalTexture.name; (material.normalTexture.isBumpmap ? BUMP_TEXTURE : NORMAL_TEXTURE),
networkMaterial->normalTexture, networkMaterial->normalTextureName);
auto normalMap = model::TextureMapPointer(new model::TextureMap()); networkMaterial->_material->setTextureMap(model::MaterialKey::NORMAL_MAP, normalMap);
normalMap->setTextureSource(networkMaterial->normalTexture->_textureSource);
material._material->setTextureMap(model::MaterialKey::NORMAL_MAP, normalMap);
} }
// Roughness first or gloss maybe // Roughness first or gloss maybe
if (!material.roughnessTexture.filename.isEmpty()) { if (!material.roughnessTexture.filename.isEmpty()) {
networkMaterial->roughnessTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.roughnessTexture.filename)), ROUGHNESS_TEXTURE, material.roughnessTexture.content); auto roughnessMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.roughnessTexture, ROUGHNESS_TEXTURE,
networkMaterial->roughnessTextureName = material.roughnessTexture.name; networkMaterial->roughnessTexture, networkMaterial->roughnessTextureName);
auto roughnessMap = model::TextureMapPointer(new model::TextureMap());
roughnessMap->setTextureSource(networkMaterial->roughnessTexture->_textureSource);
material._material->setTextureMap(model::MaterialKey::ROUGHNESS_MAP, roughnessMap); material._material->setTextureMap(model::MaterialKey::ROUGHNESS_MAP, roughnessMap);
} else if (!material.glossTexture.filename.isEmpty()) { } else if (!material.glossTexture.filename.isEmpty()) {
networkMaterial->roughnessTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.glossTexture.filename)), GLOSS_TEXTURE, material.glossTexture.content); auto roughnessMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.glossTexture, GLOSS_TEXTURE,
networkMaterial->roughnessTextureName = material.roughnessTexture.name; networkMaterial->roughnessTexture, networkMaterial->roughnessTextureName);
auto roughnessMap = model::TextureMapPointer(new model::TextureMap());
roughnessMap->setTextureSource(networkMaterial->roughnessTexture->_textureSource);
material._material->setTextureMap(model::MaterialKey::ROUGHNESS_MAP, roughnessMap); material._material->setTextureMap(model::MaterialKey::ROUGHNESS_MAP, roughnessMap);
} }
// Metallic first or specular maybe // Metallic first or specular maybe
if (!material.metallicTexture.filename.isEmpty()) { if (!material.metallicTexture.filename.isEmpty()) {
networkMaterial->metallicTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.metallicTexture.filename)), METALLIC_TEXTURE, material.metallicTexture.content); auto metallicMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.metallicTexture, METALLIC_TEXTURE,
networkMaterial->metallicTextureName = material.metallicTexture.name; networkMaterial->metallicTexture, networkMaterial->metallicTextureName);
auto metallicMap = model::TextureMapPointer(new model::TextureMap());
metallicMap->setTextureSource(networkMaterial->metallicTexture->_textureSource);
material._material->setTextureMap(model::MaterialKey::METALLIC_MAP, metallicMap); material._material->setTextureMap(model::MaterialKey::METALLIC_MAP, metallicMap);
} else if (!material.specularTexture.filename.isEmpty()) { } else if (!material.specularTexture.filename.isEmpty()) {
networkMaterial->metallicTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.specularTexture.filename)), SPECULAR_TEXTURE, material.specularTexture.content);
networkMaterial->metallicTextureName = material.specularTexture.name;
auto metallicMap = model::TextureMapPointer(new model::TextureMap());
metallicMap->setTextureSource(networkMaterial->metallicTexture->_textureSource);
auto metallicMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.specularTexture, SPECULAR_TEXTURE,
networkMaterial->metallicTexture, networkMaterial->metallicTextureName);
material._material->setTextureMap(model::MaterialKey::METALLIC_MAP, metallicMap); material._material->setTextureMap(model::MaterialKey::METALLIC_MAP, metallicMap);
} }
if (!material.occlusionTexture.filename.isEmpty()) { if (!material.occlusionTexture.filename.isEmpty()) {
networkMaterial->occlusionTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.occlusionTexture.filename)), OCCLUSION_TEXTURE, material.occlusionTexture.content); auto occlusionMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.occlusionTexture, OCCLUSION_TEXTURE,
networkMaterial->occlusionTextureName = material.occlusionTexture.name; networkMaterial->occlusionTexture, networkMaterial->occlusionTextureName);
auto occlusionMap = model::TextureMapPointer(new model::TextureMap());
occlusionMap->setTextureSource(networkMaterial->occlusionTexture->_textureSource);
material._material->setTextureMap(model::MaterialKey::OCCLUSION_MAP, occlusionMap); material._material->setTextureMap(model::MaterialKey::OCCLUSION_MAP, occlusionMap);
} }
if (!material.emissiveTexture.filename.isEmpty()) { if (!material.emissiveTexture.filename.isEmpty()) {
networkMaterial->emissiveTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.emissiveTexture.filename)), EMISSIVE_TEXTURE, material.emissiveTexture.content); auto emissiveMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.emissiveTexture, EMISSIVE_TEXTURE,
networkMaterial->emissiveTextureName = material.emissiveTexture.name; networkMaterial->emissiveTexture, networkMaterial->emissiveTextureName);
auto emissiveMap = model::TextureMapPointer(new model::TextureMap());
emissiveMap->setTextureSource(networkMaterial->emissiveTexture->_textureSource);
material._material->setTextureMap(model::MaterialKey::EMISSIVE_MAP, emissiveMap); material._material->setTextureMap(model::MaterialKey::EMISSIVE_MAP, emissiveMap);
} }
if (!material.lightmapTexture.filename.isEmpty()) { if (!material.lightmapTexture.filename.isEmpty()) {
networkMaterial->lightmapTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.lightmapTexture.filename)), LIGHTMAP_TEXTURE, material.lightmapTexture.content); auto lightmapMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.lightmapTexture, LIGHTMAP_TEXTURE,
networkMaterial->lightmapTextureName = material.lightmapTexture.name; networkMaterial->lightmapTexture, networkMaterial->lightmapTextureName);
auto lightmapMap = model::TextureMapPointer(new model::TextureMap());
lightmapMap->setTextureSource(networkMaterial->lightmapTexture->_textureSource);
lightmapMap->setTextureTransform(material.lightmapTexture.transform); lightmapMap->setTextureTransform(material.lightmapTexture.transform);
lightmapMap->setLightmapOffsetScale(material.lightmapParams.x, material.lightmapParams.y); lightmapMap->setLightmapOffsetScale(material.lightmapParams.x, material.lightmapParams.y);
material._material->setTextureMap(model::MaterialKey::LIGHTMAP_MAP, lightmapMap); material._material->setTextureMap(model::MaterialKey::LIGHTMAP_MAP, lightmapMap);
} }

View file

@ -47,22 +47,53 @@ AssetClient::AssetClient() {
} }
void AssetClient::init() { void AssetClient::init() {
if (QThread::currentThread() != thread()) { Q_ASSERT(QThread::currentThread() == thread());
QMetaObject::invokeMethod(this, "init", Qt::BlockingQueuedConnection);
}
// Setup disk cache if not already // Setup disk cache if not already
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); auto& networkAccessManager = NetworkAccessManager::getInstance();
if (!networkAccessManager.cache()) { if (!networkAccessManager.cache()) {
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
cachePath = !cachePath.isEmpty() ? cachePath : "interfaceCache"; cachePath = !cachePath.isEmpty() ? cachePath : "interfaceCache";
QNetworkDiskCache* cache = new QNetworkDiskCache(); QNetworkDiskCache* cache = new QNetworkDiskCache();
cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE);
cache->setCacheDirectory(cachePath); cache->setCacheDirectory(cachePath);
networkAccessManager.setCache(cache); networkAccessManager.setCache(cache);
qCDebug(asset_client) << "AssetClient disk cache setup at" << cachePath qDebug() << "ResourceManager disk cache setup at" << cachePath
<< "(size:" << MAXIMUM_CACHE_SIZE / BYTES_PER_GIGABYTES << "GB)"; << "(size:" << MAXIMUM_CACHE_SIZE / BYTES_PER_GIGABYTES << "GB)";
}
}
void AssetClient::cacheInfoRequest(QObject* reciever, QString slot) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "cacheInfoRequest", Qt::QueuedConnection,
Q_ARG(QObject*, reciever), Q_ARG(QString, slot));
return;
}
if (auto* cache = qobject_cast<QNetworkDiskCache*>(NetworkAccessManager::getInstance().cache())) {
QMetaObject::invokeMethod(reciever, slot.toStdString().data(), Qt::QueuedConnection,
Q_ARG(QString, cache->cacheDirectory()),
Q_ARG(qint64, cache->cacheSize()),
Q_ARG(qint64, cache->maximumCacheSize()));
} else {
qCWarning(asset_client) << "No disk cache to get info from.";
}
}
void AssetClient::clearCache() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "clearCache", Qt::QueuedConnection);
return;
}
if (auto cache = NetworkAccessManager::getInstance().cache()) {
qDebug() << "AssetClient::clearCache(): Clearing disk cache.";
cache->clear();
} else {
qCWarning(asset_client) << "No disk cache to clear.";
} }
} }

View file

@ -43,13 +43,17 @@ class AssetClient : public QObject, public Dependency {
Q_OBJECT Q_OBJECT
public: public:
AssetClient(); AssetClient();
Q_INVOKABLE void init();
Q_INVOKABLE AssetRequest* createRequest(const QString& hash, const QString& extension); Q_INVOKABLE AssetRequest* createRequest(const QString& hash, const QString& extension);
Q_INVOKABLE AssetUpload* createUpload(const QString& filename); Q_INVOKABLE AssetUpload* createUpload(const QString& filename);
Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data, const QString& extension); Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data, const QString& extension);
public slots:
void init();
void cacheInfoRequest(QObject* reciever, QString slot);
void clearCache();
private slots: private slots:
void handleAssetGetInfoReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode); void handleAssetGetInfoReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleAssetGetReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode); void handleAssetGetReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);

View file

@ -20,7 +20,7 @@
class AssetResourceRequest : public ResourceRequest { class AssetResourceRequest : public ResourceRequest {
Q_OBJECT Q_OBJECT
public: public:
AssetResourceRequest(QObject* parent, const QUrl& url) : ResourceRequest(parent, url) { } AssetResourceRequest(const QUrl& url) : ResourceRequest(url) { }
~AssetResourceRequest(); ~AssetResourceRequest();
protected: protected:

View file

@ -98,6 +98,8 @@ void DomainHandler::softReset() {
} }
void DomainHandler::hardReset() { void DomainHandler::hardReset() {
emit resetting();
softReset(); softReset();
qCDebug(networking) << "Hard reset in NodeList DomainHandler."; qCDebug(networking) << "Hard reset in NodeList DomainHandler.";

View file

@ -104,6 +104,7 @@ signals:
// It means that, either from DNS lookup or ICE, we think we have a socket we can talk to DS on // It means that, either from DNS lookup or ICE, we think we have a socket we can talk to DS on
void completedSocketDiscovery(); void completedSocketDiscovery();
void resetting();
void connectedToDomain(const QString& hostname); void connectedToDomain(const QString& hostname);
void disconnectedFromDomain(); void disconnectedFromDomain();

View file

@ -19,7 +19,7 @@
class FileResourceRequest : public ResourceRequest { class FileResourceRequest : public ResourceRequest {
Q_OBJECT Q_OBJECT
public: public:
FileResourceRequest(QObject* parent, const QUrl& url) : ResourceRequest(parent, url) { } FileResourceRequest(const QUrl& url) : ResourceRequest(url) { }
protected: protected:
virtual void doSend() override; virtual void doSend() override;

View file

@ -28,6 +28,25 @@ HTTPResourceRequest::~HTTPResourceRequest() {
} }
} }
void HTTPResourceRequest::setupTimer() {
Q_ASSERT(!_sendTimer);
static const int TIMEOUT_MS = 10000;
_sendTimer = new QTimer();
connect(this, &QObject::destroyed, _sendTimer, &QTimer::deleteLater);
connect(_sendTimer, &QTimer::timeout, this, &HTTPResourceRequest::onTimeout);
_sendTimer->setSingleShot(true);
_sendTimer->start(TIMEOUT_MS);
}
void HTTPResourceRequest::cleanupTimer() {
Q_ASSERT(_sendTimer);
_sendTimer->disconnect(this);
_sendTimer->deleteLater();
_sendTimer = nullptr;
}
void HTTPResourceRequest::doSend() { void HTTPResourceRequest::doSend() {
QNetworkRequest networkRequest(_url); QNetworkRequest networkRequest(_url);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
@ -42,18 +61,15 @@ void HTTPResourceRequest::doSend() {
connect(_reply, &QNetworkReply::finished, this, &HTTPResourceRequest::onRequestFinished); connect(_reply, &QNetworkReply::finished, this, &HTTPResourceRequest::onRequestFinished);
connect(_reply, &QNetworkReply::downloadProgress, this, &HTTPResourceRequest::onDownloadProgress); connect(_reply, &QNetworkReply::downloadProgress, this, &HTTPResourceRequest::onDownloadProgress);
connect(&_sendTimer, &QTimer::timeout, this, &HTTPResourceRequest::onTimeout);
static const int TIMEOUT_MS = 10000; setupTimer();
_sendTimer.setSingleShot(true);
_sendTimer.start(TIMEOUT_MS);
} }
void HTTPResourceRequest::onRequestFinished() { void HTTPResourceRequest::onRequestFinished() {
Q_ASSERT(_state == InProgress); Q_ASSERT(_state == InProgress);
Q_ASSERT(_reply); Q_ASSERT(_reply);
_sendTimer.stop(); cleanupTimer();
switch(_reply->error()) { switch(_reply->error()) {
case QNetworkReply::NoError: case QNetworkReply::NoError:
@ -80,7 +96,7 @@ void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
Q_ASSERT(_state == InProgress); Q_ASSERT(_state == InProgress);
// We've received data, so reset the timer // We've received data, so reset the timer
_sendTimer.start(); _sendTimer->start();
emit progress(bytesReceived, bytesTotal); emit progress(bytesReceived, bytesTotal);
} }
@ -91,6 +107,8 @@ void HTTPResourceRequest::onTimeout() {
_reply->abort(); _reply->abort();
_reply->deleteLater(); _reply->deleteLater();
_reply = nullptr; _reply = nullptr;
cleanupTimer();
_result = Timeout; _result = Timeout;
_state = Finished; _state = Finished;

View file

@ -21,7 +21,7 @@
class HTTPResourceRequest : public ResourceRequest { class HTTPResourceRequest : public ResourceRequest {
Q_OBJECT Q_OBJECT
public: public:
HTTPResourceRequest(QObject* parent, const QUrl& url) : ResourceRequest(parent, url) { } HTTPResourceRequest(const QUrl& url) : ResourceRequest(url) { }
~HTTPResourceRequest(); ~HTTPResourceRequest();
protected: protected:
@ -33,7 +33,10 @@ private slots:
void onRequestFinished(); void onRequestFinished();
private: private:
QTimer _sendTimer; void setupTimer();
void cleanupTimer();
QTimer* _sendTimer { nullptr };
QNetworkReply* _reply { nullptr }; QNetworkReply* _reply { nullptr };
}; };

View file

@ -11,12 +11,20 @@
#include "ResourceManager.h" #include "ResourceManager.h"
#include "AssetResourceRequest.h" #include <QNetworkDiskCache>
#include "FileResourceRequest.h" #include <QStandardPaths>
#include "HTTPResourceRequest.h" #include <QThread>
#include <SharedUtil.h> #include <SharedUtil.h>
#include "AssetResourceRequest.h"
#include "FileResourceRequest.h"
#include "HTTPResourceRequest.h"
#include "NetworkAccessManager.h"
QThread ResourceManager::_thread;
ResourceManager::PrefixMap ResourceManager::_prefixMap; ResourceManager::PrefixMap ResourceManager::_prefixMap;
QMutex ResourceManager::_prefixMapLock; QMutex ResourceManager::_prefixMapLock;
@ -67,18 +75,41 @@ QUrl ResourceManager::normalizeURL(const QUrl& originalUrl) {
return url; return url;
} }
void ResourceManager::init() {
_thread.setObjectName("Ressource Manager Thread");
auto assetClient = DependencyManager::set<AssetClient>();
assetClient->moveToThread(&_thread);
QObject::connect(&_thread, &QThread::started, assetClient.data(), &AssetClient::init);
_thread.start();
}
void ResourceManager::cleanup() {
// cleanup the AssetClient thread
DependencyManager::destroy<AssetClient>();
_thread.quit();
_thread.wait();
}
ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const QUrl& url) { ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const QUrl& url) {
auto normalizedURL = normalizeURL(url); auto normalizedURL = normalizeURL(url);
auto scheme = normalizedURL.scheme(); auto scheme = normalizedURL.scheme();
ResourceRequest* request = nullptr;
if (scheme == URL_SCHEME_FILE) { if (scheme == URL_SCHEME_FILE) {
return new FileResourceRequest(parent, normalizedURL); request = new FileResourceRequest(normalizedURL);
} else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) { } else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) {
return new HTTPResourceRequest(parent, normalizedURL); request = new HTTPResourceRequest(normalizedURL);
} else if (scheme == URL_SCHEME_ATP) { } else if (scheme == URL_SCHEME_ATP) {
return new AssetResourceRequest(parent, normalizedURL); request = new AssetResourceRequest(normalizedURL);
} else {
qDebug() << "Unknown scheme (" << scheme << ") for URL: " << url.url();
return nullptr;
} }
Q_ASSERT(request);
qDebug() << "Unknown scheme (" << scheme << ") for URL: " << url.url(); request->moveToThread(&_thread);
return request;
return nullptr;
} }

View file

@ -29,8 +29,15 @@ public:
static void setUrlPrefixOverride(const QString& prefix, const QString& replacement); static void setUrlPrefixOverride(const QString& prefix, const QString& replacement);
static QString normalizeURL(const QString& urlString); static QString normalizeURL(const QString& urlString);
static QUrl normalizeURL(const QUrl& url); static QUrl normalizeURL(const QUrl& url);
static ResourceRequest* createResourceRequest(QObject* parent, const QUrl& url); static ResourceRequest* createResourceRequest(QObject* parent, const QUrl& url);
static void init();
static void cleanup();
private: private:
static QThread _thread;
using PrefixMap = std::map<QString, QString>; using PrefixMap = std::map<QString, QString>;
static PrefixMap _prefixMap; static PrefixMap _prefixMap;

View file

@ -11,12 +11,15 @@
#include "ResourceRequest.h" #include "ResourceRequest.h"
ResourceRequest::ResourceRequest(QObject* parent, const QUrl& url) : #include <QtCore/QThread>
QObject(parent),
_url(url) { ResourceRequest::ResourceRequest(const QUrl& url) : _url(url) { }
}
void ResourceRequest::send() { void ResourceRequest::send() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "send", Qt::QueuedConnection);
return;
}
Q_ASSERT(_state == NotStarted); Q_ASSERT(_state == NotStarted);
_state = InProgress; _state = InProgress;

View file

@ -20,7 +20,7 @@
class ResourceRequest : public QObject { class ResourceRequest : public QObject {
Q_OBJECT Q_OBJECT
public: public:
ResourceRequest(QObject* parent, const QUrl& url); ResourceRequest(const QUrl& url);
enum State { enum State {
NotStarted = 0, NotStarted = 0,
@ -38,7 +38,6 @@ public:
NotFound NotFound
}; };
void send();
QByteArray getData() { return _data; } QByteArray getData() { return _data; }
State getState() const { return _state; } State getState() const { return _state; }
Result getResult() const { return _result; } Result getResult() const { return _result; }
@ -47,8 +46,11 @@ public:
void setCacheEnabled(bool value) { _cacheEnabled = value; } void setCacheEnabled(bool value) { _cacheEnabled = value; }
public slots:
void send();
signals: signals:
void progress(uint64_t bytesReceived, uint64_t bytesTotal); void progress(qint64 bytesReceived, qint64 bytesTotal);
void finished(); void finished();
protected: protected:

View file

@ -58,7 +58,7 @@ BasePacket::BasePacket(qint64 size) {
Q_ASSERT(size >= 0 || size < maxPayload); Q_ASSERT(size >= 0 || size < maxPayload);
_packetSize = size; _packetSize = size;
_packet.reset(new char[_packetSize]); _packet.reset(new char[_packetSize]());
_payloadCapacity = _packetSize; _payloadCapacity = _packetSize;
_payloadSize = 0; _payloadSize = 0;
_payloadStart = _packet.get(); _payloadStart = _packet.get();

View file

@ -87,7 +87,8 @@ SendQueue& Connection::getSendQueue() {
// receiver is getting the sequence numbers it expects (given that the connection must still be active) // receiver is getting the sequence numbers it expects (given that the connection must still be active)
// Lasily create send queue // Lasily create send queue
_sendQueue = SendQueue::create(_parentSocket, _destination, _inactiveSendQueueSequenceNumber); _sendQueue = SendQueue::create(_parentSocket, _destination);
_lastReceivedACK = _sendQueue->getCurrentSequenceNumber();
#ifdef UDT_CONNECTION_DEBUG #ifdef UDT_CONNECTION_DEBUG
qCDebug(networking) << "Created SendQueue for connection to" << _destination; qCDebug(networking) << "Created SendQueue for connection to" << _destination;
@ -109,10 +110,6 @@ SendQueue& Connection::getSendQueue() {
} }
void Connection::queueInactive() { void Connection::queueInactive() {
// get the current sequence number from the send queue, this is to be re-used if the send
// queue is re-activated for this connection
_inactiveSendQueueSequenceNumber = _sendQueue->getCurrentSequenceNumber();
// tell our current send queue to go down and reset our ptr to it to null // tell our current send queue to go down and reset our ptr to it to null
stopSendQueue(); stopSendQueue();
@ -728,15 +725,28 @@ void Connection::processNAK(std::unique_ptr<ControlPacket> controlPacket) {
} }
void Connection::processHandshake(std::unique_ptr<ControlPacket> controlPacket) { void Connection::processHandshake(std::unique_ptr<ControlPacket> controlPacket) {
SequenceNumber initialSequenceNumber;
controlPacket->readPrimitive(&initialSequenceNumber);
if (!_hasReceivedHandshake || _isReceivingData) { if (!_hasReceivedHandshake || initialSequenceNumber != _initialReceiveSequenceNumber) {
// server sent us a handshake - we need to assume this means state should be reset // server sent us a handshake - we need to assume this means state should be reset
// as long as we haven't received a handshake yet or we have and we've received some data // as long as we haven't received a handshake yet or we have and we've received some data
#ifdef UDT_CONNECTION_DEBUG
if (initialSequenceNumber != _initialReceiveSequenceNumber) {
qCDebug(networking) << "Resetting receive state, received a new initial sequence number in handshake";
}
#endif
resetReceiveState(); resetReceiveState();
_initialReceiveSequenceNumber = initialSequenceNumber;
_lastReceivedSequenceNumber = initialSequenceNumber - 1;
_lastSentACK = initialSequenceNumber - 1;
} }
// immediately respond with a handshake ACK // immediately respond with a handshake ACK
static auto handshakeACK = ControlPacket::create(ControlPacket::HandshakeACK, 0); static auto handshakeACK = ControlPacket::create(ControlPacket::HandshakeACK, sizeof(SequenceNumber));
handshakeACK->seek(0);
handshakeACK->writePrimitive(initialSequenceNumber);
_parentSocket->writeBasePacket(*handshakeACK, _destination); _parentSocket->writeBasePacket(*handshakeACK, _destination);
// indicate that handshake has been received // indicate that handshake has been received
@ -746,8 +756,11 @@ void Connection::processHandshake(std::unique_ptr<ControlPacket> controlPacket)
void Connection::processHandshakeACK(std::unique_ptr<ControlPacket> controlPacket) { void Connection::processHandshakeACK(std::unique_ptr<ControlPacket> controlPacket) {
// if we've decided to clean up the send queue then this handshake ACK should be ignored, it's useless // if we've decided to clean up the send queue then this handshake ACK should be ignored, it's useless
if (_sendQueue) { if (_sendQueue) {
SequenceNumber initialSequenceNumber;
controlPacket->readPrimitive(&initialSequenceNumber);
// hand off this handshake ACK to the send queue so it knows it can start sending // hand off this handshake ACK to the send queue so it knows it can start sending
getSendQueue().handshakeACK(); getSendQueue().handshakeACK(initialSequenceNumber);
// indicate that handshake ACK was received // indicate that handshake ACK was received
_hasReceivedHandshakeACK = true; _hasReceivedHandshakeACK = true;

View file

@ -130,7 +130,9 @@ private:
bool _isReceivingData { false }; // flag used for expiry of receipt portion of connection bool _isReceivingData { false }; // flag used for expiry of receipt portion of connection
bool _isActive { true }; // flag used for inactivity of connection bool _isActive { true }; // flag used for inactivity of connection
SequenceNumber _initialReceiveSequenceNumber; // Randomized by peer SendQueue on creation, identifies connection during re-connect requests
LossList _lossList; // List of all missing packets LossList _lossList; // List of all missing packets
SequenceNumber _lastReceivedSequenceNumber; // The largest sequence number received from the peer SequenceNumber _lastReceivedSequenceNumber; // The largest sequence number received from the peer
SequenceNumber _lastReceivedACK; // The last ACK received SequenceNumber _lastReceivedACK; // The last ACK received
@ -140,8 +142,6 @@ private:
SequenceNumber _lastSentACK; // The last sent ACK SequenceNumber _lastSentACK; // The last sent ACK
SequenceNumber _lastSentACK2; // The last sent ACK sub-sequence number in an ACK2 SequenceNumber _lastSentACK2; // The last sent ACK sub-sequence number in an ACK2
SequenceNumber _inactiveSendQueueSequenceNumber { 0 };
int _acksDuringSYN { 1 }; // The number of non-SYN ACKs sent during SYN int _acksDuringSYN { 1 }; // The number of non-SYN ACKs sent during SYN
int _lightACKsDuringSYN { 1 }; // The number of lite ACKs sent during SYN interval int _lightACKsDuringSYN { 1 }; // The number of lite ACKs sent during SYN interval

View file

@ -16,6 +16,10 @@
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtCore/QMetaEnum> #include <QtCore/QMetaEnum>
Q_DECLARE_METATYPE(PacketType);
static int packetTypeMetaTypeId = qRegisterMetaType<PacketType>();
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>() const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
<< PacketType::NodeJsonStats << PacketType::EntityQuery << PacketType::NodeJsonStats << PacketType::EntityQuery
<< PacketType::OctreeDataNack << PacketType::EntityEditNack << PacketType::OctreeDataNack << PacketType::EntityEditNack
@ -38,6 +42,8 @@ const QSet<PacketType> RELIABLE_PACKETS = QSet<PacketType>();
PacketVersion versionForPacketType(PacketType packetType) { PacketVersion versionForPacketType(PacketType packetType) {
switch (packetType) { switch (packetType) {
case PacketType::DomainList:
return 18;
case PacketType::EntityAdd: case PacketType::EntityAdd:
case PacketType::EntityEdit: case PacketType::EntityEdit:
case PacketType::EntityData: case PacketType::EntityData:

View file

@ -12,6 +12,7 @@
#include "SendQueue.h" #include "SendQueue.h"
#include <algorithm> #include <algorithm>
#include <random>
#include <thread> #include <thread>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
@ -53,10 +54,10 @@ private:
Mutex2& _mutex2; Mutex2& _mutex2;
}; };
std::unique_ptr<SendQueue> SendQueue::create(Socket* socket, HifiSockAddr destination, SequenceNumber currentSequenceNumber) { std::unique_ptr<SendQueue> SendQueue::create(Socket* socket, HifiSockAddr destination) {
Q_ASSERT_X(socket, "SendQueue::create", "Must be called with a valid Socket*"); Q_ASSERT_X(socket, "SendQueue::create", "Must be called with a valid Socket*");
auto queue = std::unique_ptr<SendQueue>(new SendQueue(socket, destination, currentSequenceNumber)); auto queue = std::unique_ptr<SendQueue>(new SendQueue(socket, destination));
// Setup queue private thread // Setup queue private thread
QThread* thread = new QThread; QThread* thread = new QThread;
@ -75,12 +76,23 @@ std::unique_ptr<SendQueue> SendQueue::create(Socket* socket, HifiSockAddr destin
return queue; return queue;
} }
SendQueue::SendQueue(Socket* socket, HifiSockAddr dest, SequenceNumber currentSequenceNumber) : SendQueue::SendQueue(Socket* socket, HifiSockAddr dest) :
_socket(socket), _socket(socket),
_destination(dest), _destination(dest)
_currentSequenceNumber(currentSequenceNumber)
{ {
// setup psuedo-random number generation for all instances of SendQueue
static std::random_device rd;
static std::mt19937 generator(rd());
static std::uniform_int_distribution<> distribution(0, SequenceNumber::MAX);
// randomize the intial sequence number
_initialSequenceNumber = SequenceNumber(distribution(generator));
// set our member variables from randomized initial number
_currentSequenceNumber = _initialSequenceNumber - 1;
_atomicCurrentSequenceNumber = uint32_t(_currentSequenceNumber);
_lastACKSequenceNumber = uint32_t(_currentSequenceNumber) - 1;
} }
void SendQueue::queuePacket(std::unique_ptr<Packet> packet) { void SendQueue::queuePacket(std::unique_ptr<Packet> packet) {
@ -190,7 +202,11 @@ void SendQueue::sendHandshake() {
std::unique_lock<std::mutex> handshakeLock { _handshakeMutex }; std::unique_lock<std::mutex> handshakeLock { _handshakeMutex };
if (!_hasReceivedHandshakeACK) { if (!_hasReceivedHandshakeACK) {
// we haven't received a handshake ACK from the client, send another now // we haven't received a handshake ACK from the client, send another now
static const auto handshakePacket = ControlPacket::create(ControlPacket::Handshake, 0); static const auto handshakePacket = ControlPacket::create(ControlPacket::Handshake, sizeof(SequenceNumber));
handshakePacket->seek(0);
handshakePacket->writePrimitive(_initialSequenceNumber);
_socket->writeBasePacket(*handshakePacket, _destination); _socket->writeBasePacket(*handshakePacket, _destination);
// we wait for the ACK or the re-send interval to expire // we wait for the ACK or the re-send interval to expire
@ -199,14 +215,16 @@ void SendQueue::sendHandshake() {
} }
} }
void SendQueue::handshakeACK() { void SendQueue::handshakeACK(SequenceNumber initialSequenceNumber) {
{ if (initialSequenceNumber == _initialSequenceNumber) {
std::lock_guard<std::mutex> locker { _handshakeMutex }; {
_hasReceivedHandshakeACK = true; std::lock_guard<std::mutex> locker { _handshakeMutex };
_hasReceivedHandshakeACK = true;
}
// Notify on the handshake ACK condition
_handshakeACKCondition.notify_one();
} }
// Notify on the handshake ACK condition
_handshakeACKCondition.notify_one();
} }
SequenceNumber SendQueue::getNextSequenceNumber() { SequenceNumber SendQueue::getNextSequenceNumber() {

View file

@ -50,8 +50,7 @@ public:
Stopped Stopped
}; };
static std::unique_ptr<SendQueue> create(Socket* socket, HifiSockAddr destination, static std::unique_ptr<SendQueue> create(Socket* socket, HifiSockAddr destination);
SequenceNumber currentSequenceNumber = SequenceNumber());
void queuePacket(std::unique_ptr<Packet> packet); void queuePacket(std::unique_ptr<Packet> packet);
void queuePacketList(std::unique_ptr<PacketList> packetList); void queuePacketList(std::unique_ptr<PacketList> packetList);
@ -72,7 +71,7 @@ public slots:
void ack(SequenceNumber ack); void ack(SequenceNumber ack);
void nak(SequenceNumber start, SequenceNumber end); void nak(SequenceNumber start, SequenceNumber end);
void overrideNAKListFromPacket(ControlPacket& packet); void overrideNAKListFromPacket(ControlPacket& packet);
void handshakeACK(); void handshakeACK(SequenceNumber initialSequenceNumber);
signals: signals:
void packetSent(int dataSize, int payloadSize); void packetSent(int dataSize, int payloadSize);
@ -84,7 +83,7 @@ private slots:
void run(); void run();
private: private:
SendQueue(Socket* socket, HifiSockAddr dest, SequenceNumber currentSequenceNumber); SendQueue(Socket* socket, HifiSockAddr dest);
SendQueue(SendQueue& other) = delete; SendQueue(SendQueue& other) = delete;
SendQueue(SendQueue&& other) = delete; SendQueue(SendQueue&& other) = delete;
@ -106,6 +105,8 @@ private:
Socket* _socket { nullptr }; // Socket to send packet on Socket* _socket { nullptr }; // Socket to send packet on
HifiSockAddr _destination; // Destination addr HifiSockAddr _destination; // Destination addr
SequenceNumber _initialSequenceNumber; // Randomized on SendQueue creation, identifies connection during re-connect requests
std::atomic<uint32_t> _lastACKSequenceNumber { 0 }; // Last ACKed sequence number std::atomic<uint32_t> _lastACKSequenceNumber { 0 }; // Last ACKed sequence number

View file

@ -19,13 +19,20 @@ vec3 fresnelSchlick(vec3 fresnelColor, vec3 lightDir, vec3 halfDir) {
} }
float specularDistribution(float roughness, vec3 normal, vec3 halfDir) { float specularDistribution(float roughness, vec3 normal, vec3 halfDir) {
float gloss = (1.0 - roughness) * 128.0; float ndoth = clamp(dot(halfDir, normal), 0.0, 1.0);
gloss *= gloss; float gloss2 = pow(roughness, 4);
float denom = (ndoth * ndoth*(gloss2 - 1) + 1);
float power = gloss2 / (3.14159 * denom * denom);
return power;
}
/*
float specularDistribution(float roughness, vec3 normal, vec3 halfDir) {
float gloss = exp2(10 * (1.0 - roughness) + 1);
float power = pow(clamp(dot(halfDir, normal), 0.0, 1.0), gloss); float power = pow(clamp(dot(halfDir, normal), 0.0, 1.0), gloss);
power *= (gloss * 0.125 + 0.25); power *= (gloss * 0.125 + 0.25);
return power; return power;
} }
*/
// Frag Shading returns the diffuse amount as W and the specular rgb as xyz // Frag Shading returns the diffuse amount as W and the specular rgb as xyz
vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float roughness) { vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float roughness) {
// Diffuse Lighting // Diffuse Lighting