mirror of
https://github.com/overte-org/overte.git
synced 2025-04-12 18:42:12 +02:00
Imroving the roughness/specular highlight nd fixing a bug with the CUbemap texture
This commit is contained in:
commit
16ac69315f
30 changed files with 457 additions and 245 deletions
|
@ -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>();
|
||||
|
|
115
examples/away.js
115
examples/away.js
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -98,6 +98,8 @@ void DomainHandler::softReset() {
|
|||
}
|
||||
|
||||
void DomainHandler::hardReset() {
|
||||
emit resetting();
|
||||
|
||||
softReset();
|
||||
|
||||
qCDebug(networking) << "Hard reset in NodeList DomainHandler.";
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue