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);
auto assetClient = DependencyManager::set<AssetClient>();
QThread* assetThread = new QThread;
assetThread->setObjectName("Asset Thread");
assetClient->moveToThread(assetThread);
connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init);
assetThread->start();
ResourceManager::init();
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
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
// cleanup the AssetClient thread
QThread* assetThread = DependencyManager::get<AssetClient>()->thread();
DependencyManager::destroy<AssetClient>();
assetThread->quit();
assetThread->wait();
ResourceManager::cleanup();
// cleanup the AudioInjectorManager (and any still running injectors)
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.
// 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 = {
width: OVERLAY_WIDTH,
height: OVERLAY_HEIGHT,
imageURL: "http://hifi-content.s3.amazonaws.com/alan/production/images/images/Overlay-Viz-blank.png",
color: {red: 255, green: 255, blue: 255},
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
// 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.
@ -64,29 +81,74 @@ function stopAwayAnimation() {
// OVERLAY
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() {
var properties = {visible: true},
// Update for current screen size, keeping overlay proportions constant.
screen = Controller.getViewportDimensions(),
screenRatio = screen.x / screen.y;
if (screenRatio < OVERLAY_RATIO) {
properties.width = screen.x;
properties.height = screen.x / OVERLAY_RATIO;
properties.x = 0;
properties.y = (screen.y - properties.height) / 2;
var properties = {visible: true};
if (HMD.active) {
// make sure desktop version is hidden
Overlays.editOverlay(overlay, { visible: false });
lastOverlayPosition = HMD.getHUDLookAtPosition3D();
var actualOverlayPositon = moveCloserToCamera(lastOverlayPosition);
Overlays.editOverlay(overlayHMD, { visible: true, position: actualOverlayPositon });
} else {
properties.height = screen.y;
properties.width = screen.y * OVERLAY_RATIO;
properties.y = 0;
properties.x = (screen.x - properties.width) / 2;
// make sure HMD is hidden
Overlays.editOverlay(overlayHMD, { visible: false });
// 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() {
Overlays.editOverlay(overlay, {visible: false});
Overlays.editOverlay(overlayHMD, {visible: false});
}
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
var wasMuted, isAway;
@ -106,6 +168,12 @@ function goAway() {
MyAvatar.setEnableMeshVisible(false); // just for our own display, without changing point of view
playAwayAnimation(); // animation is still seen by others
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() {
if (!isAway) {
@ -119,13 +187,20 @@ function goActive() {
MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting.
stopAwayAnimation();
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) {
if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it)
return;
}
if (!isAway && (event.text === '.')) {
if (!isAway && (event.text == 'ESC')) {
goAway();
} else {
goActive();
@ -141,10 +216,8 @@ function maybeGoAway() {
}
}
// If the mouse has gone from captured, to non-captured state,
// 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
// If the mouse has gone from captured, to non-captured state, then it likely means the person is still in the HMD, but
// 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) {
wasMouseCaptured = !wasMouseCaptured;
if (!wasMouseCaptured) {
@ -153,6 +226,8 @@ function maybeGoAway() {
}
}
Script.update.connect(maybeMoveOverlay);
Script.update.connect(maybeGoAway);
Controller.mousePressEvent.connect(goActive);
Controller.keyPressEvent.connect(maybeGoActive);

View file

@ -35,8 +35,8 @@ var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 50;
Controller.mouseMoveEvent.connect(function(mouseEvent) {
var now = Date.now();
// if the reticle is hidden, show it...
if (!Reticle.visible) {
// if the reticle is hidden, and we're not in away mode...
if (!Reticle.visible && Reticle.allowMouseCapture) {
Reticle.visible = true;
if (HMD.active) {
shouldSeekToLookAt = true;
@ -44,7 +44,7 @@ Controller.mouseMoveEvent.connect(function(mouseEvent) {
} else {
// 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
if (HMD.active && !shouldSeekToLookAt) {
if (HMD.active && !shouldSeekToLookAt && Reticle.allowMouseCapture) {
var dx = Reticle.position.x - lastMouseX;
var dy = Reticle.position.y - lastMouseY;
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<PathUtils>();
DependencyManager::set<InterfaceActionFactory>();
DependencyManager::set<AssetClient>();
DependencyManager::set<AudioInjectorManager>();
DependencyManager::set<MessagesClient>();
DependencyManager::set<UserInputMapper>();
@ -528,13 +527,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
audioThread->start();
// Setup AssetClient
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();
ResourceManager::init();
// Setup MessagesClient
auto messagesClient = DependencyManager::get<MessagesClient>();
@ -547,7 +540,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
const DomainHandler& domainHandler = nodeList->getDomainHandler();
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(disconnectedFromDomain()), SLOT(updateWindowTitle()));
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);
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);
_glWidget = new GLCanvas();
@ -661,15 +647,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_glWidget->setFocusPolicy(Qt::StrongFocus);
_glWidget->setFocus();
#ifdef Q_OS_MAC
// OSX doesn't seem to provide for hiding the cursor only on the GL widget
_window->setCursor(Qt::BlankCursor);
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
_glWidget->setCursor(Qt::BlankCursor);
// 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(Qt::BlankCursor);
// enable mouse tracking; otherwise, we only get drag events
_glWidget->setMouseTracking(true);
@ -981,6 +967,29 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_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() {
emit beforeAboutToQuit();
@ -1062,13 +1071,6 @@ void Application::cleanupBeforeQuit() {
DependencyManager::destroy<OffscreenUi>();
}
void Application::emptyLocalCache() {
if (auto cache = NetworkAccessManager::getInstance().cache()) {
qDebug() << "DiskCacheEditor::clear(): Clearing disk cache.";
cache->clear();
}
}
Application::~Application() {
EntityTreePointer tree = getEntities()->getTree();
tree->setSimulation(NULL);
@ -1106,11 +1108,7 @@ Application::~Application() {
DependencyManager::destroy<ScriptCache>();
DependencyManager::destroy<SoundCache>();
// cleanup the AssetClient thread
QThread* assetThread = DependencyManager::get<AssetClient>()->thread();
DependencyManager::destroy<AssetClient>();
assetThread->quit();
assetThread->wait();
ResourceManager::cleanup();
QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
@ -2431,6 +2429,9 @@ void Application::idle(uint64_t now) {
return; // bail early, nothing to do here.
}
checkChangeCursor();
Stats::getInstance()->updateStats();
AvatarInputs::getInstance()->update();
@ -3055,7 +3056,7 @@ void Application::reloadResourceCaches() {
_viewFrustum.setOrientation(glm::quat());
queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
emptyLocalCache();
DependencyManager::get<AssetClient>()->clearCache();
DependencyManager::get<AnimationCache>()->refreshAll();
DependencyManager::get<ModelCache>()->refreshAll();
@ -3978,13 +3979,8 @@ void Application::handleDomainConnectionDeniedPacket(QSharedPointer<ReceivedMess
AccountManager::getInstance().checkAndSignalForAccessToken();
}
void Application::connectedToDomain(const QString& hostname) {
AccountManager& accountManager = AccountManager::getInstance();
const QUuid& domainID = DependencyManager::get<NodeList>()->getDomainHandler().getUUID();
if (accountManager.isLoggedIn() && !domainID.isNull()) {
_notifiedPacketVersionMismatchThisDomain = false;
}
void Application::resettingDomain() {
_notifiedPacketVersionMismatchThisDomain = false;
}
void Application::nodeAdded(SharedNodePointer node) {

View file

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

View file

@ -337,9 +337,21 @@ QPointF ApplicationCompositor::getMouseEventPosition(QMouseEvent* event) {
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
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() {
if (shouldCaptureMouse()) {

View file

@ -106,6 +106,9 @@ public:
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
bool getReticleOverDesktop() const;
void setReticleOverDesktop(bool value) { _isOverDesktop = value; }
@ -162,6 +165,8 @@ private:
bool _reticleOverQml { false };
bool _allowMouseCapture { true };
ReticleInterface* _reticleInterface;
};
@ -173,12 +178,17 @@ class ReticleInterface : public QObject {
Q_PROPERTY(float depth READ getDepth WRITE setDepth)
Q_PROPERTY(glm::vec2 maximumPosition READ getMaximumPosition)
Q_PROPERTY(bool mouseCaptured READ isMouseCaptured)
Q_PROPERTY(bool allowMouseCapture READ getAllowMouseCapture WRITE setAllowMouseCapture)
Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay)
public:
ReticleInterface(ApplicationCompositor* outer) : QObject(outer), _compositor(outer) {}
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 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
//
#include <functional>
#include "DiskCacheEditor.h"
#include <QDebug>
#include <QDialog>
#include <QGridLayout>
#include <QPushButton>
#include <QLabel>
#include <QNetworkDiskCache>
#include <QTimer>
#include <QMessageBox>
#include <NetworkAccessManager.h>
#include <AssetClient.h>
#include "DiskCacheEditor.h"
#include "OffscreenUi.h"
DiskCacheEditor::DiskCacheEditor(QWidget* parent) : QObject(parent) {
}
QWindow* DiskCacheEditor::windowHandle() {
@ -33,7 +31,6 @@ QWindow* DiskCacheEditor::windowHandle() {
}
void DiskCacheEditor::toggle() {
qDebug() << "DiskCacheEditor::toggle()";
if (!_dialog) {
makeDialog();
}
@ -88,17 +85,17 @@ void DiskCacheEditor::makeDialog() {
Q_CHECK_PTR(_maxSize);
_maxSize->setAlignment(Qt::AlignLeft);
layout->addWidget(_maxSize, 2, 1, 1, 3);
refresh();
QPushButton* refreshCacheButton = new QPushButton(_dialog);
Q_CHECK_PTR(refreshCacheButton);
refreshCacheButton->setText("Refresh");
refreshCacheButton->setToolTip("Reload the cache stats.");
connect(refreshCacheButton, SIGNAL(clicked()), SLOT(refresh()));
layout->addWidget(refreshCacheButton, 3, 2);
static const int REFRESH_INTERVAL = 100; // msec
_refreshTimer = new QTimer(_dialog);
_refreshTimer->setInterval(REFRESH_INTERVAL);
_refreshTimer->setSingleShot(false);
QObject::connect(_refreshTimer.data(), &QTimer::timeout, this, &DiskCacheEditor::refresh);
_refreshTimer->start();
QPushButton* clearCacheButton = new QPushButton(_dialog);
Q_CHECK_PTR(clearCacheButton);
clearCacheButton->setText("Clear");
@ -108,7 +105,11 @@ void DiskCacheEditor::makeDialog() {
}
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 qint64 CHUNK = 1024;
QString unit;
@ -122,30 +123,24 @@ void DiskCacheEditor::refresh() {
}
return QString("%0 %1").arg(number).arg(UNITS[i]);
};
QNetworkDiskCache* cache = qobject_cast<QNetworkDiskCache*>(NetworkAccessManager::getInstance().cache());
if (_path) {
_path->setText(cache->cacheDirectory());
_path->setText(cacheDirectory);
}
if (_size) {
_size->setText(stringify(cache->cacheSize()));
_size->setText(stringify(cacheSize));
}
if (_maxSize) {
_maxSize->setText(stringify(cache->maximumCacheSize()));
_maxSize->setText(stringify(maximumCacheSize));
}
}
void DiskCacheEditor::clear() {
QMessageBox::StandardButton buttonClicked =
OffscreenUi::question(_dialog, "Clearing disk cache",
"You are about to erase all the content of the disk cache, "
"are you sure you want to do that?",
QMessageBox::Ok | QMessageBox::Cancel);
auto buttonClicked = OffscreenUi::question(_dialog, "Clearing disk cache",
"You are about to erase all the content of the disk cache, "
"are you sure you want to do that?",
QMessageBox::Ok | QMessageBox::Cancel);
if (buttonClicked == QMessageBox::Ok) {
if (auto cache = NetworkAccessManager::getInstance().cache()) {
qDebug() << "DiskCacheEditor::clear(): Clearing disk cache.";
cache->clear();
}
DependencyManager::get<AssetClient>()->clearCache();
}
refresh();
}

View file

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

View file

@ -322,7 +322,7 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
_ambientTexture.clear();
} else {
_ambientTexture = textureCache->getTexture(zone->getKeyLightProperties().getAmbientURL(), CUBE_TEXTURE);
if (_ambientTexture->getGPUTexture()) {
if (_ambientTexture && _ambientTexture->isLoaded() && _ambientTexture->getGPUTexture()) {
_pendingAmbientTexture = false;
if (_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
_skyboxTexture = textureCache->getTexture(zone->getSkyboxProperties().getURL(), CUBE_TEXTURE);
if (_skyboxTexture->getGPUTexture()) {
if (_skyboxTexture && _skyboxTexture->isLoaded() && _skyboxTexture->getGPUTexture()) {
auto texture = _skyboxTexture->getGPUTexture();
skybox->setCubemap(texture);
_pendingSkyboxTexture = false;

View file

@ -338,105 +338,84 @@ static NetworkMesh* buildNetworkMesh(const FBXMesh& mesh, const QUrl& textureBas
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;
if (!material.albedoTexture.filename.isEmpty()) {
networkMaterial->albedoTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.albedoTexture.filename)), DEFAULT_TEXTURE, material.albedoTexture.content);
QObject::connect(networkMaterial->albedoTexture.data(), &NetworkTexture::networkTextureCreated,
geometry, &NetworkGeometry::textureLoaded);
networkMaterial->albedoTextureName = material.albedoTexture.name;
auto albedoMap = model::TextureMapPointer(new model::TextureMap());
albedoMap->setTextureSource(networkMaterial->albedoTexture->_textureSource);
auto albedoMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.albedoTexture, DEFAULT_TEXTURE,
networkMaterial->albedoTexture, networkMaterial->albedoTextureName);
albedoMap->setTextureTransform(material.albedoTexture.transform);
material._material->setTextureMap(model::MaterialKey::ALBEDO_MAP, albedoMap);
}
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);
networkMaterial->normalTextureName = material.normalTexture.name;
auto normalMap = model::TextureMapPointer(new model::TextureMap());
normalMap->setTextureSource(networkMaterial->normalTexture->_textureSource);
material._material->setTextureMap(model::MaterialKey::NORMAL_MAP, normalMap);
auto normalMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.normalTexture,
(material.normalTexture.isBumpmap ? BUMP_TEXTURE : NORMAL_TEXTURE),
networkMaterial->normalTexture, networkMaterial->normalTextureName);
networkMaterial->_material->setTextureMap(model::MaterialKey::NORMAL_MAP, normalMap);
}
// Roughness first or gloss maybe
if (!material.roughnessTexture.filename.isEmpty()) {
networkMaterial->roughnessTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.roughnessTexture.filename)), ROUGHNESS_TEXTURE, material.roughnessTexture.content);
networkMaterial->roughnessTextureName = material.roughnessTexture.name;
auto roughnessMap = model::TextureMapPointer(new model::TextureMap());
roughnessMap->setTextureSource(networkMaterial->roughnessTexture->_textureSource);
auto roughnessMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.roughnessTexture, ROUGHNESS_TEXTURE,
networkMaterial->roughnessTexture, networkMaterial->roughnessTextureName);
material._material->setTextureMap(model::MaterialKey::ROUGHNESS_MAP, roughnessMap);
} else if (!material.glossTexture.filename.isEmpty()) {
networkMaterial->roughnessTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.glossTexture.filename)), GLOSS_TEXTURE, material.glossTexture.content);
networkMaterial->roughnessTextureName = material.roughnessTexture.name;
auto roughnessMap = model::TextureMapPointer(new model::TextureMap());
roughnessMap->setTextureSource(networkMaterial->roughnessTexture->_textureSource);
auto roughnessMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.glossTexture, GLOSS_TEXTURE,
networkMaterial->roughnessTexture, networkMaterial->roughnessTextureName);
material._material->setTextureMap(model::MaterialKey::ROUGHNESS_MAP, roughnessMap);
}
// Metallic first or specular maybe
if (!material.metallicTexture.filename.isEmpty()) {
networkMaterial->metallicTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.metallicTexture.filename)), METALLIC_TEXTURE, material.metallicTexture.content);
networkMaterial->metallicTextureName = material.metallicTexture.name;
auto metallicMap = model::TextureMapPointer(new model::TextureMap());
metallicMap->setTextureSource(networkMaterial->metallicTexture->_textureSource);
auto metallicMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.metallicTexture, METALLIC_TEXTURE,
networkMaterial->metallicTexture, networkMaterial->metallicTextureName);
material._material->setTextureMap(model::MaterialKey::METALLIC_MAP, metallicMap);
} 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);
}
if (!material.occlusionTexture.filename.isEmpty()) {
networkMaterial->occlusionTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.occlusionTexture.filename)), OCCLUSION_TEXTURE, material.occlusionTexture.content);
networkMaterial->occlusionTextureName = material.occlusionTexture.name;
auto occlusionMap = model::TextureMapPointer(new model::TextureMap());
occlusionMap->setTextureSource(networkMaterial->occlusionTexture->_textureSource);
auto occlusionMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.occlusionTexture, OCCLUSION_TEXTURE,
networkMaterial->occlusionTexture, networkMaterial->occlusionTextureName);
material._material->setTextureMap(model::MaterialKey::OCCLUSION_MAP, occlusionMap);
}
if (!material.emissiveTexture.filename.isEmpty()) {
networkMaterial->emissiveTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.emissiveTexture.filename)), EMISSIVE_TEXTURE, material.emissiveTexture.content);
networkMaterial->emissiveTextureName = material.emissiveTexture.name;
auto emissiveMap = model::TextureMapPointer(new model::TextureMap());
emissiveMap->setTextureSource(networkMaterial->emissiveTexture->_textureSource);
auto emissiveMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.emissiveTexture, EMISSIVE_TEXTURE,
networkMaterial->emissiveTexture, networkMaterial->emissiveTextureName);
material._material->setTextureMap(model::MaterialKey::EMISSIVE_MAP, emissiveMap);
}
if (!material.lightmapTexture.filename.isEmpty()) {
networkMaterial->lightmapTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.lightmapTexture.filename)), LIGHTMAP_TEXTURE, material.lightmapTexture.content);
networkMaterial->lightmapTextureName = material.lightmapTexture.name;
auto lightmapMap = model::TextureMapPointer(new model::TextureMap());
lightmapMap->setTextureSource(networkMaterial->lightmapTexture->_textureSource);
auto lightmapMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.lightmapTexture, LIGHTMAP_TEXTURE,
networkMaterial->lightmapTexture, networkMaterial->lightmapTextureName);
lightmapMap->setTextureTransform(material.lightmapTexture.transform);
lightmapMap->setLightmapOffsetScale(material.lightmapParams.x, material.lightmapParams.y);
material._material->setTextureMap(model::MaterialKey::LIGHTMAP_MAP, lightmapMap);
}

View file

@ -47,22 +47,53 @@ AssetClient::AssetClient() {
}
void AssetClient::init() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "init", Qt::BlockingQueuedConnection);
}
Q_ASSERT(QThread::currentThread() == thread());
// Setup disk cache if not already
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
auto& networkAccessManager = NetworkAccessManager::getInstance();
if (!networkAccessManager.cache()) {
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
cachePath = !cachePath.isEmpty() ? cachePath : "interfaceCache";
QNetworkDiskCache* cache = new QNetworkDiskCache();
cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE);
cache->setCacheDirectory(cachePath);
networkAccessManager.setCache(cache);
qCDebug(asset_client) << "AssetClient disk cache setup at" << cachePath
<< "(size:" << MAXIMUM_CACHE_SIZE / BYTES_PER_GIGABYTES << "GB)";
qDebug() << "ResourceManager disk cache setup at" << cachePath
<< "(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
public:
AssetClient();
Q_INVOKABLE void init();
Q_INVOKABLE AssetRequest* createRequest(const QString& hash, const QString& extension);
Q_INVOKABLE AssetUpload* createUpload(const QString& filename);
Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data, const QString& extension);
public slots:
void init();
void cacheInfoRequest(QObject* reciever, QString slot);
void clearCache();
private slots:
void handleAssetGetInfoReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleAssetGetReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);

View file

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

View file

@ -98,6 +98,8 @@ void DomainHandler::softReset() {
}
void DomainHandler::hardReset() {
emit resetting();
softReset();
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
void completedSocketDiscovery();
void resetting();
void connectedToDomain(const QString& hostname);
void disconnectedFromDomain();

View file

@ -19,7 +19,7 @@
class FileResourceRequest : public ResourceRequest {
Q_OBJECT
public:
FileResourceRequest(QObject* parent, const QUrl& url) : ResourceRequest(parent, url) { }
FileResourceRequest(const QUrl& url) : ResourceRequest(url) { }
protected:
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() {
QNetworkRequest networkRequest(_url);
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::downloadProgress, this, &HTTPResourceRequest::onDownloadProgress);
connect(&_sendTimer, &QTimer::timeout, this, &HTTPResourceRequest::onTimeout);
static const int TIMEOUT_MS = 10000;
_sendTimer.setSingleShot(true);
_sendTimer.start(TIMEOUT_MS);
setupTimer();
}
void HTTPResourceRequest::onRequestFinished() {
Q_ASSERT(_state == InProgress);
Q_ASSERT(_reply);
_sendTimer.stop();
cleanupTimer();
switch(_reply->error()) {
case QNetworkReply::NoError:
@ -80,7 +96,7 @@ void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
Q_ASSERT(_state == InProgress);
// We've received data, so reset the timer
_sendTimer.start();
_sendTimer->start();
emit progress(bytesReceived, bytesTotal);
}
@ -91,6 +107,8 @@ void HTTPResourceRequest::onTimeout() {
_reply->abort();
_reply->deleteLater();
_reply = nullptr;
cleanupTimer();
_result = Timeout;
_state = Finished;

View file

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

View file

@ -11,12 +11,20 @@
#include "ResourceManager.h"
#include "AssetResourceRequest.h"
#include "FileResourceRequest.h"
#include "HTTPResourceRequest.h"
#include <QNetworkDiskCache>
#include <QStandardPaths>
#include <QThread>
#include <SharedUtil.h>
#include "AssetResourceRequest.h"
#include "FileResourceRequest.h"
#include "HTTPResourceRequest.h"
#include "NetworkAccessManager.h"
QThread ResourceManager::_thread;
ResourceManager::PrefixMap ResourceManager::_prefixMap;
QMutex ResourceManager::_prefixMapLock;
@ -67,18 +75,41 @@ QUrl ResourceManager::normalizeURL(const QUrl& originalUrl) {
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) {
auto normalizedURL = normalizeURL(url);
auto scheme = normalizedURL.scheme();
ResourceRequest* request = nullptr;
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) {
return new HTTPResourceRequest(parent, normalizedURL);
request = new HTTPResourceRequest(normalizedURL);
} 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();
return nullptr;
request->moveToThread(&_thread);
return request;
}

View file

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

View file

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

View file

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

View file

@ -58,7 +58,7 @@ BasePacket::BasePacket(qint64 size) {
Q_ASSERT(size >= 0 || size < maxPayload);
_packetSize = size;
_packet.reset(new char[_packetSize]);
_packet.reset(new char[_packetSize]());
_payloadCapacity = _packetSize;
_payloadSize = 0;
_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)
// Lasily create send queue
_sendQueue = SendQueue::create(_parentSocket, _destination, _inactiveSendQueueSequenceNumber);
_sendQueue = SendQueue::create(_parentSocket, _destination);
_lastReceivedACK = _sendQueue->getCurrentSequenceNumber();
#ifdef UDT_CONNECTION_DEBUG
qCDebug(networking) << "Created SendQueue for connection to" << _destination;
@ -109,10 +110,6 @@ SendQueue& Connection::getSendQueue() {
}
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
stopSendQueue();
@ -728,15 +725,28 @@ void Connection::processNAK(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
// 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();
_initialReceiveSequenceNumber = initialSequenceNumber;
_lastReceivedSequenceNumber = initialSequenceNumber - 1;
_lastSentACK = initialSequenceNumber - 1;
}
// 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);
// 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) {
// if we've decided to clean up the send queue then this handshake ACK should be ignored, it's useless
if (_sendQueue) {
SequenceNumber initialSequenceNumber;
controlPacket->readPrimitive(&initialSequenceNumber);
// 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
_hasReceivedHandshakeACK = true;

View file

@ -130,7 +130,9 @@ private:
bool _isReceivingData { false }; // flag used for expiry of receipt portion 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
SequenceNumber _lastReceivedSequenceNumber; // The largest sequence number received from the peer
SequenceNumber _lastReceivedACK; // The last ACK received
@ -140,8 +142,6 @@ private:
SequenceNumber _lastSentACK; // The last sent ACK
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 _lightACKsDuringSYN { 1 }; // The number of lite ACKs sent during SYN interval

View file

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

View file

@ -12,6 +12,7 @@
#include "SendQueue.h"
#include <algorithm>
#include <random>
#include <thread>
#include <QtCore/QCoreApplication>
@ -53,10 +54,10 @@ private:
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*");
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
QThread* thread = new QThread;
@ -75,12 +76,23 @@ std::unique_ptr<SendQueue> SendQueue::create(Socket* socket, HifiSockAddr destin
return queue;
}
SendQueue::SendQueue(Socket* socket, HifiSockAddr dest, SequenceNumber currentSequenceNumber) :
SendQueue::SendQueue(Socket* socket, HifiSockAddr dest) :
_socket(socket),
_destination(dest),
_currentSequenceNumber(currentSequenceNumber)
_destination(dest)
{
// 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) {
@ -190,7 +202,11 @@ void SendQueue::sendHandshake() {
std::unique_lock<std::mutex> handshakeLock { _handshakeMutex };
if (!_hasReceivedHandshakeACK) {
// 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);
// we wait for the ACK or the re-send interval to expire
@ -199,14 +215,16 @@ void SendQueue::sendHandshake() {
}
}
void SendQueue::handshakeACK() {
{
std::lock_guard<std::mutex> locker { _handshakeMutex };
_hasReceivedHandshakeACK = true;
void SendQueue::handshakeACK(SequenceNumber initialSequenceNumber) {
if (initialSequenceNumber == _initialSequenceNumber) {
{
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() {

View file

@ -50,8 +50,7 @@ public:
Stopped
};
static std::unique_ptr<SendQueue> create(Socket* socket, HifiSockAddr destination,
SequenceNumber currentSequenceNumber = SequenceNumber());
static std::unique_ptr<SendQueue> create(Socket* socket, HifiSockAddr destination);
void queuePacket(std::unique_ptr<Packet> packet);
void queuePacketList(std::unique_ptr<PacketList> packetList);
@ -72,7 +71,7 @@ public slots:
void ack(SequenceNumber ack);
void nak(SequenceNumber start, SequenceNumber end);
void overrideNAKListFromPacket(ControlPacket& packet);
void handshakeACK();
void handshakeACK(SequenceNumber initialSequenceNumber);
signals:
void packetSent(int dataSize, int payloadSize);
@ -84,7 +83,7 @@ private slots:
void run();
private:
SendQueue(Socket* socket, HifiSockAddr dest, SequenceNumber currentSequenceNumber);
SendQueue(Socket* socket, HifiSockAddr dest);
SendQueue(SendQueue& other) = delete;
SendQueue(SendQueue&& other) = delete;
@ -106,6 +105,8 @@ private:
Socket* _socket { nullptr }; // Socket to send packet on
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

View file

@ -19,13 +19,20 @@ vec3 fresnelSchlick(vec3 fresnelColor, vec3 lightDir, vec3 halfDir) {
}
float specularDistribution(float roughness, vec3 normal, vec3 halfDir) {
float gloss = (1.0 - roughness) * 128.0;
gloss *= gloss;
float ndoth = clamp(dot(halfDir, normal), 0.0, 1.0);
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);
power *= (gloss * 0.125 + 0.25);
return power;
}
*/
// 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) {
// Diffuse Lighting