mirror of
https://github.com/overte-org/overte.git
synced 2025-04-11 04:12:09 +02:00
fixing merge conflicts
This commit is contained in:
commit
038ffe920a
131 changed files with 3578 additions and 1409 deletions
8
cmake/externals/nvtt/CMakeLists.txt
vendored
8
cmake/externals/nvtt/CMakeLists.txt
vendored
|
@ -8,8 +8,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://s3.amazonaws.com/hifi-public/dependencies/nvtt-win-2.1.0.zip
|
||||
URL_MD5 3ea6eeadbcc69071acf9c49ba565760e
|
||||
URL http://s3.amazonaws.com/hifi-public/dependencies/nvtt-win-2.1.0.hifi.zip
|
||||
URL_MD5 10da01cf601f88f6dc12a6bc13c89136
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
@ -29,8 +29,8 @@ else ()
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/nvidia-texture-tools-2.1.0.zip
|
||||
URL_MD5 81b8fa6a9ee3f986088eb6e2215d6a57
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/nvidia-texture-tools-2.1.0.hifi.zip
|
||||
URL_MD5 5794b950f8b265a9a41b2839b3bf7ebb
|
||||
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DNVTT_SHARED=1 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
|
|
|
@ -35,35 +35,35 @@
|
|||
{ "from": "Vive.RightApplicationMenu", "to": "Standard.RightSecondaryThumb" },
|
||||
|
||||
{ "from": "Vive.LeftHand", "to": "Standard.LeftHand", "when": [ "Application.InHMD" ] },
|
||||
{ "from": "Vive.RightHand", "to": "Standard.RightHand", "when": [ "Application.InHMD" ] },
|
||||
{ "from": "Vive.RightHand", "to": "Standard.RightHand", "when": [ "Application.InHMD" ] },
|
||||
|
||||
{
|
||||
"from": "Vive.LeftFoot", "to" : "Standard.LeftFoot",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}],
|
||||
"when": [ "Application.InHMD"]
|
||||
"when": [ "Application.InHMD" ]
|
||||
},
|
||||
|
||||
{
|
||||
"from": "Vive.RightFoot", "to" : "Standard.RightFoot",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}],
|
||||
"when": [ "Application.InHMD"]
|
||||
"when": [ "Application.InHMD" ]
|
||||
},
|
||||
|
||||
{
|
||||
"from": "Vive.Hips", "to" : "Standard.Hips",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}],
|
||||
"when": [ "Application.InHMD"]
|
||||
"when": [ "Application.InHMD" ]
|
||||
},
|
||||
|
||||
{
|
||||
"from": "Vive.Spine2", "to" : "Standard.Spine2",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}],
|
||||
"when": [ "Application.InHMD"]
|
||||
"when": [ "Application.InHMD" ]
|
||||
},
|
||||
|
||||
{ "from": "Vive.Head", "to" : "Standard.Head", "when" : [ "Application.InHMD"] },
|
||||
{ "from": "Vive.Head", "to" : "Standard.Head", "when": [ "Application.InHMD" ] },
|
||||
|
||||
{ "from": "Vive.RightArm", "to" : "Standard.RightArm", "when" : [ "Application.InHMD"] },
|
||||
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm", "when" : [ "Application.InHMD"] }
|
||||
{ "from": "Vive.RightArm", "to" : "Standard.RightArm", "when": [ "Application.InHMD" ] },
|
||||
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm", "when": [ "Application.InHMD" ] }
|
||||
]
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -170,20 +170,24 @@ Item {
|
|||
objectName: "loader"
|
||||
asynchronous: false
|
||||
|
||||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
// Hook up callback for clara.io download from the marketplace.
|
||||
Connections {
|
||||
id: eventBridgeConnection
|
||||
target: null
|
||||
onWebEventReceived: {
|
||||
if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(message.slice(18));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
if (loader.item.hasOwnProperty("eventBridge")) {
|
||||
loader.item.eventBridge = eventBridge;
|
||||
|
||||
// Hook up callback for clara.io download from the marketplace.
|
||||
eventBridge.webEventReceived.connect(function (event) {
|
||||
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
|
||||
}
|
||||
});
|
||||
eventBridgeConnection.target = eventBridge
|
||||
}
|
||||
if (loader.item.hasOwnProperty("sendToScript")) {
|
||||
loader.item.sendToScript.connect(tabletRoot.sendToScript);
|
||||
|
|
|
@ -90,16 +90,21 @@ Windows.ScrollingWindow {
|
|||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
|
||||
// Hook up callback for clara.io download from the marketplace.
|
||||
Connections {
|
||||
id: eventBridgeConnection
|
||||
target: null
|
||||
onWebEventReceived: {
|
||||
if (message.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(message.slice(18));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
if (loader.item.hasOwnProperty("eventBridge")) {
|
||||
loader.item.eventBridge = eventBridge;
|
||||
|
||||
// Hook up callback for clara.io download from the marketplace.
|
||||
eventBridge.webEventReceived.connect(function (event) {
|
||||
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
|
||||
}
|
||||
});
|
||||
eventBridgeConnection.target = eventBridge
|
||||
}
|
||||
if (loader.item.hasOwnProperty("sendToScript")) {
|
||||
loader.item.sendToScript.connect(tabletRoot.sendToScript);
|
||||
|
|
|
@ -136,7 +136,7 @@ Item {
|
|||
for (var i = 0; i < sections.length; i++) {
|
||||
totalHeight += sections[i].height + sections[i].getPreferencesHeight();
|
||||
}
|
||||
var bottomPadding = 100;
|
||||
var bottomPadding = 170;
|
||||
return (totalHeight + bottomPadding);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
#include "Application.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <gl/Config.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/component_wise.hpp>
|
||||
|
@ -145,6 +148,7 @@
|
|||
#include "InterfaceLogging.h"
|
||||
#include "LODManager.h"
|
||||
#include "ModelPackager.h"
|
||||
#include "networking/CloseEventSender.h"
|
||||
#include "networking/HFWebEngineProfile.h"
|
||||
#include "networking/HFTabletWebEngineProfile.h"
|
||||
#include "networking/FileTypeProfile.h"
|
||||
|
@ -536,6 +540,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::set<AvatarBookmarks>();
|
||||
DependencyManager::set<LocationBookmarks>();
|
||||
DependencyManager::set<Snapshot>();
|
||||
DependencyManager::set<CloseEventSender>();
|
||||
|
||||
return previousSessionCrashed;
|
||||
}
|
||||
|
@ -1461,6 +1466,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
updateSystemTabletMode();
|
||||
|
||||
connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged);
|
||||
|
||||
qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
|
||||
}
|
||||
|
||||
void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
|
||||
|
@ -1570,6 +1577,14 @@ void Application::aboutToQuit() {
|
|||
|
||||
getActiveDisplayPlugin()->deactivate();
|
||||
|
||||
// use the CloseEventSender via a QThread to send an event that says the user asked for the app to close
|
||||
auto closeEventSender = DependencyManager::get<CloseEventSender>();
|
||||
QThread* closureEventThread = new QThread(this);
|
||||
closeEventSender->moveToThread(closureEventThread);
|
||||
// sendQuitEventAsync will bail immediately if the UserActivityLogger is not enabled
|
||||
connect(closureEventThread, &QThread::started, closeEventSender.data(), &CloseEventSender::sendQuitEventAsync);
|
||||
closureEventThread->start();
|
||||
|
||||
// Hide Running Scripts dialog so that it gets destroyed in an orderly manner; prevents warnings at shutdown.
|
||||
DependencyManager::get<OffscreenUi>()->hide("RunningScripts");
|
||||
|
||||
|
@ -1683,6 +1698,10 @@ Application::~Application() {
|
|||
|
||||
_physicsEngine->setCharacterController(nullptr);
|
||||
|
||||
// the _shapeManager should have zero references
|
||||
_shapeManager.collectGarbage();
|
||||
assert(_shapeManager.getNumShapes() == 0);
|
||||
|
||||
// shutdown render engine
|
||||
_main3DScene = nullptr;
|
||||
_renderEngine = nullptr;
|
||||
|
@ -1734,6 +1753,15 @@ Application::~Application() {
|
|||
|
||||
_window->deleteLater();
|
||||
|
||||
// make sure that the quit event has finished sending before we take the application down
|
||||
auto closeEventSender = DependencyManager::get<CloseEventSender>();
|
||||
while (!closeEventSender->hasFinishedQuitEvent() && !closeEventSender->hasTimedOutQuitEvent()) {
|
||||
// sleep a little so we're not spinning at 100%
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
// quit the thread used by the closure event sender
|
||||
closeEventSender->thread()->quit();
|
||||
|
||||
// Can't log to file passed this point, FileLogger about to be deleted
|
||||
qInstallMessageHandler(LogHandler::verboseMessageHandler);
|
||||
}
|
||||
|
@ -2185,6 +2213,9 @@ void Application::paintGL() {
|
|||
});
|
||||
renderArgs._context->setStereoProjections(eyeProjections);
|
||||
renderArgs._context->setStereoViews(eyeOffsets);
|
||||
|
||||
// Configure the type of display / stereo
|
||||
renderArgs._displayMode = (isHMDMode() ? RenderArgs::STEREO_HMD : RenderArgs::STEREO_MONITOR);
|
||||
}
|
||||
renderArgs._blitFramebuffer = finalFramebuffer;
|
||||
displaySide(&renderArgs, _myCamera);
|
||||
|
@ -2384,15 +2415,16 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
|
||||
// Check HMD use (may be technically available without being in use)
|
||||
bool hasHMD = PluginUtils::isHMDAvailable();
|
||||
bool isUsingHMD = hasHMD && hasHandControllers && _displayPlugin->isHmd();
|
||||
bool isUsingHMD = _displayPlugin->isHmd();
|
||||
bool isUsingHMDAndHandControllers = hasHMD && hasHandControllers && isUsingHMD;
|
||||
|
||||
Setting::Handle<bool> tutorialComplete{ "tutorialComplete", false };
|
||||
Setting::Handle<bool> firstRun{ Settings::firstRun, true };
|
||||
|
||||
bool isTutorialComplete = tutorialComplete.get();
|
||||
bool shouldGoToTutorial = isUsingHMD && hasTutorialContent && !isTutorialComplete;
|
||||
bool shouldGoToTutorial = isUsingHMDAndHandControllers && hasTutorialContent && !isTutorialComplete;
|
||||
|
||||
qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMD;
|
||||
qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMDAndHandControllers;
|
||||
qCDebug(interfaceapp) << "Tutorial version:" << contentVersion << ", sufficient:" << hasTutorialContent <<
|
||||
", complete:" << isTutorialComplete << ", should go:" << shouldGoToTutorial;
|
||||
|
||||
|
@ -2406,10 +2438,18 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
|
||||
const QString TUTORIAL_PATH = "/tutorial_begin";
|
||||
|
||||
static const QString SENT_TO_TUTORIAL = "tutorial";
|
||||
static const QString SENT_TO_PREVIOUS_LOCATION = "previous_location";
|
||||
static const QString SENT_TO_ENTRY = "entry";
|
||||
static const QString SENT_TO_SANDBOX = "sandbox";
|
||||
|
||||
QString sentTo;
|
||||
|
||||
if (shouldGoToTutorial) {
|
||||
if (sandboxIsRunning) {
|
||||
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
|
||||
DependencyManager::get<AddressManager>()->goToLocalSandbox(TUTORIAL_PATH);
|
||||
sentTo = SENT_TO_TUTORIAL;
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
|
||||
if (firstRun.get()) {
|
||||
|
@ -2417,8 +2457,10 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
}
|
||||
if (addressLookupString.isEmpty()) {
|
||||
DependencyManager::get<AddressManager>()->goToEntry();
|
||||
sentTo = SENT_TO_ENTRY;
|
||||
} else {
|
||||
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
|
||||
sentTo = SENT_TO_PREVIOUS_LOCATION;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -2431,23 +2473,40 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
|
||||
// If this is a first run we short-circuit the address passed in
|
||||
if (isFirstRun) {
|
||||
if (isUsingHMD) {
|
||||
if (isUsingHMDAndHandControllers) {
|
||||
if (sandboxIsRunning) {
|
||||
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
|
||||
DependencyManager::get<AddressManager>()->goToLocalSandbox();
|
||||
sentTo = SENT_TO_SANDBOX;
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
|
||||
DependencyManager::get<AddressManager>()->goToEntry();
|
||||
sentTo = SENT_TO_ENTRY;
|
||||
}
|
||||
} else {
|
||||
DependencyManager::get<AddressManager>()->goToEntry();
|
||||
sentTo = SENT_TO_ENTRY;
|
||||
}
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);
|
||||
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
|
||||
sentTo = SENT_TO_PREVIOUS_LOCATION;
|
||||
}
|
||||
}
|
||||
|
||||
UserActivityLogger::getInstance().logAction("startup_sent_to", {
|
||||
{ "sent_to", sentTo },
|
||||
{ "sandbox_is_running", sandboxIsRunning },
|
||||
{ "has_hmd", hasHMD },
|
||||
{ "has_hand_controllers", hasHandControllers },
|
||||
{ "is_using_hmd", isUsingHMD },
|
||||
{ "is_using_hmd_and_hand_controllers", isUsingHMDAndHandControllers },
|
||||
{ "content_version", contentVersion },
|
||||
{ "is_tutorial_complete", isTutorialComplete },
|
||||
{ "has_tutorial_content", hasTutorialContent },
|
||||
{ "should_go_to_tutorial", shouldGoToTutorial }
|
||||
});
|
||||
|
||||
_connectionMonitor.init();
|
||||
|
||||
// After all of the constructor is completed, then set firstRun to false.
|
||||
|
@ -2776,6 +2835,17 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
if (isShifted && isMeta && !isOption) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings);
|
||||
} else if (!isOption && !isShifted && isMeta) {
|
||||
AudioInjectorOptions options;
|
||||
options.localOnly = true;
|
||||
options.stereo = true;
|
||||
|
||||
if (_snapshotSoundInjector) {
|
||||
_snapshotSoundInjector->setOptions(options);
|
||||
_snapshotSoundInjector->restart();
|
||||
} else {
|
||||
QByteArray samples = _snapshotSound->getByteArray();
|
||||
_snapshotSoundInjector = AudioInjector::playSound(samples, options);
|
||||
}
|
||||
takeSnapshot(true);
|
||||
}
|
||||
break;
|
||||
|
@ -4490,12 +4560,13 @@ void Application::update(float deltaTime) {
|
|||
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
PerformanceTimer perfTimer("handleOutgoingChanges");
|
||||
const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates();
|
||||
_entitySimulation->handleDeactivatedMotionStates(deactivations);
|
||||
|
||||
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates();
|
||||
_entitySimulation->handleChangedMotionStates(outgoingChanges);
|
||||
avatarManager->handleChangedMotionStates(outgoingChanges);
|
||||
|
||||
const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates();
|
||||
_entitySimulation->handleDeactivatedMotionStates(deactivations);
|
||||
});
|
||||
|
||||
if (!_aboutToQuit) {
|
||||
|
@ -6307,21 +6378,6 @@ void Application::loadAddAvatarBookmarkDialog() const {
|
|||
}
|
||||
|
||||
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
|
||||
|
||||
//keep sound thread out of event loop scope
|
||||
|
||||
AudioInjectorOptions options;
|
||||
options.localOnly = true;
|
||||
options.stereo = true;
|
||||
|
||||
if (_snapshotSoundInjector) {
|
||||
_snapshotSoundInjector->setOptions(options);
|
||||
_snapshotSoundInjector->restart();
|
||||
} else {
|
||||
QByteArray samples = _snapshotSound->getByteArray();
|
||||
_snapshotSoundInjector = AudioInjector::playSound(samples, options);
|
||||
}
|
||||
|
||||
postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
|
||||
// Get a screenshot and save it
|
||||
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio));
|
||||
|
|
|
@ -414,6 +414,12 @@ Menu::Menu() {
|
|||
#endif
|
||||
|
||||
|
||||
{
|
||||
auto action = addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderClearKtxCache);
|
||||
connect(action, &QAction::triggered, []{
|
||||
Setting::Handle<int>(KTXCache::SETTING_VERSION_NAME, KTXCache::INVALID_VERSION).set(KTXCache::INVALID_VERSION);
|
||||
});
|
||||
}
|
||||
|
||||
// Developer > Render > LOD Tools
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, 0,
|
||||
|
|
|
@ -145,6 +145,7 @@ namespace MenuOption {
|
|||
const QString Quit = "Quit";
|
||||
const QString ReloadAllScripts = "Reload All Scripts";
|
||||
const QString ReloadContent = "Reload Content (Clears all caches)";
|
||||
const QString RenderClearKtxCache = "Clear KTX Cache (requires restart)";
|
||||
const QString RenderMaxTextureMemory = "Maximum Texture Memory";
|
||||
const QString RenderMaxTextureAutomatic = "Automatic Texture Memory";
|
||||
const QString RenderMaxTexture4MB = "4 MB";
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <SandboxUtils.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
|
||||
#include "AddressManager.h"
|
||||
#include "Application.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
@ -191,7 +190,7 @@ int main(int argc, const char* argv[]) {
|
|||
|
||||
int exitCode;
|
||||
{
|
||||
RunningMarker runningMarker(nullptr, RUNNING_MARKER_FILENAME);
|
||||
RunningMarker runningMarker(RUNNING_MARKER_FILENAME);
|
||||
bool runningMarkerExisted = runningMarker.fileExists();
|
||||
runningMarker.writeRunningMarkerFile();
|
||||
|
||||
|
@ -200,14 +199,11 @@ int main(int argc, const char* argv[]) {
|
|||
bool serverContentPathOptionIsSet = parser.isSet(serverContentPathOption);
|
||||
QString serverContentPath = serverContentPathOptionIsSet ? parser.value(serverContentPathOption) : QString();
|
||||
if (runServer) {
|
||||
SandboxUtils::runLocalSandbox(serverContentPath, true, RUNNING_MARKER_FILENAME, noUpdater);
|
||||
SandboxUtils::runLocalSandbox(serverContentPath, true, noUpdater);
|
||||
}
|
||||
|
||||
Application app(argc, const_cast<char**>(argv), startupTime, runningMarkerExisted);
|
||||
|
||||
// Now that the main event loop is setup, launch running marker thread
|
||||
runningMarker.startRunningMarker();
|
||||
|
||||
// If we failed the OpenGLVersion check, log it.
|
||||
if (override) {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
|
90
interface/src/networking/CloseEventSender.cpp
Normal file
90
interface/src/networking/CloseEventSender.cpp
Normal file
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// CloseEventSender.cpp
|
||||
// interface/src/networking
|
||||
//
|
||||
// Created by Stephen Birarda on 5/31/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QEventLoop>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <NetworkLogging.h>
|
||||
#include <UserActivityLogger.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "CloseEventSender.h"
|
||||
|
||||
QNetworkRequest createNetworkRequest() {
|
||||
|
||||
QNetworkRequest request;
|
||||
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
requestURL.setPath(USER_ACTIVITY_URL);
|
||||
|
||||
request.setUrl(requestURL);
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
||||
if (accountManager->hasValidAccessToken()) {
|
||||
request.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER,
|
||||
accountManager->getAccountInfo().getAccessToken().authorizationHeaderValue());
|
||||
}
|
||||
|
||||
request.setRawHeader(METAVERSE_SESSION_ID_HEADER,
|
||||
uuidStringWithoutCurlyBraces(accountManager->getSessionID()).toLocal8Bit());
|
||||
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
request.setPriority(QNetworkRequest::HighPriority);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
QByteArray postDataForAction(QString action) {
|
||||
return QString("{\"action_name\": \"" + action + "\"}").toUtf8();
|
||||
}
|
||||
|
||||
QNetworkReply* replyForAction(QString action) {
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
return networkAccessManager.post(createNetworkRequest(), postDataForAction(action));
|
||||
}
|
||||
|
||||
void CloseEventSender::sendQuitEventAsync() {
|
||||
if (UserActivityLogger::getInstance().isEnabled()) {
|
||||
QNetworkReply* reply = replyForAction("quit");
|
||||
connect(reply, &QNetworkReply::finished, this, &CloseEventSender::handleQuitEventFinished);
|
||||
_quitEventStartTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
} else {
|
||||
_hasFinishedQuitEvent = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CloseEventSender::handleQuitEventFinished() {
|
||||
_hasFinishedQuitEvent = true;
|
||||
|
||||
auto reply = qobject_cast<QNetworkReply*>(sender());
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
qCDebug(networking) << "Quit event sent successfully";
|
||||
} else {
|
||||
qCDebug(networking) << "Failed to send quit event -" << reply->errorString();
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
bool CloseEventSender::hasTimedOutQuitEvent() {
|
||||
const int CLOSURE_EVENT_TIMEOUT_MS = 5000;
|
||||
return _quitEventStartTimestamp != 0
|
||||
&& QDateTime::currentMSecsSinceEpoch() - _quitEventStartTimestamp > CLOSURE_EVENT_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
|
41
interface/src/networking/CloseEventSender.h
Normal file
41
interface/src/networking/CloseEventSender.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// CloseEventSender.h
|
||||
// interface/src/networking
|
||||
//
|
||||
// Created by Stephen Birarda on 5/31/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_CloseEventSender_h
|
||||
#define hifi_CloseEventSender_h
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
class CloseEventSender : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
bool hasTimedOutQuitEvent();
|
||||
bool hasFinishedQuitEvent() { return _hasFinishedQuitEvent; }
|
||||
|
||||
public slots:
|
||||
void sendQuitEventAsync();
|
||||
|
||||
private slots:
|
||||
void handleQuitEventFinished();
|
||||
|
||||
private:
|
||||
std::atomic<bool> _hasFinishedQuitEvent { false };
|
||||
std::atomic<int64_t> _quitEventStartTimestamp;
|
||||
};
|
||||
|
||||
#endif // hifi_CloseEventSender_h
|
|
@ -1135,9 +1135,9 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
|
|||
}
|
||||
|
||||
glm::vec3 headUp = headQuat * Vectors::UNIT_Y;
|
||||
glm::vec3 z, y, x;
|
||||
generateBasisVectors(lookAtVector, headUp, z, y, x);
|
||||
glm::mat3 m(glm::cross(y, z), y, z);
|
||||
glm::vec3 z, y, zCrossY;
|
||||
generateBasisVectors(lookAtVector, headUp, z, y, zCrossY);
|
||||
glm::mat3 m(-zCrossY, y, z);
|
||||
glm::quat desiredQuat = glm::normalize(glm::quat_cast(m));
|
||||
|
||||
glm::quat deltaQuat = desiredQuat * glm::inverse(headQuat);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <QStandardPaths>
|
||||
#include <PathUtils.h>
|
||||
#include <QUrl>
|
||||
#include <Gzip.h>
|
||||
|
||||
#include <BuildInfo.h>
|
||||
#include <GLMHelpers.h>
|
||||
|
@ -27,7 +28,7 @@
|
|||
|
||||
QString SAVE_DIRECTORY = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/" + BuildInfo::MODIFIED_ORGANIZATION + "/" + BuildInfo::INTERFACE_NAME + "/hifi-input-recordings/";
|
||||
QString FILE_PREFIX_NAME = "input-recording-";
|
||||
QString COMPRESS_EXTENSION = "json.gz";
|
||||
QString COMPRESS_EXTENSION = ".json.gz";
|
||||
namespace controller {
|
||||
|
||||
QJsonObject poseToJsonObject(const Pose pose) {
|
||||
|
@ -93,23 +94,26 @@ namespace controller {
|
|||
}
|
||||
|
||||
|
||||
void exportToFile(const QJsonObject& object) {
|
||||
void exportToFile(const QJsonObject& object, const QString& fileName) {
|
||||
if (!QDir(SAVE_DIRECTORY).exists()) {
|
||||
QDir().mkdir(SAVE_DIRECTORY);
|
||||
}
|
||||
|
||||
QString timeStamp = QDateTime::currentDateTime().toString(Qt::ISODate);
|
||||
timeStamp.replace(":", "-");
|
||||
QString fileName = SAVE_DIRECTORY + FILE_PREFIX_NAME + timeStamp + COMPRESS_EXTENSION;
|
||||
qDebug() << fileName;
|
||||
|
||||
QFile saveFile (fileName);
|
||||
if (!saveFile.open(QIODevice::WriteOnly)) {
|
||||
qWarning() << "could not open file: " << fileName;
|
||||
return;
|
||||
}
|
||||
QJsonDocument saveData(object);
|
||||
QByteArray compressedData = qCompress(saveData.toJson(QJsonDocument::Compact));
|
||||
saveFile.write(compressedData);
|
||||
QByteArray jsonData = saveData.toJson(QJsonDocument::Indented);
|
||||
QByteArray jsonDataForFile;
|
||||
|
||||
if (!gzip(jsonData, jsonDataForFile, -1)) {
|
||||
qCritical("unable to gzip while saving to json.");
|
||||
return;
|
||||
}
|
||||
|
||||
saveFile.write(jsonDataForFile);
|
||||
saveFile.close();
|
||||
}
|
||||
|
||||
|
@ -121,8 +125,16 @@ namespace controller {
|
|||
status = false;
|
||||
return object;
|
||||
}
|
||||
QByteArray compressedData = qUncompress(openFile.readAll());
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(compressedData);
|
||||
QByteArray compressedData = openFile.readAll();
|
||||
QByteArray jsonData;
|
||||
|
||||
if (!gunzip(compressedData, jsonData)) {
|
||||
qCritical() << "json file not in gzip format: " << file;
|
||||
status = false;
|
||||
return object;
|
||||
}
|
||||
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
|
||||
object = jsonDoc.object();
|
||||
status = true;
|
||||
openFile.close();
|
||||
|
@ -153,7 +165,7 @@ namespace controller {
|
|||
QJsonObject InputRecorder::recordDataToJson() {
|
||||
QJsonObject data;
|
||||
data["frameCount"] = _framesRecorded;
|
||||
data["version"] = "1.0";
|
||||
data["version"] = "0.0";
|
||||
|
||||
QJsonArray actionArrayList;
|
||||
QJsonArray poseArrayList;
|
||||
|
@ -187,7 +199,10 @@ namespace controller {
|
|||
|
||||
void InputRecorder::saveRecording() {
|
||||
QJsonObject jsonData = recordDataToJson();
|
||||
exportToFile(jsonData);
|
||||
QString timeStamp = QDateTime::currentDateTime().toString(Qt::ISODate);
|
||||
timeStamp.replace(":", "-");
|
||||
QString fileName = SAVE_DIRECTORY + FILE_PREFIX_NAME + timeStamp + COMPRESS_EXTENSION;
|
||||
exportToFile(jsonData, fileName);
|
||||
}
|
||||
|
||||
void InputRecorder::loadRecording(const QString& path) {
|
||||
|
@ -202,10 +217,12 @@ namespace controller {
|
|||
QString filePath = urlPath.toLocalFile();
|
||||
QFileInfo info(filePath);
|
||||
QString extension = info.suffix();
|
||||
|
||||
if (extension != "gz") {
|
||||
qWarning() << "can not load file with exentsion of " << extension;
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
QJsonObject data = openFile(filePath, success);
|
||||
auto keyValue = data.find("version");
|
||||
|
@ -233,34 +250,7 @@ namespace controller {
|
|||
_poseStateList.push_back(_currentFramePoses);
|
||||
_currentFramePoses.clear();
|
||||
}
|
||||
} else if (success) {
|
||||
//convert recording to new reacording standard and rewrite file
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
_framesRecorded = data["frameCount"].toInt();
|
||||
QJsonArray actionArrayList = data["actionList"].toArray();
|
||||
QJsonArray poseArrayList = data["poseList"].toArray();
|
||||
|
||||
for (int actionIndex = 0; actionIndex < actionArrayList.size(); actionIndex++) {
|
||||
QJsonArray actionState = actionArrayList[actionIndex].toArray();
|
||||
for (int index = 0; index < actionState.size(); index++) {
|
||||
QString actionName = userInputMapper->getActionName(Action(index));
|
||||
_currentFrameActions[actionName] = actionState[index].toDouble();
|
||||
}
|
||||
_actionStateList.push_back(_currentFrameActions);
|
||||
_currentFrameActions.clear();
|
||||
}
|
||||
|
||||
for (int poseIndex = 0; poseIndex < poseArrayList.size(); poseIndex++) {
|
||||
QJsonArray poseState = poseArrayList[poseIndex].toArray();
|
||||
for (int index = 0; index < poseState.size(); index++) {
|
||||
QString actionName = userInputMapper->getActionName(Action(index));
|
||||
_currentFramePoses[actionName] = jsonObjectToPose(poseState[index].toObject());
|
||||
}
|
||||
_poseStateList.push_back(_currentFramePoses);
|
||||
_currentFramePoses.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -329,6 +329,16 @@ QString UserInputMapper::getActionName(Action action) const {
|
|||
return QString();
|
||||
}
|
||||
|
||||
QString UserInputMapper::getStandardPoseName(uint16_t pose) {
|
||||
Locker locker(_lock);
|
||||
for (auto posePair : getStandardInputs()) {
|
||||
if (posePair.first.channel == pose && posePair.first.getType() == ChannelType::POSE) {
|
||||
return posePair.second;
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QVector<QString> UserInputMapper::getActionNames() const {
|
||||
Locker locker(_lock);
|
||||
QVector<QString> result;
|
||||
|
|
|
@ -80,6 +80,7 @@ namespace controller {
|
|||
|
||||
QVector<Action> getAllActions() const;
|
||||
QString getActionName(Action action) const;
|
||||
QString getStandardPoseName(uint16_t pose);
|
||||
float getActionState(Action action) const { return _actionStates[toInt(action)]; }
|
||||
Pose getPoseState(Action action) const;
|
||||
int findAction(const QString& actionName) const;
|
||||
|
|
|
@ -36,9 +36,6 @@ void ActionEndpoint::apply(const Pose& value, const Pointer& source) {
|
|||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
QString actionName = userInputMapper->getActionName(Action(_input.getChannel()));
|
||||
inputRecorder->setActionState(actionName, _currentPose);
|
||||
if (inputRecorder->isPlayingback()) {
|
||||
_currentPose = inputRecorder->getPoseState(actionName);
|
||||
}
|
||||
|
||||
if (!_currentPose.isValid()) {
|
||||
return;
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
|
||||
#include "../Endpoint.h"
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "../../InputRecorder.h"
|
||||
#include "../../UserInputMapper.h"
|
||||
|
||||
namespace controller {
|
||||
|
||||
class StandardEndpoint : public VirtualEndpoint {
|
||||
|
@ -40,6 +45,12 @@ public:
|
|||
|
||||
virtual Pose pose() override {
|
||||
_read = true;
|
||||
InputRecorder* inputRecorder = InputRecorder::getInstance();
|
||||
if (inputRecorder->isPlayingback()) {
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
QString actionName = userInputMapper->getStandardPoseName(_input.getChannel());
|
||||
return inputRecorder->getPoseState(actionName);
|
||||
}
|
||||
return VirtualEndpoint::pose();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <DependencyManager.h>
|
||||
#include <PerfStat.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <StencilMaskPass.h>
|
||||
#include <AbstractViewStateInterface.h>
|
||||
#include "EntitiesRendererLogging.h"
|
||||
|
||||
|
@ -292,6 +293,7 @@ void RenderableParticleEffectEntityItem::createPipelines() {
|
|||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
PrepareStencil::testMask(*state);
|
||||
|
||||
auto vertShader = gpu::Shader::createVertex(std::string(untextured_particle_vert));
|
||||
auto fragShader = gpu::Shader::createPixel(std::string(untextured_particle_frag));
|
||||
|
@ -305,6 +307,7 @@ void RenderableParticleEffectEntityItem::createPipelines() {
|
|||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
PrepareStencil::testMask(*state);
|
||||
|
||||
auto vertShader = gpu::Shader::createVertex(std::string(textured_particle_vert));
|
||||
auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag));
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <GeometryCache.h>
|
||||
#include <StencilMaskPass.h>
|
||||
#include <TextureCache.h>
|
||||
#include <PathUtils.h>
|
||||
#include <PerfStat.h>
|
||||
|
@ -69,6 +70,7 @@ void RenderablePolyLineEntityItem::createPipeline() {
|
|||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
PrepareStencil::testMask(*state);
|
||||
state->setBlendFunction(true,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
|
|
|
@ -46,6 +46,9 @@
|
|||
#endif
|
||||
|
||||
#include "model/Geometry.h"
|
||||
|
||||
#include "StencilMaskPass.h"
|
||||
|
||||
#include "EntityTreeRenderer.h"
|
||||
#include "polyvox_vert.h"
|
||||
#include "polyvox_frag.h"
|
||||
|
@ -743,6 +746,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
|
|||
auto state = std::make_shared<gpu::State>();
|
||||
state->setCullMode(gpu::State::CULL_BACK);
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
PrepareStencil::testMaskDrawShape(*state);
|
||||
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
|
||||
|
@ -750,6 +754,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
|
|||
wireframeState->setCullMode(gpu::State::CULL_BACK);
|
||||
wireframeState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
wireframeState->setFillMode(gpu::State::FILL_LINE);
|
||||
PrepareStencil::testMaskDrawShape(*wireframeState);
|
||||
|
||||
_wireframePipeline = gpu::Pipeline::create(program, wireframeState);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <gpu/Batch.h>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <StencilMaskPass.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
|
@ -93,6 +94,7 @@ void RenderableShapeEntityItem::render(RenderArgs* args) {
|
|||
_procedural->_fragmentSource = simple_frag;
|
||||
_procedural->_opaqueState->setCullMode(gpu::State::CULL_NONE);
|
||||
_procedural->_opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
PrepareStencil::testMaskDrawShape(*_procedural->_opaqueState);
|
||||
_procedural->_opaqueState->setBlendFunction(false,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
|
|
|
@ -681,7 +681,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
// and pretend that we own it (we assume we'll recover it soon)
|
||||
|
||||
// However, for now, when the server uses a newer time than what we sent, listen to what we're told.
|
||||
if (overwriteLocalData) weOwnSimulation = false;
|
||||
if (overwriteLocalData) {
|
||||
weOwnSimulation = false;
|
||||
}
|
||||
} else if (_simulationOwner.set(newSimOwner)) {
|
||||
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
|
||||
somethingChanged = true;
|
||||
|
@ -1293,27 +1295,15 @@ void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) c
|
|||
properties._accelerationChanged = true;
|
||||
}
|
||||
|
||||
void EntityItem::pokeSimulationOwnership() {
|
||||
markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_POKE);
|
||||
void EntityItem::flagForOwnershipBid(uint8_t priority) {
|
||||
markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) {
|
||||
// we already own it
|
||||
_simulationOwner.promotePriority(SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
_simulationOwner.promotePriority(priority);
|
||||
} else {
|
||||
// we don't own it yet
|
||||
_simulationOwner.setPendingPriority(SCRIPT_POKE_SIMULATION_PRIORITY, usecTimestampNow());
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItem::grabSimulationOwnership() {
|
||||
markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) {
|
||||
// we already own it
|
||||
_simulationOwner.promotePriority(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||
} else {
|
||||
// we don't own it yet
|
||||
_simulationOwner.setPendingPriority(SCRIPT_GRAB_SIMULATION_PRIORITY, usecTimestampNow());
|
||||
_simulationOwner.setPendingPriority(priority, usecTimestampNow());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -321,6 +321,7 @@ public:
|
|||
void updateSimulationOwner(const SimulationOwner& owner);
|
||||
void clearSimulationOwnership();
|
||||
void setPendingOwnershipPriority(quint8 priority, const quint64& timestamp);
|
||||
uint8_t getPendingOwnershipPriority() const { return _simulationOwner.getPendingPriority(); }
|
||||
void rememberHasSimulationOwnershipBid() const;
|
||||
|
||||
QString getMarketplaceID() const;
|
||||
|
@ -394,8 +395,7 @@ public:
|
|||
|
||||
void getAllTerseUpdateProperties(EntityItemProperties& properties) const;
|
||||
|
||||
void pokeSimulationOwnership();
|
||||
void grabSimulationOwnership();
|
||||
void flagForOwnershipBid(uint8_t priority);
|
||||
void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; }
|
||||
|
||||
QString actionsToDebugString();
|
||||
|
|
|
@ -230,6 +230,8 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
|||
}
|
||||
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
// since we're creating this object we will immediately volunteer to own its simulation
|
||||
entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY);
|
||||
propertiesWithSimID.setLastEdited(entity->getLastEdited());
|
||||
} else {
|
||||
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
||||
|
@ -440,7 +442,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
} else {
|
||||
// we make a bid for simulation ownership
|
||||
properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
entity->pokeSimulationOwnership();
|
||||
entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
entity->rememberHasSimulationOwnershipBid();
|
||||
}
|
||||
}
|
||||
|
@ -1194,7 +1196,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
|||
}
|
||||
action->setIsMine(true);
|
||||
success = entity->addAction(simulation, action);
|
||||
entity->grabSimulationOwnership();
|
||||
entity->flagForOwnershipBid(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||
return false; // Physics will cause a packet to be sent, so don't send from here.
|
||||
});
|
||||
if (success) {
|
||||
|
@ -1210,7 +1212,7 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid&
|
|||
return actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
|
||||
bool success = entity->updateAction(simulation, actionID, arguments);
|
||||
if (success) {
|
||||
entity->grabSimulationOwnership();
|
||||
entity->flagForOwnershipBid(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||
}
|
||||
return success;
|
||||
});
|
||||
|
@ -1224,7 +1226,7 @@ bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid&
|
|||
success = entity->removeAction(simulation, actionID);
|
||||
if (success) {
|
||||
// reduce from grab to poke
|
||||
entity->pokeSimulationOwnership();
|
||||
entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
}
|
||||
return false; // Physics will cause a packet to be sent, so don't send from here.
|
||||
});
|
||||
|
|
|
@ -26,12 +26,10 @@ namespace Simulation {
|
|||
const uint32_t DIRTY_MATERIAL = 0x00400;
|
||||
const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine
|
||||
const uint32_t DIRTY_SIMULATOR_ID = 0x1000; // the simulatorID has changed
|
||||
const uint32_t DIRTY_SIMULATION_OWNERSHIP_FOR_POKE = 0x2000; // bid for simulation ownership at "poke"
|
||||
const uint32_t DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB = 0x4000; // bid for simulation ownership at "grab"
|
||||
const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed
|
||||
|
||||
const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION;
|
||||
const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY;
|
||||
const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = DIRTY_SIMULATION_OWNERSHIP_FOR_POKE | DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB;
|
||||
};
|
||||
|
||||
#endif // hifi_SimulationFlags_h
|
||||
|
|
|
@ -26,9 +26,9 @@ const int SimulationOwner::NUM_BYTES_ENCODED = NUM_BYTES_RFC4122_UUID + 1;
|
|||
SimulationOwner::SimulationOwner() :
|
||||
_id(),
|
||||
_expiry(0),
|
||||
_pendingTimestamp(0),
|
||||
_pendingBidTimestamp(0),
|
||||
_priority(0),
|
||||
_pendingPriority(0),
|
||||
_pendingBidPriority(0),
|
||||
_pendingState(PENDING_STATE_NOTHING)
|
||||
{
|
||||
}
|
||||
|
@ -36,9 +36,9 @@ SimulationOwner::SimulationOwner() :
|
|||
SimulationOwner::SimulationOwner(const QUuid& id, quint8 priority) :
|
||||
_id(id),
|
||||
_expiry(0),
|
||||
_pendingTimestamp(0),
|
||||
_pendingBidTimestamp(0),
|
||||
_priority(priority),
|
||||
_pendingPriority(0)
|
||||
_pendingBidPriority(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -61,9 +61,9 @@ bool SimulationOwner::fromByteArray(const QByteArray& data) {
|
|||
void SimulationOwner::clear() {
|
||||
_id = QUuid();
|
||||
_expiry = 0;
|
||||
_pendingTimestamp = 0;
|
||||
_pendingBidTimestamp = 0;
|
||||
_priority = 0;
|
||||
_pendingPriority = 0;
|
||||
_pendingBidPriority = 0;
|
||||
_pendingState = PENDING_STATE_NOTHING;
|
||||
}
|
||||
|
||||
|
@ -102,9 +102,9 @@ bool SimulationOwner::set(const SimulationOwner& owner) {
|
|||
}
|
||||
|
||||
void SimulationOwner::setPendingPriority(quint8 priority, const quint64& timestamp) {
|
||||
_pendingPriority = priority;
|
||||
_pendingTimestamp = timestamp;
|
||||
_pendingState = (_pendingPriority == 0) ? PENDING_STATE_RELEASE : PENDING_STATE_TAKE;
|
||||
_pendingBidPriority = priority;
|
||||
_pendingBidTimestamp = timestamp;
|
||||
_pendingState = (_pendingBidPriority == 0) ? PENDING_STATE_RELEASE : PENDING_STATE_TAKE;
|
||||
}
|
||||
|
||||
void SimulationOwner::updateExpiry() {
|
||||
|
@ -113,11 +113,11 @@ void SimulationOwner::updateExpiry() {
|
|||
}
|
||||
|
||||
bool SimulationOwner::pendingRelease(const quint64& timestamp) {
|
||||
return _pendingPriority == 0 && _pendingState == PENDING_STATE_RELEASE && _pendingTimestamp >= timestamp;
|
||||
return _pendingBidPriority == 0 && _pendingState == PENDING_STATE_RELEASE && _pendingBidTimestamp >= timestamp;
|
||||
}
|
||||
|
||||
bool SimulationOwner::pendingTake(const quint64& timestamp) {
|
||||
return _pendingPriority > 0 && _pendingState == PENDING_STATE_TAKE && _pendingTimestamp >= timestamp;
|
||||
return _pendingBidPriority > 0 && _pendingState == PENDING_STATE_TAKE && _pendingBidTimestamp >= timestamp;
|
||||
}
|
||||
|
||||
void SimulationOwner::clearCurrentOwner() {
|
||||
|
|
|
@ -66,6 +66,7 @@ public:
|
|||
|
||||
bool hasExpired() const { return usecTimestampNow() > _expiry; }
|
||||
|
||||
uint8_t getPendingPriority() const { return _pendingBidPriority; }
|
||||
bool pendingRelease(const quint64& timestamp); // return true if valid pending RELEASE
|
||||
bool pendingTake(const quint64& timestamp); // return true if valid pending TAKE
|
||||
void clearCurrentOwner();
|
||||
|
@ -84,9 +85,9 @@ public:
|
|||
private:
|
||||
QUuid _id; // owner
|
||||
quint64 _expiry; // time when ownership can transition at equal priority
|
||||
quint64 _pendingTimestamp; // time when pending update was set
|
||||
quint64 _pendingBidTimestamp; // time when pending bid was set
|
||||
quint8 _priority; // priority of current owner
|
||||
quint8 _pendingPriority; // priority of pendingTake
|
||||
quint8 _pendingBidPriority; // priority at which we'd like to own it
|
||||
quint8 _pendingState; // NOTHING, TAKE, or RELEASE
|
||||
};
|
||||
|
||||
|
|
28
libraries/gpu/src/gpu/DrawTransformVertexPosition.slv
Normal file
28
libraries/gpu/src/gpu/DrawTransformVertexPosition.slv
Normal file
|
@ -0,0 +1,28 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// Draw and transform the fed vertex position with the standard MVP stack
|
||||
// Output the clip position
|
||||
//
|
||||
// Created by Sam Gateau on 5/30/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
<@include gpu/Transform.slh@>
|
||||
|
||||
<$declareStandardTransform()$>
|
||||
|
||||
layout(location = 0) in vec4 inPosition;
|
||||
|
||||
out vec3 varWorldPos;
|
||||
|
||||
void main(void) {
|
||||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
|
||||
}
|
|
@ -2,25 +2,18 @@
|
|||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// hit_effect.vert
|
||||
// vertex shader
|
||||
// Draw the fed vertex position, pass straight as clip pos
|
||||
// Output the clip position
|
||||
//
|
||||
// Created by Eric Levin on 7/20/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Created by Sam Gateau on 5/30/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
<@include gpu/Inputs.slh@>
|
||||
|
||||
<@include gpu/Transform.slh@>
|
||||
|
||||
<$declareStandardTransform()$>
|
||||
|
||||
out vec2 varQuadPosition;
|
||||
layout(location = 0) in vec4 inPosition;
|
||||
|
||||
void main(void) {
|
||||
varQuadPosition = inPosition.xy;
|
||||
gl_Position = inPosition;
|
||||
}
|
||||
}
|
|
@ -2,15 +2,17 @@
|
|||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// drawOpaqueStencil.frag
|
||||
// fragment shader
|
||||
// Draw white
|
||||
//
|
||||
// Created by Sam Gateau on 9/29/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Created by Sam Gateau on 5/30/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
outFragColor = vec4(1.0);
|
||||
}
|
|
@ -16,6 +16,12 @@
|
|||
#include "DrawTransformUnitQuad_vert.h"
|
||||
#include "DrawTexcoordRectTransformUnitQuad_vert.h"
|
||||
#include "DrawViewportQuadTransformTexcoord_vert.h"
|
||||
#include "DrawVertexPosition_vert.h"
|
||||
#include "DrawTransformVertexPosition_vert.h"
|
||||
|
||||
const char DrawNada_frag[] = "void main(void) {}"; // DrawNada is really simple...
|
||||
|
||||
#include "DrawWhite_frag.h"
|
||||
#include "DrawTexture_frag.h"
|
||||
#include "DrawTextureOpaque_frag.h"
|
||||
#include "DrawColoredTexture_frag.h"
|
||||
|
@ -26,6 +32,10 @@ ShaderPointer StandardShaderLib::_drawUnitQuadTexcoordVS;
|
|||
ShaderPointer StandardShaderLib::_drawTransformUnitQuadVS;
|
||||
ShaderPointer StandardShaderLib::_drawTexcoordRectTransformUnitQuadVS;
|
||||
ShaderPointer StandardShaderLib::_drawViewportQuadTransformTexcoordVS;
|
||||
ShaderPointer StandardShaderLib::_drawVertexPositionVS;
|
||||
ShaderPointer StandardShaderLib::_drawTransformVertexPositionVS;
|
||||
ShaderPointer StandardShaderLib::_drawNadaPS;
|
||||
ShaderPointer StandardShaderLib::_drawWhitePS;
|
||||
ShaderPointer StandardShaderLib::_drawTexturePS;
|
||||
ShaderPointer StandardShaderLib::_drawTextureOpaquePS;
|
||||
ShaderPointer StandardShaderLib::_drawColoredTexturePS;
|
||||
|
@ -85,6 +95,34 @@ ShaderPointer StandardShaderLib::getDrawViewportQuadTransformTexcoordVS() {
|
|||
return _drawViewportQuadTransformTexcoordVS;
|
||||
}
|
||||
|
||||
ShaderPointer StandardShaderLib::getDrawVertexPositionVS() {
|
||||
if (!_drawVertexPositionVS) {
|
||||
_drawVertexPositionVS = gpu::Shader::createVertex(std::string(DrawVertexPosition_vert));
|
||||
}
|
||||
return _drawVertexPositionVS;
|
||||
}
|
||||
|
||||
ShaderPointer StandardShaderLib::getDrawTransformVertexPositionVS() {
|
||||
if (!_drawTransformVertexPositionVS) {
|
||||
_drawTransformVertexPositionVS = gpu::Shader::createVertex(std::string(DrawTransformVertexPosition_vert));
|
||||
}
|
||||
return _drawTransformVertexPositionVS;
|
||||
}
|
||||
|
||||
ShaderPointer StandardShaderLib::getDrawNadaPS() {
|
||||
if (!_drawNadaPS) {
|
||||
_drawNadaPS = gpu::Shader::createPixel(std::string(DrawNada_frag));
|
||||
}
|
||||
return _drawNadaPS;
|
||||
}
|
||||
|
||||
ShaderPointer StandardShaderLib::getDrawWhitePS() {
|
||||
if (!_drawWhitePS) {
|
||||
_drawWhitePS = gpu::Shader::createPixel(std::string(DrawWhite_frag));
|
||||
}
|
||||
return _drawWhitePS;
|
||||
}
|
||||
|
||||
ShaderPointer StandardShaderLib::getDrawTexturePS() {
|
||||
if (!_drawTexturePS) {
|
||||
_drawTexturePS = gpu::Shader::createPixel(std::string(DrawTexture_frag));
|
||||
|
@ -99,8 +137,6 @@ ShaderPointer StandardShaderLib::getDrawTextureOpaquePS() {
|
|||
return _drawTextureOpaquePS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ShaderPointer StandardShaderLib::getDrawColoredTexturePS() {
|
||||
if (!_drawColoredTexturePS) {
|
||||
_drawColoredTexturePS = gpu::Shader::createPixel(std::string(DrawColoredTexture_frag));
|
||||
|
|
|
@ -37,6 +37,15 @@ public:
|
|||
// Shader draws the unit quad in the full viewport clipPos = ([(-1,-1),(1,1)]) and transform the texcoord = [(0,0),(1,1)] by the model transform.
|
||||
static ShaderPointer getDrawViewportQuadTransformTexcoordVS();
|
||||
|
||||
// Shader draw the fed vertex position and transform it by the full model transform stack (Model, View, Proj).
|
||||
// simply output the world pos and the clip pos to the next stage
|
||||
static ShaderPointer getDrawVertexPositionVS();
|
||||
static ShaderPointer getDrawTransformVertexPositionVS();
|
||||
|
||||
// PShader does nothing, no really nothing, but still needed for defining a program triggering rasterization
|
||||
static ShaderPointer getDrawNadaPS();
|
||||
|
||||
static ShaderPointer getDrawWhitePS();
|
||||
static ShaderPointer getDrawTexturePS();
|
||||
static ShaderPointer getDrawTextureOpaquePS();
|
||||
static ShaderPointer getDrawColoredTexturePS();
|
||||
|
@ -51,6 +60,12 @@ protected:
|
|||
static ShaderPointer _drawTransformUnitQuadVS;
|
||||
static ShaderPointer _drawTexcoordRectTransformUnitQuadVS;
|
||||
static ShaderPointer _drawViewportQuadTransformTexcoordVS;
|
||||
|
||||
static ShaderPointer _drawVertexPositionVS;
|
||||
static ShaderPointer _drawTransformVertexPositionVS;
|
||||
|
||||
static ShaderPointer _drawNadaPS;
|
||||
static ShaderPointer _drawWhitePS;
|
||||
static ShaderPointer _drawTexturePS;
|
||||
static ShaderPointer _drawTextureOpaquePS;
|
||||
static ShaderPointer _drawColoredTexturePS;
|
||||
|
|
|
@ -211,6 +211,8 @@ namespace khronos {
|
|||
|
||||
template <uint32_t ALIGNMENT>
|
||||
inline uint32_t evalAlignedCompressedBlockCount(uint32_t value) {
|
||||
enum { val = ALIGNMENT && !(ALIGNMENT & (ALIGNMENT - 1)) };
|
||||
static_assert(val, "template parameter ALIGNMENT must be a power of 2");
|
||||
// FIXME add static assert that ALIGNMENT is a power of 2
|
||||
static uint32_t ALIGNMENT_REMAINDER = ALIGNMENT - 1;
|
||||
return (value + ALIGNMENT_REMAINDER) / ALIGNMENT;
|
||||
|
|
|
@ -11,14 +11,28 @@
|
|||
|
||||
#include "KTXCache.h"
|
||||
|
||||
#include <SettingHandle.h>
|
||||
#include <ktx/KTX.h>
|
||||
|
||||
using File = cache::File;
|
||||
using FilePointer = cache::FilePointer;
|
||||
|
||||
// Whenever a change is made to the serialized format for the KTX cache that isn't backward compatible,
|
||||
// this value should be incremented. This will force the KTX cache to be wiped
|
||||
const int KTXCache::CURRENT_VERSION = 0x01;
|
||||
const int KTXCache::INVALID_VERSION = 0x00;
|
||||
const char* KTXCache::SETTING_VERSION_NAME = "hifi.ktx.cache_version";
|
||||
|
||||
KTXCache::KTXCache(const std::string& dir, const std::string& ext) :
|
||||
FileCache(dir, ext) {
|
||||
initialize();
|
||||
|
||||
Setting::Handle<int> cacheVersionHandle(SETTING_VERSION_NAME, INVALID_VERSION);
|
||||
auto cacheVersion = cacheVersionHandle.get();
|
||||
if (cacheVersion != CURRENT_VERSION) {
|
||||
wipe();
|
||||
cacheVersionHandle.set(CURRENT_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
KTXFilePointer KTXCache::writeFile(const char* data, Metadata&& metadata) {
|
||||
|
|
|
@ -27,6 +27,12 @@ class KTXCache : public cache::FileCache {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Whenever a change is made to the serialized format for the KTX cache that isn't backward compatible,
|
||||
// this value should be incremented. This will force the KTX cache to be wiped
|
||||
static const int CURRENT_VERSION;
|
||||
static const int INVALID_VERSION;
|
||||
static const char* SETTING_VERSION_NAME;
|
||||
|
||||
KTXCache(const std::string& dir, const std::string& ext);
|
||||
|
||||
KTXFilePointer writeFile(const char* data, Metadata&& metadata);
|
||||
|
|
|
@ -241,6 +241,42 @@ void Mesh::forEach(std::function<void(glm::vec3)> vertexFunc,
|
|||
}
|
||||
}
|
||||
|
||||
MeshPointer Mesh::createIndexedTriangles_P3F(uint32_t numVertices, uint32_t numIndices, const glm::vec3* vertices, const uint32_t* indices) {
|
||||
MeshPointer mesh;
|
||||
if (numVertices == 0) { return mesh; }
|
||||
if (numIndices < 3) { return mesh; }
|
||||
|
||||
mesh = std::make_shared<Mesh>();
|
||||
|
||||
// Vertex buffer
|
||||
mesh->setVertexBuffer(gpu::BufferView(new gpu::Buffer(numVertices * sizeof(glm::vec3), (gpu::Byte*) vertices), gpu::Element::VEC3F_XYZ));
|
||||
|
||||
// trim down the indices to shorts if possible
|
||||
if (numIndices < std::numeric_limits<uint16_t>::max()) {
|
||||
Indices16 shortIndicesVector;
|
||||
int16_t* shortIndices = nullptr;
|
||||
if (indices) {
|
||||
shortIndicesVector.resize(numIndices);
|
||||
for (uint32_t i = 0; i < numIndices; i++) {
|
||||
shortIndicesVector[i] = indices[i];
|
||||
}
|
||||
shortIndices = shortIndicesVector.data();
|
||||
}
|
||||
|
||||
mesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(numIndices * sizeof(uint16_t), (gpu::Byte*) shortIndices), gpu::Element::INDEX_UINT16));
|
||||
} else {
|
||||
|
||||
mesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(numIndices * sizeof(uint32_t), (gpu::Byte*) indices), gpu::Element::INDEX_INT32));
|
||||
}
|
||||
|
||||
|
||||
std::vector<model::Mesh::Part> parts;
|
||||
parts.push_back(model::Mesh::Part(0, numIndices, 0, model::Mesh::TRIANGLES));
|
||||
mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL));
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
|
||||
Geometry::Geometry() {
|
||||
}
|
||||
|
@ -256,3 +292,5 @@ Geometry::~Geometry() {
|
|||
void Geometry::setMesh(const MeshPointer& mesh) {
|
||||
_mesh = mesh;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -65,6 +65,9 @@ public:
|
|||
const gpu::BufferStream& getVertexStream() const { return _vertexStream; }
|
||||
|
||||
// Index Buffer
|
||||
using Indices16 = std::vector<int16_t>;
|
||||
using Indices32 = std::vector<int32_t>;
|
||||
|
||||
void setIndexBuffer(const BufferView& buffer);
|
||||
const BufferView& getIndexBuffer() const { return _indexBuffer; }
|
||||
size_t getNumIndices() const { return _indexBuffer.getNumElements(); }
|
||||
|
@ -127,6 +130,9 @@ public:
|
|||
std::function<void(glm::vec3)> normalFunc,
|
||||
std::function<void(uint32_t)> indexFunc);
|
||||
|
||||
|
||||
static MeshPointer createIndexedTriangles_P3F(uint32_t numVertices, uint32_t numTriangles, const glm::vec3* vertices = nullptr, const uint32_t* indices = nullptr);
|
||||
|
||||
protected:
|
||||
|
||||
gpu::Stream::FormatPointer _vertexFormat;
|
||||
|
|
|
@ -97,7 +97,7 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
|
|||
}
|
||||
|
||||
auto skyState = std::make_shared<gpu::State>();
|
||||
skyState->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
skyState->setStencilTest(true, 0xFF, gpu::State::StencilTest(1, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
|
||||
thePipeline = gpu::Pipeline::create(skyShader, skyState);
|
||||
}
|
||||
|
|
|
@ -45,7 +45,6 @@ Q_DECLARE_METATYPE(QNetworkAccessManager::Operation)
|
|||
Q_DECLARE_METATYPE(JSONCallbackParameters)
|
||||
|
||||
const QString ACCOUNTS_GROUP = "accounts";
|
||||
static const auto METAVERSE_SESSION_ID_HEADER = QString("HFM-SessionID").toLocal8Bit();
|
||||
|
||||
JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, const QString& jsonCallbackMethod,
|
||||
QObject* errorCallbackReceiver, const QString& errorCallbackMethod,
|
||||
|
@ -201,6 +200,13 @@ void AccountManager::setAuthURL(const QUrl& authURL) {
|
|||
}
|
||||
}
|
||||
|
||||
void AccountManager::setSessionID(const QUuid& sessionID) {
|
||||
if (_sessionID != sessionID) {
|
||||
qCDebug(networking) << "Metaverse session ID changed to" << uuidStringWithoutCurlyBraces(sessionID);
|
||||
_sessionID = sessionID;
|
||||
}
|
||||
}
|
||||
|
||||
void AccountManager::sendRequest(const QString& path,
|
||||
AccountManagerAuth::Type authType,
|
||||
QNetworkAccessManager::Operation operation,
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace AccountManagerAuth {
|
|||
Q_DECLARE_METATYPE(AccountManagerAuth::Type);
|
||||
|
||||
const QByteArray ACCESS_TOKEN_AUTHORIZATION_HEADER = "Authorization";
|
||||
const auto METAVERSE_SESSION_ID_HEADER = QString("HFM-SessionID").toLocal8Bit();
|
||||
|
||||
using UserAgentGetter = std::function<QString()>;
|
||||
|
||||
|
@ -90,7 +91,7 @@ public:
|
|||
static QJsonObject dataObjectFromResponse(QNetworkReply& requestReply);
|
||||
|
||||
QUuid getSessionID() const { return _sessionID; }
|
||||
void setSessionID(const QUuid& sessionID) { _sessionID = sessionID; }
|
||||
void setSessionID(const QUuid& sessionID);
|
||||
|
||||
void setTemporaryDomain(const QUuid& domainID, const QString& key);
|
||||
const QString& getTemporaryDomainKey(const QUuid& domainID) { return _accountInfo.getTemporaryDomainKey(domainID); }
|
||||
|
|
|
@ -11,52 +11,81 @@
|
|||
|
||||
#include "FileCache.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <QDir>
|
||||
#include <QSaveFile>
|
||||
#include <unordered_set>
|
||||
#include <queue>
|
||||
#include <cassert>
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QSaveFile>
|
||||
#include <QtCore/QStorageInfo>
|
||||
|
||||
#include <PathUtils.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <sys/utime.h>
|
||||
#else
|
||||
#include <utime.h>
|
||||
#endif
|
||||
|
||||
#ifdef NDEBUG
|
||||
Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache", QtWarningMsg)
|
||||
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache")
|
||||
#endif
|
||||
using namespace cache;
|
||||
|
||||
static const std::string MANIFEST_NAME = "manifest";
|
||||
static const char DIR_SEP = '/';
|
||||
static const char EXT_SEP = '.';
|
||||
|
||||
static const size_t BYTES_PER_MEGABYTES = 1024 * 1024;
|
||||
static const size_t BYTES_PER_GIGABYTES = 1024 * BYTES_PER_MEGABYTES;
|
||||
const size_t FileCache::DEFAULT_UNUSED_MAX_SIZE = 5 * BYTES_PER_GIGABYTES; // 5GB
|
||||
const size_t FileCache::MAX_UNUSED_MAX_SIZE = 100 * BYTES_PER_GIGABYTES; // 100GB
|
||||
const size_t FileCache::DEFAULT_OFFLINE_MAX_SIZE = 2 * BYTES_PER_GIGABYTES; // 2GB
|
||||
const size_t FileCache::DEFAULT_MAX_SIZE { GB_TO_BYTES(5) };
|
||||
const size_t FileCache::MAX_MAX_SIZE { GB_TO_BYTES(100) };
|
||||
const size_t FileCache::DEFAULT_MIN_FREE_STORAGE_SPACE { GB_TO_BYTES(1) };
|
||||
|
||||
void FileCache::setUnusedFileCacheSize(size_t unusedFilesMaxSize) {
|
||||
_unusedFilesMaxSize = std::min(unusedFilesMaxSize, MAX_UNUSED_MAX_SIZE);
|
||||
reserve(0);
|
||||
|
||||
std::string getCacheName(const std::string& dirname_str) {
|
||||
QString dirname { dirname_str.c_str() };
|
||||
QDir dir(dirname);
|
||||
if (!dir.isAbsolute()) {
|
||||
return dirname_str;
|
||||
}
|
||||
return dir.dirName().toStdString();
|
||||
}
|
||||
|
||||
std::string getCachePath(const std::string& dirname_str) {
|
||||
QString dirname { dirname_str.c_str() };
|
||||
QDir dir(dirname);
|
||||
if (dir.isAbsolute()) {
|
||||
return dirname_str;
|
||||
}
|
||||
return PathUtils::getAppLocalDataFilePath(dirname).toStdString();
|
||||
}
|
||||
|
||||
void FileCache::setMinFreeSize(size_t size) {
|
||||
_minFreeSpaceSize = size;
|
||||
clean();
|
||||
emit dirty();
|
||||
}
|
||||
|
||||
void FileCache::setOfflineFileCacheSize(size_t offlineFilesMaxSize) {
|
||||
_offlineFilesMaxSize = std::min(offlineFilesMaxSize, MAX_UNUSED_MAX_SIZE);
|
||||
void FileCache::setMaxSize(size_t maxSize) {
|
||||
_maxSize = std::min(maxSize, MAX_MAX_SIZE);
|
||||
clean();
|
||||
emit dirty();
|
||||
}
|
||||
|
||||
FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject* parent) :
|
||||
QObject(parent),
|
||||
_ext(ext),
|
||||
_dirname(dirname),
|
||||
_dirpath(PathUtils::getAppLocalDataFilePath(dirname.c_str()).toStdString()) {}
|
||||
_dirname(getCacheName(dirname)),
|
||||
_dirpath(getCachePath(dirname)) {
|
||||
}
|
||||
|
||||
FileCache::~FileCache() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void fileDeleter(File* file) {
|
||||
file->deleter();
|
||||
}
|
||||
|
||||
void FileCache::initialize() {
|
||||
QDir dir(_dirpath.c_str());
|
||||
|
||||
|
@ -84,7 +113,7 @@ void FileCache::initialize() {
|
|||
}
|
||||
|
||||
FilePointer FileCache::addFile(Metadata&& metadata, const std::string& filepath) {
|
||||
FilePointer file(createFile(std::move(metadata), filepath).release(), &fileDeleter);
|
||||
FilePointer file(createFile(std::move(metadata), filepath).release(), std::mem_fn(&File::deleter));
|
||||
if (file) {
|
||||
_numTotalFiles += 1;
|
||||
_totalFilesSize += file->getLength();
|
||||
|
@ -141,6 +170,7 @@ FilePointer FileCache::getFile(const Key& key) {
|
|||
if (it != _files.cend()) {
|
||||
file = it->second.lock();
|
||||
if (file) {
|
||||
file->touch();
|
||||
// if it exists, it is active - remove it from the cache
|
||||
removeUnusedFile(file);
|
||||
qCDebug(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str());
|
||||
|
@ -155,82 +185,127 @@ FilePointer FileCache::getFile(const Key& key) {
|
|||
}
|
||||
|
||||
std::string FileCache::getFilepath(const Key& key) {
|
||||
return _dirpath + '/' + key + '.' + _ext;
|
||||
return _dirpath + DIR_SEP + key + EXT_SEP + _ext;
|
||||
}
|
||||
|
||||
void FileCache::addUnusedFile(const FilePointer file) {
|
||||
void FileCache::addUnusedFile(const FilePointer& file) {
|
||||
{
|
||||
Lock lock(_filesMutex);
|
||||
_files[file->getKey()] = file;
|
||||
}
|
||||
|
||||
reserve(file->getLength());
|
||||
file->_LRUKey = ++_lastLRUKey;
|
||||
|
||||
{
|
||||
Lock lock(_unusedFilesMutex);
|
||||
_unusedFiles.insert({ file->_LRUKey, file });
|
||||
_unusedFiles.insert(file);
|
||||
_numUnusedFiles += 1;
|
||||
_unusedFilesSize += file->getLength();
|
||||
}
|
||||
clean();
|
||||
|
||||
emit dirty();
|
||||
}
|
||||
|
||||
void FileCache::removeUnusedFile(const FilePointer file) {
|
||||
void FileCache::removeUnusedFile(const FilePointer& file) {
|
||||
Lock lock(_unusedFilesMutex);
|
||||
const auto it = _unusedFiles.find(file->_LRUKey);
|
||||
if (it != _unusedFiles.cend()) {
|
||||
_unusedFiles.erase(it);
|
||||
if (_unusedFiles.erase(file)) {
|
||||
_numUnusedFiles -= 1;
|
||||
_unusedFilesSize -= file->getLength();
|
||||
}
|
||||
}
|
||||
|
||||
void FileCache::reserve(size_t length) {
|
||||
Lock unusedLock(_unusedFilesMutex);
|
||||
while (!_unusedFiles.empty() &&
|
||||
_unusedFilesSize + length > _unusedFilesMaxSize) {
|
||||
auto it = _unusedFiles.begin();
|
||||
auto file = it->second;
|
||||
auto length = file->getLength();
|
||||
size_t FileCache::getOverbudgetAmount() const {
|
||||
size_t result = 0;
|
||||
|
||||
unusedLock.unlock();
|
||||
{
|
||||
file->_cache = nullptr;
|
||||
Lock lock(_filesMutex);
|
||||
_files.erase(file->getKey());
|
||||
size_t currentFreeSpace = QStorageInfo(_dirpath.c_str()).bytesFree();
|
||||
if (_minFreeSpaceSize > currentFreeSpace) {
|
||||
result = _minFreeSpaceSize - currentFreeSpace;
|
||||
}
|
||||
|
||||
if (_totalFilesSize > _maxSize) {
|
||||
result = std::max(_totalFilesSize - _maxSize, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace cache {
|
||||
struct FilePointerComparator {
|
||||
bool operator()(const FilePointer& a, const FilePointer& b) {
|
||||
return a->_modified > b->_modified;
|
||||
}
|
||||
unusedLock.lock();
|
||||
};
|
||||
}
|
||||
|
||||
_unusedFiles.erase(it);
|
||||
_numTotalFiles -= 1;
|
||||
_numUnusedFiles -= 1;
|
||||
_totalFilesSize -= length;
|
||||
_unusedFilesSize -= length;
|
||||
void FileCache::eject(const FilePointer& file) {
|
||||
file->_cache = nullptr;
|
||||
const auto& length = file->getLength();
|
||||
const auto& key = file->getKey();
|
||||
|
||||
{
|
||||
Lock lock(_filesMutex);
|
||||
if (0 != _files.erase(key)) {
|
||||
_numTotalFiles -= 1;
|
||||
_totalFilesSize -= length;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Lock unusedLock(_unusedFilesMutex);
|
||||
if (0 != _unusedFiles.erase(file)) {
|
||||
_numUnusedFiles -= 1;
|
||||
_unusedFilesSize -= length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileCache::clean() {
|
||||
size_t overbudgetAmount = getOverbudgetAmount();
|
||||
|
||||
// Avoid sorting the unused files by LRU if we're not over budget / under free space
|
||||
if (0 == overbudgetAmount) {
|
||||
return;
|
||||
}
|
||||
|
||||
Lock unusedLock(_unusedFilesMutex);
|
||||
using Queue = std::priority_queue<FilePointer, std::vector<FilePointer>, FilePointerComparator>;
|
||||
Queue queue;
|
||||
for (const auto& file : _unusedFiles) {
|
||||
queue.push(file);
|
||||
}
|
||||
|
||||
while (!queue.empty() && overbudgetAmount > 0) {
|
||||
auto file = queue.top();
|
||||
queue.pop();
|
||||
eject(file);
|
||||
auto length = file->getLength();
|
||||
overbudgetAmount -= std::min(length, overbudgetAmount);
|
||||
}
|
||||
}
|
||||
|
||||
void FileCache::wipe() {
|
||||
Lock unusedFilesLock(_unusedFilesMutex);
|
||||
while (!_unusedFiles.empty()) {
|
||||
eject(*_unusedFiles.begin());
|
||||
}
|
||||
}
|
||||
|
||||
void FileCache::clear() {
|
||||
Lock unusedFilesLock(_unusedFilesMutex);
|
||||
for (const auto& pair : _unusedFiles) {
|
||||
auto& file = pair.second;
|
||||
file->_cache = nullptr;
|
||||
// Eliminate any overbudget files
|
||||
clean();
|
||||
|
||||
if (_totalFilesSize > _offlineFilesMaxSize) {
|
||||
_totalFilesSize -= file->getLength();
|
||||
} else {
|
||||
file->_shouldPersist = true;
|
||||
qCDebug(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str());
|
||||
}
|
||||
// Mark everything remaining as persisted
|
||||
Lock unusedFilesLock(_unusedFilesMutex);
|
||||
for (auto& file : _unusedFiles) {
|
||||
file->_shouldPersist = true;
|
||||
file->_cache = nullptr;
|
||||
qCDebug(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str());
|
||||
}
|
||||
_unusedFiles.clear();
|
||||
}
|
||||
|
||||
void File::deleter() {
|
||||
if (_cache) {
|
||||
FilePointer self(this, &fileDeleter);
|
||||
_cache->addUnusedFile(self);
|
||||
_cache->addUnusedFile(FilePointer(this, std::mem_fn(&File::deleter)));
|
||||
} else {
|
||||
deleteLater();
|
||||
}
|
||||
|
@ -239,7 +314,9 @@ void File::deleter() {
|
|||
File::File(Metadata&& metadata, const std::string& filepath) :
|
||||
_key(std::move(metadata.key)),
|
||||
_length(metadata.length),
|
||||
_filepath(filepath) {}
|
||||
_filepath(filepath),
|
||||
_modified(QFileInfo(_filepath.c_str()).lastRead().toMSecsSinceEpoch()) {
|
||||
}
|
||||
|
||||
File::~File() {
|
||||
QFile file(getFilepath().c_str());
|
||||
|
@ -248,3 +325,8 @@ File::~File() {
|
|||
file.remove();
|
||||
}
|
||||
}
|
||||
|
||||
void File::touch() {
|
||||
utime(_filepath.c_str(), nullptr);
|
||||
_modified = std::max<int64_t>(QFileInfo(_filepath.c_str()).lastRead().toMSecsSinceEpoch(), _modified);
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
#include <memory>
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
@ -35,24 +36,31 @@ class FileCache : public QObject {
|
|||
Q_PROPERTY(size_t sizeTotal READ getSizeTotalFiles NOTIFY dirty)
|
||||
Q_PROPERTY(size_t sizeCached READ getSizeCachedFiles NOTIFY dirty)
|
||||
|
||||
static const size_t DEFAULT_UNUSED_MAX_SIZE;
|
||||
static const size_t MAX_UNUSED_MAX_SIZE;
|
||||
static const size_t DEFAULT_OFFLINE_MAX_SIZE;
|
||||
static const size_t DEFAULT_MAX_SIZE;
|
||||
static const size_t MAX_MAX_SIZE;
|
||||
static const size_t DEFAULT_MIN_FREE_STORAGE_SPACE;
|
||||
|
||||
public:
|
||||
// You can initialize the FileCache with a directory name (ex.: "temp_jpgs") that will be relative to the application local data, OR with a full path
|
||||
// The file cache will ignore any file that doesn't match the extension provided
|
||||
FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr);
|
||||
virtual ~FileCache();
|
||||
|
||||
// Remove all unlocked items from the cache
|
||||
void wipe();
|
||||
|
||||
size_t getNumTotalFiles() const { return _numTotalFiles; }
|
||||
size_t getNumCachedFiles() const { return _numUnusedFiles; }
|
||||
size_t getSizeTotalFiles() const { return _totalFilesSize; }
|
||||
size_t getSizeCachedFiles() const { return _unusedFilesSize; }
|
||||
|
||||
void setUnusedFileCacheSize(size_t unusedFilesMaxSize);
|
||||
size_t getUnusedFileCacheSize() const { return _unusedFilesSize; }
|
||||
// Set the maximum amount of disk space to use on disk
|
||||
void setMaxSize(size_t maxCacheSize);
|
||||
|
||||
void setOfflineFileCacheSize(size_t offlineFilesMaxSize);
|
||||
|
||||
// initialize FileCache with a directory name (not a path, ex.: "temp_jpgs") and an ext (ex.: "jpg")
|
||||
FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr);
|
||||
virtual ~FileCache();
|
||||
// Set the minumum amount of free disk space to retain. This supercedes the max size,
|
||||
// so if the cache is consuming all but 500 MB of the drive, unused entries will be ejected
|
||||
// to free up more space, regardless of the cache max size
|
||||
void setMinFreeSize(size_t size);
|
||||
|
||||
using Key = std::string;
|
||||
struct Metadata {
|
||||
|
@ -76,10 +84,11 @@ public:
|
|||
signals:
|
||||
void dirty();
|
||||
|
||||
protected:
|
||||
public:
|
||||
/// must be called after construction to create the cache on the fs and restore persisted files
|
||||
void initialize();
|
||||
|
||||
// Add file to the cache and return the cache entry.
|
||||
FilePointer writeFile(const char* data, Metadata&& metadata, bool overwrite = false);
|
||||
FilePointer getFile(const Key& key);
|
||||
|
||||
|
@ -89,17 +98,28 @@ protected:
|
|||
private:
|
||||
using Mutex = std::recursive_mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
using Map = std::unordered_map<Key, std::weak_ptr<File>>;
|
||||
using Set = std::unordered_set<FilePointer>;
|
||||
using KeySet = std::unordered_set<Key>;
|
||||
|
||||
friend class File;
|
||||
|
||||
std::string getFilepath(const Key& key);
|
||||
|
||||
FilePointer addFile(Metadata&& metadata, const std::string& filepath);
|
||||
void addUnusedFile(const FilePointer file);
|
||||
void removeUnusedFile(const FilePointer file);
|
||||
void reserve(size_t length);
|
||||
void addUnusedFile(const FilePointer& file);
|
||||
void removeUnusedFile(const FilePointer& file);
|
||||
void clean();
|
||||
void clear();
|
||||
// Remove a file from the cache
|
||||
void eject(const FilePointer& file);
|
||||
|
||||
size_t getOverbudgetAmount() const;
|
||||
|
||||
// FIXME it might be desirable to have the min free space variable be static so it can be
|
||||
// shared among multiple instances of FileCache
|
||||
std::atomic<size_t> _minFreeSpaceSize { DEFAULT_MIN_FREE_STORAGE_SPACE };
|
||||
std::atomic<size_t> _maxSize { DEFAULT_MAX_SIZE };
|
||||
std::atomic<size_t> _numTotalFiles { 0 };
|
||||
std::atomic<size_t> _numUnusedFiles { 0 };
|
||||
std::atomic<size_t> _totalFilesSize { 0 };
|
||||
|
@ -110,15 +130,11 @@ private:
|
|||
std::string _dirpath;
|
||||
bool _initialized { false };
|
||||
|
||||
std::unordered_map<Key, std::weak_ptr<File>> _files;
|
||||
Map _files;
|
||||
Mutex _filesMutex;
|
||||
|
||||
std::map<int, FilePointer> _unusedFiles;
|
||||
Set _unusedFiles;
|
||||
Mutex _unusedFilesMutex;
|
||||
size_t _unusedFilesMaxSize { DEFAULT_UNUSED_MAX_SIZE };
|
||||
int _lastLRUKey { 0 };
|
||||
|
||||
size_t _offlineFilesMaxSize { DEFAULT_OFFLINE_MAX_SIZE };
|
||||
};
|
||||
|
||||
class File : public QObject {
|
||||
|
@ -128,8 +144,8 @@ public:
|
|||
using Key = FileCache::Key;
|
||||
using Metadata = FileCache::Metadata;
|
||||
|
||||
Key getKey() const { return _key; }
|
||||
size_t getLength() const { return _length; }
|
||||
const Key& getKey() const { return _key; }
|
||||
const size_t& getLength() const { return _length; }
|
||||
std::string getFilepath() const { return _filepath; }
|
||||
|
||||
virtual ~File();
|
||||
|
@ -142,13 +158,15 @@ protected:
|
|||
|
||||
private:
|
||||
friend class FileCache;
|
||||
friend struct FilePointerComparator;
|
||||
|
||||
const Key _key;
|
||||
const size_t _length;
|
||||
const std::string _filepath;
|
||||
|
||||
FileCache* _cache;
|
||||
int _LRUKey { 0 };
|
||||
void touch();
|
||||
FileCache* _cache { nullptr };
|
||||
int64_t _modified { 0 };
|
||||
|
||||
bool _shouldPersist { false };
|
||||
};
|
||||
|
|
|
@ -52,9 +52,8 @@ bool readStatus(QByteArray statusData) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void runLocalSandbox(QString contentPath, bool autoShutdown, QString runningMarkerName, bool noUpdater) {
|
||||
void runLocalSandbox(QString contentPath, bool autoShutdown, bool noUpdater) {
|
||||
QString serverPath = "./server-console/server-console.exe";
|
||||
qCDebug(networking) << "Running marker path is: " << runningMarkerName;
|
||||
qCDebug(networking) << "Server path is: " << serverPath;
|
||||
qCDebug(networking) << "autoShutdown: " << autoShutdown;
|
||||
qCDebug(networking) << "noUpdater: " << noUpdater;
|
||||
|
@ -74,8 +73,8 @@ void runLocalSandbox(QString contentPath, bool autoShutdown, QString runningMark
|
|||
}
|
||||
|
||||
if (autoShutdown) {
|
||||
QString interfaceRunningStateFile = RunningMarker::getMarkerFilePath(runningMarkerName);
|
||||
args << "--shutdownWatcher" << interfaceRunningStateFile;
|
||||
auto pid = QCoreApplication::applicationPid();
|
||||
args << "--shutdownWith" << QString::number(pid);
|
||||
}
|
||||
|
||||
if (noUpdater) {
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace SandboxUtils {
|
|||
|
||||
QNetworkReply* getStatus();
|
||||
bool readStatus(QByteArray statusData);
|
||||
void runLocalSandbox(QString contentPath, bool autoShutdown, QString runningMarkerName, bool noUpdater);
|
||||
void runLocalSandbox(QString contentPath, bool autoShutdown, bool noUpdater);
|
||||
};
|
||||
|
||||
#endif // hifi_SandboxUtils_h
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
#include <DependencyManager.h>
|
||||
#include "AddressManager.h"
|
||||
|
||||
static const QString USER_ACTIVITY_URL = "/api/v1/user_activities";
|
||||
|
||||
UserActivityLogger& UserActivityLogger::getInstance() {
|
||||
static UserActivityLogger sharedInstance;
|
||||
return sharedInstance;
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include <SettingHandle.h>
|
||||
#include "AddressManager.h"
|
||||
|
||||
const QString USER_ACTIVITY_URL = "/api/v1/user_activities";
|
||||
|
||||
class UserActivityLogger : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ void UserActivityLoggerScriptingInterface::palAction(QString action, QString tar
|
|||
}
|
||||
|
||||
void UserActivityLoggerScriptingInterface::palOpened(float secondsOpened) {
|
||||
doLogAction("pal_opened", {
|
||||
doLogAction("pal_opened", {
|
||||
{ "seconds_opened", secondsOpened }
|
||||
});
|
||||
}
|
||||
|
@ -71,6 +71,14 @@ void UserActivityLoggerScriptingInterface::makeUserConnection(QString otherID, b
|
|||
doLogAction("makeUserConnection", payload);
|
||||
}
|
||||
|
||||
void UserActivityLoggerScriptingInterface::bubbleToggled(bool newValue) {
|
||||
doLogAction(newValue ? "bubbleOn" : "bubbleOff");
|
||||
}
|
||||
|
||||
void UserActivityLoggerScriptingInterface::bubbleActivated() {
|
||||
doLogAction("bubbleActivated");
|
||||
}
|
||||
|
||||
void UserActivityLoggerScriptingInterface::logAction(QString action, QVariantMap details) {
|
||||
doLogAction(action, QJsonObject::fromVariantMap(details));
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ public:
|
|||
Q_INVOKABLE void palAction(QString action, QString target);
|
||||
Q_INVOKABLE void palOpened(float secondsOpen);
|
||||
Q_INVOKABLE void makeUserConnection(QString otherUser, bool success, QString details = "");
|
||||
Q_INVOKABLE void bubbleToggled(bool newValue);
|
||||
Q_INVOKABLE void bubbleActivated();
|
||||
Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{});
|
||||
private:
|
||||
void doLogAction(QString action, QJsonObject details = {});
|
||||
|
|
|
@ -65,8 +65,7 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
|
|||
_lastStep(0),
|
||||
_loopsWithoutOwner(0),
|
||||
_accelerationNearlyGravityCount(0),
|
||||
_numInactiveUpdates(1),
|
||||
_outgoingPriority(0)
|
||||
_numInactiveUpdates(1)
|
||||
{
|
||||
_type = MOTIONSTATE_TYPE_ENTITY;
|
||||
assert(_entity);
|
||||
|
@ -75,6 +74,8 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
|
|||
// we need the side-effects of EntityMotionState::setShape() so we call it explicitly here
|
||||
// rather than pass the legit shape pointer to the ObjectMotionState ctor above.
|
||||
setShape(shape);
|
||||
|
||||
_outgoingPriority = _entity->getPendingOwnershipPriority();
|
||||
}
|
||||
|
||||
EntityMotionState::~EntityMotionState() {
|
||||
|
@ -84,7 +85,7 @@ EntityMotionState::~EntityMotionState() {
|
|||
|
||||
void EntityMotionState::updateServerPhysicsVariables() {
|
||||
assert(entityTreeIsLocked());
|
||||
if (_entity->getSimulatorID() == Physics::getSessionUUID()) {
|
||||
if (isLocallyOwned()) {
|
||||
// don't slam these values if we are the simulation owner
|
||||
return;
|
||||
}
|
||||
|
@ -114,6 +115,7 @@ void EntityMotionState::handleDeactivation() {
|
|||
|
||||
// virtual
|
||||
void EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
||||
assert(_entity);
|
||||
assert(entityTreeIsLocked());
|
||||
updateServerPhysicsVariables();
|
||||
ObjectMotionState::handleEasyChanges(flags);
|
||||
|
@ -135,23 +137,23 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
|||
_nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||
}
|
||||
_loopsWithoutOwner = 0;
|
||||
} else if (_entity->getSimulatorID() == Physics::getSessionUUID()) {
|
||||
_numInactiveUpdates = 0;
|
||||
} else if (isLocallyOwned()) {
|
||||
// we just inherited ownership, make sure our desired priority matches what we have
|
||||
upgradeOutgoingPriority(_entity->getSimulationPriority());
|
||||
} else {
|
||||
_outgoingPriority = 0;
|
||||
_nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||
_numInactiveUpdates = 0;
|
||||
}
|
||||
}
|
||||
if (flags & Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY) {
|
||||
// The DIRTY_SIMULATOR_OWNERSHIP_PRIORITY bits really mean "we should bid for ownership because
|
||||
// a local script has been changing physics properties, or we should adjust our own ownership priority".
|
||||
// The desired priority is determined by which bits were set.
|
||||
if (flags & Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB) {
|
||||
_outgoingPriority = SCRIPT_GRAB_SIMULATION_PRIORITY;
|
||||
} else {
|
||||
_outgoingPriority = SCRIPT_POKE_SIMULATION_PRIORITY;
|
||||
}
|
||||
// The DIRTY_SIMULATOR_OWNERSHIP_PRIORITY bit means one of the following:
|
||||
// (1) we own it but may need to change the priority OR...
|
||||
// (2) we don't own it but should bid (because a local script has been changing physics properties)
|
||||
uint8_t newPriority = isLocallyOwned() ? _entity->getSimulationOwner().getPriority() : _entity->getSimulationOwner().getPendingPriority();
|
||||
_outgoingPriority = glm::max(_outgoingPriority, newPriority);
|
||||
|
||||
// reset bid expiry so that we bid ASAP
|
||||
_nextOwnershipBid = 0;
|
||||
}
|
||||
|
@ -170,6 +172,7 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
|||
|
||||
// virtual
|
||||
bool EntityMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
|
||||
assert(_entity);
|
||||
updateServerPhysicsVariables();
|
||||
return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
|
||||
}
|
||||
|
@ -315,7 +318,7 @@ bool EntityMotionState::isCandidateForOwnership() const {
|
|||
assert(_entity);
|
||||
assert(entityTreeIsLocked());
|
||||
return _outgoingPriority != 0
|
||||
|| Physics::getSessionUUID() == _entity->getSimulatorID()
|
||||
|| isLocallyOwned()
|
||||
|| _entity->dynamicDataNeedsTransmit();
|
||||
}
|
||||
|
||||
|
@ -489,7 +492,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_entity->getSimulatorID() != Physics::getSessionUUID()) {
|
||||
if (!isLocallyOwned()) {
|
||||
// we don't own the simulation
|
||||
|
||||
// NOTE: we do not volunteer to own kinematic or static objects
|
||||
|
@ -597,7 +600,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
properties.clearSimulationOwner();
|
||||
_outgoingPriority = 0;
|
||||
_entity->setPendingOwnershipPriority(_outgoingPriority, now);
|
||||
} else if (Physics::getSessionUUID() != _entity->getSimulatorID()) {
|
||||
} else if (!isLocallyOwned()) {
|
||||
// we don't own the simulation for this entity yet, but we're sending a bid for it
|
||||
quint8 bidPriority = glm::max<uint8_t>(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY);
|
||||
properties.setSimulationOwner(Physics::getSessionUUID(), bidPriority);
|
||||
|
@ -786,6 +789,10 @@ void EntityMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& ma
|
|||
_entity->computeCollisionGroupAndFinalMask(group, mask);
|
||||
}
|
||||
|
||||
bool EntityMotionState::isLocallyOwned() const {
|
||||
return _entity->getSimulatorID() == Physics::getSessionUUID();
|
||||
}
|
||||
|
||||
bool EntityMotionState::shouldBeLocallyOwned() const {
|
||||
return (_outgoingPriority > VOLUNTEER_SIMULATION_PRIORITY && _outgoingPriority > _entity->getSimulationPriority()) ||
|
||||
_entity->getSimulatorID() == Physics::getSessionUUID();
|
||||
|
|
|
@ -79,6 +79,7 @@ public:
|
|||
|
||||
virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override;
|
||||
|
||||
bool isLocallyOwned() const override;
|
||||
bool shouldBeLocallyOwned() const override;
|
||||
|
||||
friend class PhysicalEntitySimulation;
|
||||
|
|
|
@ -202,6 +202,7 @@ void ObjectMotionState::setShape(const btCollisionShape* shape) {
|
|||
}
|
||||
|
||||
void ObjectMotionState::handleEasyChanges(uint32_t& flags) {
|
||||
assert(_body && _shape);
|
||||
if (flags & Simulation::DIRTY_POSITION) {
|
||||
btTransform worldTrans = _body->getWorldTransform();
|
||||
btVector3 newPosition = glmToBullet(getObjectPosition());
|
||||
|
@ -282,6 +283,7 @@ void ObjectMotionState::handleEasyChanges(uint32_t& flags) {
|
|||
}
|
||||
|
||||
bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
|
||||
assert(_body && _shape);
|
||||
if (flags & Simulation::DIRTY_SHAPE) {
|
||||
// make sure the new shape is valid
|
||||
if (!isReadyToComputeShape()) {
|
||||
|
|
|
@ -79,7 +79,7 @@ public:
|
|||
static ShapeManager* getShapeManager();
|
||||
|
||||
ObjectMotionState(const btCollisionShape* shape);
|
||||
~ObjectMotionState();
|
||||
virtual ~ObjectMotionState();
|
||||
|
||||
virtual void handleEasyChanges(uint32_t& flags);
|
||||
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine);
|
||||
|
@ -146,6 +146,7 @@ public:
|
|||
void dirtyInternalKinematicChanges() { _hasInternalKinematicChanges = true; }
|
||||
void clearInternalKinematicChanges() { _hasInternalKinematicChanges = false; }
|
||||
|
||||
virtual bool isLocallyOwned() const { return false; }
|
||||
virtual bool shouldBeLocallyOwned() const { return false; }
|
||||
|
||||
friend class PhysicsEngine;
|
||||
|
|
|
@ -130,7 +130,7 @@ void PhysicalEntitySimulation::clearEntitiesInternal() {
|
|||
}
|
||||
|
||||
// then remove the objects (aka MotionStates) from physics
|
||||
_physicsEngine->removeObjects(_physicalObjects);
|
||||
_physicsEngine->removeSetOfObjects(_physicalObjects);
|
||||
|
||||
// delete the MotionStates
|
||||
// TODO: after we invert the entities/physics lib dependencies we will let EntityItem delete
|
||||
|
|
|
@ -129,6 +129,9 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
|||
}
|
||||
body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
|
||||
body->updateInertiaTensor();
|
||||
if (motionState->isLocallyOwned()) {
|
||||
_activeStaticBodies.insert(body);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -174,19 +177,9 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) {
|
|||
// frame (because the framerate is faster than our physics simulation rate). When this happens we must scan
|
||||
// _activeStaticBodies for objects that were recently deleted so we don't try to access a dangling pointer.
|
||||
for (auto object : objects) {
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
|
||||
std::vector<btRigidBody*>::reverse_iterator itr = _activeStaticBodies.rbegin();
|
||||
while (itr != _activeStaticBodies.rend()) {
|
||||
if (body == *itr) {
|
||||
if (*itr != *(_activeStaticBodies.rbegin())) {
|
||||
// swap with rbegin
|
||||
*itr = *(_activeStaticBodies.rbegin());
|
||||
}
|
||||
_activeStaticBodies.pop_back();
|
||||
break;
|
||||
}
|
||||
++itr;
|
||||
std::set<btRigidBody*>::iterator itr = _activeStaticBodies.find(object->getRigidBody());
|
||||
if (itr != _activeStaticBodies.end()) {
|
||||
_activeStaticBodies.erase(itr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -207,7 +200,7 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) {
|
|||
}
|
||||
|
||||
// Same as above, but takes a Set instead of a Vector. Should only be called during teardown.
|
||||
void PhysicsEngine::removeObjects(const SetOfMotionStates& objects) {
|
||||
void PhysicsEngine::removeSetOfObjects(const SetOfMotionStates& objects) {
|
||||
_contactMap.clear();
|
||||
for (auto object : objects) {
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
|
@ -245,14 +238,16 @@ VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& ob
|
|||
object->clearIncomingDirtyFlags();
|
||||
}
|
||||
if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) {
|
||||
_activeStaticBodies.push_back(object->getRigidBody());
|
||||
_activeStaticBodies.insert(object->getRigidBody());
|
||||
}
|
||||
}
|
||||
// active static bodies have changed (in an Easy way) and need their Aabbs updated
|
||||
// but we've configured Bullet to NOT update them automatically (for improved performance)
|
||||
// so we must do it ourselves
|
||||
for (size_t i = 0; i < _activeStaticBodies.size(); ++i) {
|
||||
_dynamicsWorld->updateSingleAabb(_activeStaticBodies[i]);
|
||||
std::set<btRigidBody*>::const_iterator itr = _activeStaticBodies.begin();
|
||||
while (itr != _activeStaticBodies.end()) {
|
||||
_dynamicsWorld->updateSingleAabb(*itr);
|
||||
++itr;
|
||||
}
|
||||
return stillNeedChange;
|
||||
}
|
||||
|
@ -496,13 +491,23 @@ const CollisionEvents& PhysicsEngine::getCollisionEvents() {
|
|||
|
||||
const VectorOfMotionStates& PhysicsEngine::getChangedMotionStates() {
|
||||
BT_PROFILE("copyOutgoingChanges");
|
||||
|
||||
_dynamicsWorld->synchronizeMotionStates();
|
||||
|
||||
// Bullet will not deactivate static objects (it doesn't expect them to be active)
|
||||
// so we must deactivate them ourselves
|
||||
for (size_t i = 0; i < _activeStaticBodies.size(); ++i) {
|
||||
_activeStaticBodies[i]->forceActivationState(ISLAND_SLEEPING);
|
||||
std::set<btRigidBody*>::const_iterator itr = _activeStaticBodies.begin();
|
||||
while (itr != _activeStaticBodies.end()) {
|
||||
btRigidBody* body = *itr;
|
||||
body->forceActivationState(ISLAND_SLEEPING);
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(body->getUserPointer());
|
||||
if (motionState) {
|
||||
_dynamicsWorld->addChangedMotionState(motionState);
|
||||
}
|
||||
++itr;
|
||||
}
|
||||
_activeStaticBodies.clear();
|
||||
_dynamicsWorld->synchronizeMotionStates();
|
||||
|
||||
_hasOutgoingChanges = false;
|
||||
return _dynamicsWorld->getChangedMotionStates();
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_PhysicsEngine_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <QUuid>
|
||||
|
@ -53,7 +54,7 @@ public:
|
|||
uint32_t getNumSubsteps();
|
||||
|
||||
void removeObjects(const VectorOfMotionStates& objects);
|
||||
void removeObjects(const SetOfMotionStates& objects); // only called during teardown
|
||||
void removeSetOfObjects(const SetOfMotionStates& objects); // only called during teardown
|
||||
|
||||
void addObjects(const VectorOfMotionStates& objects);
|
||||
VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects);
|
||||
|
@ -114,7 +115,7 @@ private:
|
|||
CollisionEvents _collisionEvents;
|
||||
QHash<QUuid, EntityDynamicPointer> _objectDynamics;
|
||||
QHash<btRigidBody*, QSet<QUuid>> _objectDynamicsByBody;
|
||||
std::vector<btRigidBody*> _activeStaticBodies;
|
||||
std::set<btRigidBody*> _activeStaticBodies;
|
||||
|
||||
glm::vec3 _originOffset;
|
||||
|
||||
|
|
|
@ -51,6 +51,8 @@ public:
|
|||
const VectorOfMotionStates& getChangedMotionStates() const { return _changedMotionStates; }
|
||||
const VectorOfMotionStates& getDeactivatedMotionStates() const { return _deactivatedStates; }
|
||||
|
||||
void addChangedMotionState(ObjectMotionState* motionState) { _changedMotionStates.push_back(motionState); }
|
||||
|
||||
private:
|
||||
// call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState()
|
||||
void synchronizeMotionState(btRigidBody* body);
|
||||
|
|
|
@ -23,7 +23,7 @@ ProceduralSkybox::ProceduralSkybox() : model::Skybox() {
|
|||
_procedural._fragmentSource = skybox_frag;
|
||||
// Adjust the pipeline state for background using the stencil test
|
||||
_procedural.setDoesFade(false);
|
||||
_procedural._opaqueState->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
_procedural._opaqueState->setStencilTest(true, 0xFF, gpu::State::StencilTest(1, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
}
|
||||
|
||||
bool ProceduralSkybox::empty() {
|
||||
|
|
|
@ -260,7 +260,7 @@ static void addLink(const AnimPose& rootPose, const AnimPose& pose, const AnimPo
|
|||
// there is room, so lets draw a nice bone
|
||||
|
||||
glm::vec3 uAxis, vAxis, wAxis;
|
||||
generateBasisVectors(boneAxis0, glm::vec3(1, 0, 0), uAxis, vAxis, wAxis);
|
||||
generateBasisVectors(boneAxis0, glm::vec3(1.0f, 0.0f, 0.0f), uAxis, vAxis, wAxis);
|
||||
|
||||
glm::vec3 boneBaseCorners[NUM_BASE_CORNERS];
|
||||
boneBaseCorners[0] = pose0 * ((uAxis * radius) + (vAxis * radius) + (wAxis * radius));
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <gpu/Context.h>
|
||||
|
||||
#include "AntialiasingEffect.h"
|
||||
#include "StencilMaskPass.h"
|
||||
#include "TextureCache.h"
|
||||
#include "FramebufferCache.h"
|
||||
#include "DependencyManager.h"
|
||||
|
@ -70,6 +71,8 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() {
|
|||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
|
||||
PrepareStencil::testMask(*state);
|
||||
|
||||
state->setDepthTest(false, false, gpu::LESS_EQUAL);
|
||||
|
||||
// Good to go add the brand new pipeline
|
||||
|
@ -93,6 +96,7 @@ const gpu::PipelinePointer& Antialiasing::getBlendPipeline() {
|
|||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
|
||||
state->setDepthTest(false, false, gpu::LESS_EQUAL);
|
||||
PrepareStencil::testMask(*state);
|
||||
|
||||
// Good to go add the brand new pipeline
|
||||
_blendPipeline = gpu::Pipeline::create(program, state);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <gpu/Batch.h>
|
||||
#include <gpu/Context.h>
|
||||
|
||||
#include "StencilMaskPass.h"
|
||||
#include "AbstractViewStateInterface.h"
|
||||
#include "GeometryCache.h"
|
||||
#include "TextureCache.h"
|
||||
|
@ -27,18 +28,15 @@
|
|||
#include "deferred_light_point_vert.h"
|
||||
#include "deferred_light_spot_vert.h"
|
||||
|
||||
#include "directional_light_frag.h"
|
||||
#include "directional_ambient_light_frag.h"
|
||||
#include "directional_skybox_light_frag.h"
|
||||
|
||||
#include "directional_light_shadow_frag.h"
|
||||
#include "directional_ambient_light_shadow_frag.h"
|
||||
#include "directional_skybox_light_shadow_frag.h"
|
||||
|
||||
#include "local_lights_shading_frag.h"
|
||||
#include "local_lights_drawOutline_frag.h"
|
||||
#include "point_light_frag.h"
|
||||
#include "spot_light_frag.h"
|
||||
|
||||
|
||||
using namespace render;
|
||||
|
||||
|
@ -82,48 +80,26 @@ enum DeferredShader_BufferSlot {
|
|||
};
|
||||
|
||||
static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& program, LightLocationsPtr& locations);
|
||||
static void loadLightVolumeProgram(const char* vertSource, const char* fragSource, bool front, gpu::PipelinePointer& program, LightLocationsPtr& locations);
|
||||
|
||||
const char no_light_frag[] =
|
||||
R"SCRIBE(
|
||||
out vec4 _fragColor;
|
||||
|
||||
void main(void) {
|
||||
_fragColor = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
)SCRIBE"
|
||||
;
|
||||
|
||||
void DeferredLightingEffect::init() {
|
||||
_directionalLightLocations = std::make_shared<LightLocations>();
|
||||
_directionalAmbientSphereLightLocations = std::make_shared<LightLocations>();
|
||||
_directionalSkyboxLightLocations = std::make_shared<LightLocations>();
|
||||
|
||||
_directionalLightShadowLocations = std::make_shared<LightLocations>();
|
||||
_directionalAmbientSphereLightShadowLocations = std::make_shared<LightLocations>();
|
||||
_directionalSkyboxLightShadowLocations = std::make_shared<LightLocations>();
|
||||
|
||||
_localLightLocations = std::make_shared<LightLocations>();
|
||||
_localLightOutlineLocations = std::make_shared<LightLocations>();
|
||||
_pointLightLocations = std::make_shared<LightLocations>();
|
||||
_spotLightLocations = std::make_shared<LightLocations>();
|
||||
|
||||
loadLightProgram(deferred_light_vert, directional_light_frag, false, _directionalLight, _directionalLightLocations);
|
||||
loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations);
|
||||
loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations);
|
||||
|
||||
loadLightProgram(deferred_light_vert, directional_light_shadow_frag, false, _directionalLightShadow, _directionalLightShadowLocations);
|
||||
loadLightProgram(deferred_light_vert, directional_ambient_light_shadow_frag, false, _directionalAmbientSphereLightShadow, _directionalAmbientSphereLightShadowLocations);
|
||||
loadLightProgram(deferred_light_vert, directional_skybox_light_shadow_frag, false, _directionalSkyboxLightShadow, _directionalSkyboxLightShadowLocations);
|
||||
|
||||
loadLightProgram(deferred_light_vert, local_lights_shading_frag, true, _localLight, _localLightLocations);
|
||||
loadLightProgram(deferred_light_vert, local_lights_drawOutline_frag, true, _localLightOutline, _localLightOutlineLocations);
|
||||
|
||||
loadLightVolumeProgram(deferred_light_point_vert, no_light_frag, false, _pointLightBack, _pointLightLocations);
|
||||
loadLightVolumeProgram(deferred_light_point_vert, no_light_frag, true, _pointLightFront, _pointLightLocations);
|
||||
loadLightVolumeProgram(deferred_light_spot_vert, no_light_frag, false, _spotLightBack, _spotLightLocations);
|
||||
loadLightVolumeProgram(deferred_light_spot_vert, no_light_frag, true, _spotLightFront, _spotLightLocations);
|
||||
|
||||
// Light Stage and clusters
|
||||
_lightStage = std::make_shared<LightStage>();
|
||||
|
||||
|
@ -160,11 +136,11 @@ void DeferredLightingEffect::init() {
|
|||
|
||||
|
||||
lp->setAmbientIntensity(0.5f);
|
||||
lp->setAmbientMap(_defaultSkyboxAmbientTexture);
|
||||
auto irradianceSH = _defaultSkyboxAmbientTexture->getIrradiance();
|
||||
if (irradianceSH) {
|
||||
lp->setAmbientSphere((*irradianceSH));
|
||||
}
|
||||
lp->setAmbientMap(_defaultSkyboxAmbientTexture);
|
||||
auto irradianceSH = _defaultSkyboxAmbientTexture->getIrradiance();
|
||||
if (irradianceSH) {
|
||||
lp->setAmbientSphere((*irradianceSH));
|
||||
}
|
||||
}
|
||||
|
||||
void DeferredLightingEffect::setupKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit) {
|
||||
|
@ -267,7 +243,7 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo
|
|||
state->setColorWriteMask(true, true, true, false);
|
||||
|
||||
if (lightVolume) {
|
||||
state->setStencilTest(true, 0x00, gpu::State::StencilTest(1, 0xFF, gpu::LESS_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
PrepareStencil::testShape(*state);
|
||||
|
||||
state->setCullMode(gpu::State::CULL_BACK);
|
||||
// state->setCullMode(gpu::State::CULL_FRONT);
|
||||
|
@ -280,7 +256,7 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo
|
|||
|
||||
} else {
|
||||
// Stencil test all the light passes for objects pixels only, not the background
|
||||
state->setStencilTest(true, 0x00, gpu::State::StencilTest(0, 0x01, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
PrepareStencil::testShape(*state);
|
||||
|
||||
state->setCullMode(gpu::State::CULL_BACK);
|
||||
// additive blending
|
||||
|
@ -290,39 +266,6 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo
|
|||
|
||||
}
|
||||
|
||||
|
||||
static void loadLightVolumeProgram(const char* vertSource, const char* fragSource, bool front, gpu::PipelinePointer& pipeline, LightLocationsPtr& locations) {
|
||||
gpu::ShaderPointer program = makeLightProgram(vertSource, fragSource, locations);
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
|
||||
// Stencil test all the light passes for objects pixels only, not the background
|
||||
|
||||
if (front) {
|
||||
state->setCullMode(gpu::State::CULL_BACK);
|
||||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_DECR, gpu::State::STENCIL_OP_KEEP));
|
||||
|
||||
// state->setDepthClampEnable(true);
|
||||
// TODO: We should use DepthClamp and avoid changing geometry for inside /outside cases
|
||||
// additive blending
|
||||
// state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
|
||||
//state->setColorWriteMask(true, true, true, false);
|
||||
state->setColorWriteMask(false, false, false, false);
|
||||
} else {
|
||||
state->setCullMode(gpu::State::CULL_FRONT);
|
||||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_INCR, gpu::State::STENCIL_OP_KEEP));
|
||||
// additive blending
|
||||
// state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
// state->setColorWriteMask(true, true, true, false);
|
||||
state->setColorWriteMask(false, false, false, false);
|
||||
}
|
||||
pipeline = gpu::Pipeline::create(program, state);
|
||||
|
||||
}
|
||||
|
||||
void DeferredLightingEffect::setGlobalLight(const model::LightPointer& light) {
|
||||
/* auto globalLight = _allocatedLights.front();
|
||||
globalLight->setDirection(light->getDirection());
|
||||
|
@ -535,7 +478,7 @@ void PrepareDeferred::run(const RenderContextPointer& renderContext, const Input
|
|||
gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_COLOR1 | gpu::Framebuffer::BUFFER_COLOR2 | gpu::Framebuffer::BUFFER_COLOR3 |
|
||||
gpu::Framebuffer::BUFFER_DEPTH |
|
||||
gpu::Framebuffer::BUFFER_STENCIL,
|
||||
vec4(vec3(0), 0), 1.0, 0.0, true);
|
||||
vec4(vec3(0), 0), 1.0, 1, true);
|
||||
|
||||
// For the rest of the rendering, bind the lighting model
|
||||
batch.setUniformBuffer(LIGHTING_MODEL_BUFFER_SLOT, lightingModel->getParametersBuffer());
|
||||
|
@ -619,8 +562,8 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
|
|||
batch.setResourceTexture(SHADOW_MAP_UNIT, globalShadow->map);
|
||||
}
|
||||
|
||||
auto& program = deferredLightingEffect->_shadowMapEnabled ? deferredLightingEffect->_directionalLightShadow : deferredLightingEffect->_directionalLight;
|
||||
LightLocationsPtr locations = deferredLightingEffect->_shadowMapEnabled ? deferredLightingEffect->_directionalLightShadowLocations : deferredLightingEffect->_directionalLightLocations;
|
||||
auto& program = deferredLightingEffect->_directionalSkyboxLight;
|
||||
LightLocationsPtr locations = deferredLightingEffect->_directionalSkyboxLightLocations;
|
||||
const auto& keyLight = deferredLightingEffect->_allocatedLights[deferredLightingEffect->_globalLights.front()];
|
||||
|
||||
// Setup the global directional pass pipeline
|
||||
|
|
|
@ -82,32 +82,21 @@ private:
|
|||
|
||||
gpu::PipelinePointer _directionalSkyboxLight;
|
||||
gpu::PipelinePointer _directionalAmbientSphereLight;
|
||||
gpu::PipelinePointer _directionalLight;
|
||||
|
||||
gpu::PipelinePointer _directionalSkyboxLightShadow;
|
||||
gpu::PipelinePointer _directionalAmbientSphereLightShadow;
|
||||
gpu::PipelinePointer _directionalLightShadow;
|
||||
|
||||
gpu::PipelinePointer _localLight;
|
||||
gpu::PipelinePointer _localLightOutline;
|
||||
|
||||
gpu::PipelinePointer _pointLightBack;
|
||||
gpu::PipelinePointer _pointLightFront;
|
||||
gpu::PipelinePointer _spotLightBack;
|
||||
gpu::PipelinePointer _spotLightFront;
|
||||
|
||||
LightLocationsPtr _directionalSkyboxLightLocations;
|
||||
LightLocationsPtr _directionalAmbientSphereLightLocations;
|
||||
LightLocationsPtr _directionalLightLocations;
|
||||
|
||||
LightLocationsPtr _directionalSkyboxLightShadowLocations;
|
||||
LightLocationsPtr _directionalAmbientSphereLightShadowLocations;
|
||||
LightLocationsPtr _directionalLightShadowLocations;
|
||||
|
||||
LightLocationsPtr _localLightLocations;
|
||||
LightLocationsPtr _localLightOutlineLocations;
|
||||
LightLocationsPtr _pointLightLocations;
|
||||
LightLocationsPtr _spotLightLocations;
|
||||
|
||||
using Lights = std::vector<model::LightPointer>;
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "TextureCache.h"
|
||||
#include "RenderUtilsLogging.h"
|
||||
#include "StencilMaskPass.h"
|
||||
|
||||
#include "gpu/StandardShaderLib.h"
|
||||
|
||||
|
@ -1610,6 +1611,9 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const
|
|||
state->setBlendFunction(true,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
|
||||
PrepareStencil::testMask(*state);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("lineData"), LINE_DATA_SLOT));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
@ -1663,11 +1667,14 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) {
|
|||
|
||||
// enable decal blend
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
PrepareStencil::testMask(*state);
|
||||
|
||||
_standardDrawPipeline = gpu::Pipeline::create(program, state);
|
||||
|
||||
|
||||
auto stateNoBlend = std::make_shared<gpu::State>();
|
||||
PrepareStencil::testMaskDrawShape(*state);
|
||||
|
||||
auto noBlendPS = gpu::StandardShaderLib::getDrawTextureOpaquePS();
|
||||
auto programNoBlend = gpu::Shader::createProgram(vs, noBlendPS);
|
||||
gpu::Shader::makeProgram((*programNoBlend));
|
||||
|
@ -1690,12 +1697,14 @@ void GeometryCache::useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bo
|
|||
|
||||
auto stateLayered = std::make_shared<gpu::State>();
|
||||
stateLayered->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
PrepareStencil::testMask(*stateLayered);
|
||||
_gridPipelineLayered = gpu::Pipeline::create(program, stateLayered);
|
||||
|
||||
auto state = std::make_shared<gpu::State>(stateLayered->getValues());
|
||||
const float DEPTH_BIAS = 0.001f;
|
||||
state->setDepthBias(DEPTH_BIAS);
|
||||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
PrepareStencil::testMaskDrawShape(*state);
|
||||
_gridPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
|
@ -1773,6 +1782,11 @@ static void buildWebShader(const std::string& vertShaderText, const std::string&
|
|||
state->setBlendFunction(blendEnable,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
if (blendEnable) {
|
||||
PrepareStencil::testMask(*state);
|
||||
} else {
|
||||
PrepareStencil::testMaskDrawShape(*state);
|
||||
}
|
||||
|
||||
pipelinePointerOut = gpu::Pipeline::create(shaderPointerOut, state);
|
||||
}
|
||||
|
@ -1858,6 +1872,12 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transp
|
|||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
|
||||
if (config.isTransparent()) {
|
||||
PrepareStencil::testMask(*state);
|
||||
} else {
|
||||
PrepareStencil::testMaskDrawShape(*state);
|
||||
}
|
||||
|
||||
gpu::ShaderPointer program = (config.isUnlit()) ? _unlitShader : _simpleShader;
|
||||
gpu::PipelinePointer pipeline = gpu::Pipeline::create(program, state);
|
||||
_simplePrograms.insert(config, pipeline);
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
//
|
||||
// HitEffect.cpp
|
||||
// interface/src/renderer
|
||||
//
|
||||
// Created by Andrzej Kapolka on 7/14/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL
|
||||
|
||||
|
||||
#include <glm/gtc/random.hpp>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <PathUtils.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "AbstractViewStateInterface.h"
|
||||
#include "HitEffect.h"
|
||||
|
||||
#include "TextureCache.h"
|
||||
#include "DependencyManager.h"
|
||||
#include "ViewFrustum.h"
|
||||
#include "GeometryCache.h"
|
||||
|
||||
#include <gpu/Context.h>
|
||||
|
||||
#include "hit_effect_vert.h"
|
||||
#include "hit_effect_frag.h"
|
||||
|
||||
|
||||
HitEffect::HitEffect() {
|
||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
}
|
||||
|
||||
HitEffect::~HitEffect() {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (_geometryId && geometryCache) {
|
||||
geometryCache->releaseID(_geometryId);
|
||||
}
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer& HitEffect::getHitEffectPipeline() {
|
||||
if (!_hitEffectPipeline) {
|
||||
auto vs = gpu::Shader::createVertex(std::string(hit_effect_vert));
|
||||
auto ps = gpu::Shader::createPixel(std::string(hit_effect_frag));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
|
||||
state->setDepthTest(false, false, gpu::LESS_EQUAL);
|
||||
|
||||
// Blend on transparent
|
||||
state->setBlendFunction(true,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
|
||||
// Good to go add the brand new pipeline
|
||||
_hitEffectPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
return _hitEffectPipeline;
|
||||
}
|
||||
|
||||
void HitEffect::run(const render::RenderContextPointer& renderContext) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
args->getViewFrustum().evalProjectionMatrix(projMat);
|
||||
args->getViewFrustum().evalViewTransform(viewMat);
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat);
|
||||
batch.setModelTransform(Transform());
|
||||
|
||||
batch.setPipeline(getHitEffectPipeline());
|
||||
|
||||
static const glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
static const glm::vec2 bottomLeft(-1.0f, -1.0f);
|
||||
static const glm::vec2 topRight(1.0f, 1.0f);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, color, _geometryId);
|
||||
});
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
//
|
||||
// hitEffect.h
|
||||
// hifi
|
||||
//
|
||||
// Created by eric levin on 7/17/15.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef hifi_hitEffect_h
|
||||
#define hifi_hitEffect_h
|
||||
|
||||
#include <render/DrawTask.h>
|
||||
|
||||
class HitEffectConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool enabled MEMBER enabled)
|
||||
public:
|
||||
HitEffectConfig() : render::Job::Config(false) {}
|
||||
};
|
||||
|
||||
class HitEffect {
|
||||
public:
|
||||
using Config = HitEffectConfig;
|
||||
using JobModel = render::Job::Model<HitEffect, Config>;
|
||||
|
||||
HitEffect();
|
||||
~HitEffect();
|
||||
void configure(const Config& config) {}
|
||||
void run(const render::RenderContextPointer& renderContext);
|
||||
|
||||
const gpu::PipelinePointer& getHitEffectPipeline();
|
||||
|
||||
private:
|
||||
int _geometryId { 0 };
|
||||
gpu::PipelinePointer _hitEffectPipeline;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
|
||||
#include "StencilMaskPass.h"
|
||||
|
||||
#include "lightClusters_drawGrid_vert.h"
|
||||
#include "lightClusters_drawGrid_frag.h"
|
||||
|
||||
|
|
|
@ -27,12 +27,12 @@
|
|||
#include <render/BlurTask.h>
|
||||
|
||||
#include "LightingModel.h"
|
||||
#include "StencilMaskPass.h"
|
||||
#include "DebugDeferredBuffer.h"
|
||||
#include "DeferredFramebuffer.h"
|
||||
#include "DeferredLightingEffect.h"
|
||||
#include "SurfaceGeometryPass.h"
|
||||
#include "FramebufferCache.h"
|
||||
#include "HitEffect.h"
|
||||
#include "TextureCache.h"
|
||||
#include "ZoneRenderer.h"
|
||||
|
||||
|
@ -43,8 +43,6 @@
|
|||
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
|
||||
#include "drawOpaqueStencil_frag.h"
|
||||
|
||||
|
||||
using namespace render;
|
||||
extern void initOverlay3DPipelines(render::ShapePlumber& plumber);
|
||||
|
@ -85,13 +83,13 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
const auto deferredFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(0);
|
||||
const auto lightingFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(1);
|
||||
|
||||
// draw a stencil mask in hidden regions of the framebuffer.
|
||||
task.addJob<PrepareStencil>("PrepareStencil", primaryFramebuffer);
|
||||
|
||||
// Render opaque objects in DeferredBuffer
|
||||
const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel).hasVarying();
|
||||
task.addJob<DrawStateSortDeferred>("DrawOpaqueDeferred", opaqueInputs, shapePlumber);
|
||||
|
||||
// Once opaque is all rendered create stencil background
|
||||
task.addJob<DrawStencilDeferred>("DrawOpaqueStencil", deferredFramebuffer);
|
||||
|
||||
task.addJob<EndGPURangeTimer>("OpaqueRangeTimer", opaqueRangeTimer);
|
||||
|
||||
|
||||
|
@ -387,88 +385,6 @@ void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs&
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
gpu::PipelinePointer DrawStencilDeferred::getOpaquePipeline() {
|
||||
if (!_opaquePipeline) {
|
||||
const gpu::int8 STENCIL_OPAQUE = 1;
|
||||
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(drawOpaqueStencil_frag));
|
||||
auto program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram((*program));
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_OPAQUE, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_KEEP));
|
||||
state->setColorWriteMask(0);
|
||||
|
||||
_opaquePipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
return _opaquePipeline;
|
||||
}
|
||||
|
||||
void DrawStencilDeferred::run(const RenderContextPointer& renderContext, const DeferredFramebufferPointer& deferredFramebuffer) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
|
||||
// from the touched pixel generate the stencil buffer
|
||||
RenderArgs* args = renderContext->args;
|
||||
doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
args->_batch = &batch;
|
||||
|
||||
auto deferredFboColorDepthStencil = deferredFramebuffer->getDeferredFramebufferDepthColor();
|
||||
|
||||
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setFramebuffer(deferredFboColorDepthStencil);
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setStateScissorRect(args->_viewport);
|
||||
|
||||
batch.setPipeline(getOpaquePipeline());
|
||||
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
batch.setResourceTexture(0, nullptr);
|
||||
|
||||
});
|
||||
args->_batch = nullptr;
|
||||
}
|
||||
|
||||
void DrawBackgroundDeferred::run(const RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
|
||||
const auto& inItems = inputs.get0();
|
||||
const auto& lightingModel = inputs.get1();
|
||||
if (!lightingModel->isBackgroundEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
args->_batch = &batch;
|
||||
// _gpuTimer.begin(batch);
|
||||
|
||||
batch.enableSkybox(true);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setStateScissorRect(args->_viewport);
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
args->getViewFrustum().evalProjectionMatrix(projMat);
|
||||
args->getViewFrustum().evalViewTransform(viewMat);
|
||||
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat);
|
||||
|
||||
renderItems(renderContext, inItems);
|
||||
// _gpuTimer.end(batch);
|
||||
});
|
||||
args->_batch = nullptr;
|
||||
|
||||
// std::static_pointer_cast<Config>(renderContext->jobConfig)->gpuTime = _gpuTimer.getAverage();
|
||||
}
|
||||
|
||||
void Blit::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_context);
|
||||
|
@ -538,3 +454,4 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -120,35 +120,6 @@ protected:
|
|||
bool _stateSort;
|
||||
};
|
||||
|
||||
class DeferredFramebuffer;
|
||||
class DrawStencilDeferred {
|
||||
public:
|
||||
using JobModel = render::Job::ModelI<DrawStencilDeferred, std::shared_ptr<DeferredFramebuffer>>;
|
||||
|
||||
void run(const render::RenderContextPointer& renderContext, const std::shared_ptr<DeferredFramebuffer>& deferredFramebuffer);
|
||||
|
||||
protected:
|
||||
gpu::PipelinePointer _opaquePipeline;
|
||||
|
||||
gpu::PipelinePointer getOpaquePipeline();
|
||||
};
|
||||
|
||||
using DrawBackgroundDeferredConfig = render::GPUJobConfig;
|
||||
|
||||
class DrawBackgroundDeferred {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2 <render::ItemBounds, LightingModelPointer>;
|
||||
|
||||
using Config = DrawBackgroundDeferredConfig;
|
||||
using JobModel = render::Job::ModelI<DrawBackgroundDeferred, Inputs, Config>;
|
||||
|
||||
void configure(const Config& config) {}
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
protected:
|
||||
gpu::RangeTimerPointer _gpuTimer;
|
||||
};
|
||||
|
||||
class DrawOverlay3DConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged)
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <gpu/Context.h>
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
|
||||
#include "StencilMaskPass.h"
|
||||
#include "DeferredLightingEffect.h"
|
||||
#include "TextureCache.h"
|
||||
#include "render/DrawTask.h"
|
||||
|
@ -330,6 +331,7 @@ void addPlumberPipeline(ShapePlumber& plumber,
|
|||
bool isWireframed = (i & 4);
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
PrepareStencil::testMaskDrawShape(*state);
|
||||
|
||||
// Depth test depends on transparency
|
||||
state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL);
|
||||
|
|
126
libraries/render-utils/src/StencilMaskPass.cpp
Normal file
126
libraries/render-utils/src/StencilMaskPass.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
//
|
||||
// StencilMaskPass.cpp
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Sam Gateau on 5/31/17.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "StencilMaskPass.h"
|
||||
|
||||
#include <RenderArgs.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include <gpu/Context.h>
|
||||
|
||||
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
|
||||
#include "stencil_drawMask_frag.h"
|
||||
|
||||
using namespace render;
|
||||
|
||||
void PrepareStencil::configure(const Config& config) {
|
||||
_maskMode = config.maskMode;
|
||||
_forceDraw = config.forceDraw;
|
||||
}
|
||||
|
||||
model::MeshPointer PrepareStencil::getMesh() {
|
||||
if (!_mesh) {
|
||||
|
||||
std::vector<glm::vec3> vertices {
|
||||
{ -1.0f, -1.0f, 0.0f }, { -1.0f, 0.0f, 0.0f },
|
||||
{ -1.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f },
|
||||
{ 1.0f, 1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f },
|
||||
{ 1.0f, -1.0f, 0.0f }, { 0.0f, -1.0f, 0.0f } };
|
||||
|
||||
std::vector<uint32_t> indices { 0, 7, 1, 1, 3, 2, 3, 5, 4, 5, 7, 6 };
|
||||
_mesh = model::Mesh::createIndexedTriangles_P3F((uint32_t) vertices.size(), (uint32_t) indices.size(), vertices.data(), indices.data());
|
||||
}
|
||||
return _mesh;
|
||||
}
|
||||
|
||||
gpu::PipelinePointer PrepareStencil::getMeshStencilPipeline() {
|
||||
if (!_meshStencilPipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawVertexPositionVS();
|
||||
auto ps = gpu::StandardShaderLib::getDrawNadaPS();
|
||||
auto program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram((*program));
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
drawMask(*state);
|
||||
state->setColorWriteMask(0);
|
||||
|
||||
_meshStencilPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
return _meshStencilPipeline;
|
||||
}
|
||||
|
||||
gpu::PipelinePointer PrepareStencil::getPaintStencilPipeline() {
|
||||
if (!_paintStencilPipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(stencil_drawMask_frag));
|
||||
auto program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram((*program));
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
drawMask(*state);
|
||||
state->setColorWriteMask(0);
|
||||
|
||||
_paintStencilPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
return _paintStencilPipeline;
|
||||
}
|
||||
|
||||
void PrepareStencil::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) {
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
// Only draw the stencil mask if in HMD mode or not forced.
|
||||
if (!_forceDraw && (args->_displayMode != RenderArgs::STEREO_HMD)) {
|
||||
return;
|
||||
}
|
||||
|
||||
doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
|
||||
if (_maskMode < 0) {
|
||||
batch.setPipeline(getMeshStencilPipeline());
|
||||
|
||||
auto mesh = getMesh();
|
||||
batch.setIndexBuffer(mesh->getIndexBuffer());
|
||||
batch.setInputFormat((mesh->getVertexFormat()));
|
||||
batch.setInputStream(0, mesh->getVertexStream());
|
||||
|
||||
// Draw
|
||||
auto part = mesh->getPartBuffer().get<model::Mesh::Part>(0);
|
||||
batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex);
|
||||
} else {
|
||||
batch.setPipeline(getPaintStencilPipeline());
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PrepareStencil::drawMask(gpu::State& state) {
|
||||
state.setStencilTest(true, 0xFF, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE));
|
||||
}
|
||||
|
||||
void PrepareStencil::testMask(gpu::State& state) {
|
||||
state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
}
|
||||
|
||||
void PrepareStencil::testBackground(gpu::State& state) {
|
||||
state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_BACKGROUND, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
}
|
||||
|
||||
void PrepareStencil::testMaskDrawShape(gpu::State& state) {
|
||||
state.setStencilTest(true, 0xFF, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_ZERO));
|
||||
}
|
||||
|
||||
void PrepareStencil::testShape(gpu::State& state) {
|
||||
state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_SHAPE, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
}
|
71
libraries/render-utils/src/StencilMaskPass.h
Normal file
71
libraries/render-utils/src/StencilMaskPass.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// StencilMaskPass.h
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Sam Gateau on 5/31/17.
|
||||
// Copyright 20154 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
#ifndef hifi_StencilMaskPass_h
|
||||
#define hifi_StencilMaskPass_h
|
||||
|
||||
#include <render/Engine.h>
|
||||
#include <gpu/Pipeline.h>
|
||||
#include <model/Geometry.h>
|
||||
|
||||
class PrepareStencilConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int maskMode MEMBER maskMode NOTIFY dirty)
|
||||
Q_PROPERTY(bool forceDraw MEMBER forceDraw NOTIFY dirty)
|
||||
|
||||
public:
|
||||
PrepareStencilConfig(bool enabled = true) : JobConfig(enabled) {}
|
||||
|
||||
int maskMode { 0 };
|
||||
bool forceDraw { false };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class PrepareStencil {
|
||||
public:
|
||||
using Config = PrepareStencilConfig;
|
||||
|
||||
using JobModel = render::Job::ModelI<PrepareStencil, gpu::FramebufferPointer, Config>;
|
||||
|
||||
void configure(const Config& config);
|
||||
|
||||
void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& dstFramebuffer);
|
||||
|
||||
static const gpu::int8 STENCIL_MASK = 2;
|
||||
static const gpu::int8 STENCIL_BACKGROUND = 1;
|
||||
static const gpu::int8 STENCIL_SHAPE = 0;
|
||||
|
||||
|
||||
static void drawMask(gpu::State& state);
|
||||
static void testMask(gpu::State& state);
|
||||
static void testBackground(gpu::State& state);
|
||||
static void testMaskDrawShape(gpu::State& state);
|
||||
static void testShape(gpu::State& state);
|
||||
|
||||
|
||||
private:
|
||||
gpu::PipelinePointer _meshStencilPipeline;
|
||||
gpu::PipelinePointer getMeshStencilPipeline();
|
||||
|
||||
gpu::PipelinePointer _paintStencilPipeline;
|
||||
gpu::PipelinePointer getPaintStencilPipeline();
|
||||
|
||||
model::MeshPointer _mesh;
|
||||
model::MeshPointer getMesh();
|
||||
|
||||
int _maskMode { 0 };
|
||||
bool _forceDraw { false };
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_StencilMaskPass_h
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#include <gpu/Context.h>
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
|
||||
#include "StencilMaskPass.h"
|
||||
|
||||
const int DepthLinearPass_FrameTransformSlot = 0;
|
||||
const int DepthLinearPass_DepthMapSlot = 0;
|
||||
|
@ -224,7 +224,7 @@ const gpu::PipelinePointer& LinearDepthPass::getLinearDepthPipeline() {
|
|||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
|
||||
// Stencil test the curvature pass for objects pixels only, not the background
|
||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
PrepareStencil::testShape(*state);
|
||||
|
||||
state->setColorWriteMask(true, false, false, false);
|
||||
|
||||
|
@ -250,6 +250,7 @@ const gpu::PipelinePointer& LinearDepthPass::getDownsamplePipeline() {
|
|||
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
PrepareStencil::testShape(*state);
|
||||
|
||||
state->setColorWriteMask(true, true, true, false);
|
||||
|
||||
|
@ -554,7 +555,7 @@ const gpu::PipelinePointer& SurfaceGeometryPass::getCurvaturePipeline() {
|
|||
|
||||
#ifdef USE_STENCIL_TEST
|
||||
// Stencil test the curvature pass for objects pixels only, not the background
|
||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
PrepareStencil::testShape(*state);
|
||||
#endif
|
||||
// Good to go add the brand new pipeline
|
||||
_curvaturePipeline = gpu::Pipeline::create(program, state);
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include <gpu/StandardShaderLib.h>
|
||||
|
||||
#include <RenderArgs.h>
|
||||
|
||||
#include "StencilMaskPass.h"
|
||||
#include "FramebufferCache.h"
|
||||
|
||||
#include "toneMapping_frag.h"
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <render/FilterTask.h>
|
||||
#include <render/DrawTask.h>
|
||||
|
||||
#include "StencilMaskPass.h"
|
||||
#include "DeferredLightingEffect.h"
|
||||
|
||||
#include "zone_drawKeyLight_frag.h"
|
||||
|
@ -74,6 +75,7 @@ const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() {
|
|||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
|
||||
PrepareStencil::testMask(*state);
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
_keyLightPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
@ -95,6 +97,7 @@ const gpu::PipelinePointer& DebugZoneLighting::getAmbientPipeline() {
|
|||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
|
||||
PrepareStencil::testMask(*state);
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
_ambientPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
@ -115,6 +118,7 @@ const gpu::PipelinePointer& DebugZoneLighting::getBackgroundPipeline() {
|
|||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
|
||||
PrepareStencil::testMask(*state);
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
_backgroundPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// directional_light.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 9/3/14.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
<@include DeferredBufferRead.slh@>
|
||||
<@include DeferredGlobalLight.slh@>
|
||||
|
||||
<$declareEvalLightmappedColor()$>
|
||||
<$declareEvalAmbientGlobalColor()$>
|
||||
|
||||
in vec2 _texCoord0;
|
||||
out vec4 _fragColor;
|
||||
|
||||
void main(void) {
|
||||
DeferredFrameTransform deferredTransform = getDeferredFrameTransform();
|
||||
DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0);
|
||||
|
||||
float shadowAttenuation = 1.0;
|
||||
|
||||
if (frag.mode == FRAG_MODE_UNLIT) {
|
||||
discard;
|
||||
} else if (frag.mode == FRAG_MODE_LIGHTMAPPED) {
|
||||
discard;
|
||||
} else {
|
||||
vec3 color = evalAmbientGlobalColor(
|
||||
getViewInverse(),
|
||||
shadowAttenuation,
|
||||
frag.obscurance,
|
||||
frag.position.xyz,
|
||||
frag.normal,
|
||||
frag.albedo,
|
||||
frag.fresnel,
|
||||
frag.metallic,
|
||||
frag.roughness);
|
||||
_fragColor = vec4(color, 1.0);
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// directional_light_shadow.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Zach Pomerantz on 1/18/2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
<@include Shadow.slh@>
|
||||
<@include DeferredBufferRead.slh@>
|
||||
<@include DeferredGlobalLight.slh@>
|
||||
|
||||
<$declareEvalLightmappedColor()$>
|
||||
<$declareEvalAmbientGlobalColor()$>
|
||||
|
||||
in vec2 _texCoord0;
|
||||
out vec4 _fragColor;
|
||||
|
||||
void main(void) {
|
||||
DeferredFrameTransform deferredTransform = getDeferredFrameTransform();
|
||||
DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0);
|
||||
|
||||
vec4 worldPos = getViewInverse() * vec4(frag.position.xyz, 1.0);
|
||||
float shadowAttenuation = evalShadowAttenuation(worldPos);
|
||||
|
||||
if (frag.mode == FRAG_MODE_UNLIT) {
|
||||
discard;
|
||||
} else if (frag.mode == FRAG_MODE_LIGHTMAPPED) {
|
||||
discard;
|
||||
} else {
|
||||
vec3 color = evalAmbientGlobalColor(
|
||||
getViewInverse(),
|
||||
shadowAttenuation,
|
||||
frag.obscurance,
|
||||
frag.position.xyz,
|
||||
frag.normal,
|
||||
frag.albedo,
|
||||
frag.fresnel,
|
||||
frag.metallic,
|
||||
frag.roughness);
|
||||
_fragColor = vec4(color, 1.0);
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
|
||||
// hit_effect.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Eric Levin on 7/20
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
<@include DeferredBufferWrite.slh@>
|
||||
|
||||
in vec2 varQuadPosition;
|
||||
out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
vec2 center = vec2(0.0, 0.0);
|
||||
float distFromCenter = distance( vec2(0.0, 0.0), varQuadPosition);
|
||||
float alpha = mix(0.0, 0.5, pow(distFromCenter,5.));
|
||||
outFragColor = vec4(1.0, 0.0, 0.0, alpha);
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// point_light.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Sam Gateau on 9/18/15.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
<!
|
||||
// Everything about deferred buffer
|
||||
<@include DeferredBufferRead.slh@>
|
||||
|
||||
<$declareDeferredCurvature()$>
|
||||
|
||||
// Everything about light
|
||||
<@include model/Light.slh@>
|
||||
<$declareLightBuffer()$>
|
||||
|
||||
<@include LightingModel.slh@>
|
||||
|
||||
<@include LightPoint.slh@>
|
||||
<$declareLightingPoint(supportScattering)$>
|
||||
|
||||
|
||||
uniform vec4 texcoordFrameTransform;
|
||||
|
||||
in vec4 _texCoord0;!>
|
||||
out vec4 _fragColor;
|
||||
|
||||
void main(void) {
|
||||
_fragColor = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
<!
|
||||
DeferredFrameTransform deferredTransform = getDeferredFrameTransform();
|
||||
|
||||
// Grab the fragment data from the uv
|
||||
vec2 texCoord = _texCoord0.st / _texCoord0.q;
|
||||
texCoord *= texcoordFrameTransform.zw;
|
||||
texCoord += texcoordFrameTransform.xy;
|
||||
|
||||
DeferredFragment frag = unpackDeferredFragment(deferredTransform, texCoord);
|
||||
|
||||
if (frag.mode == FRAG_MODE_UNLIT) {
|
||||
discard;
|
||||
}
|
||||
|
||||
// Need the light now
|
||||
Light light = getLight();
|
||||
|
||||
// Frag pos in world
|
||||
mat4 invViewMat = getViewInverse();
|
||||
vec4 fragPos = invViewMat * frag.position;
|
||||
|
||||
// Clip againgst the light volume and Make the Light vector going from fragment to light center in world space
|
||||
vec4 fragLightVecLen2;
|
||||
if (!lightVolume_clipFragToLightVolumePoint(light.volume, fragPos.xyz, fragLightVecLen2)) {
|
||||
discard;
|
||||
}
|
||||
|
||||
// Frag to eye vec
|
||||
vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0);
|
||||
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
|
||||
|
||||
|
||||
vec3 diffuse;
|
||||
vec3 specular;
|
||||
vec4 midNormalCurvature;
|
||||
vec4 lowNormalCurvature;
|
||||
if (frag.mode == FRAG_MODE_SCATTERING) {
|
||||
unpackMidLowNormalCurvature(texCoord, midNormalCurvature, lowNormalCurvature);
|
||||
}
|
||||
evalLightingPoint(diffuse, specular, light,
|
||||
fragLightVecLen2.xyz, fragEyeDir, frag.normal, frag.roughness,
|
||||
frag.metallic, frag.fresnel, frag.albedo, 1.0,
|
||||
frag.scattering, midNormalCurvature, lowNormalCurvature);
|
||||
|
||||
_fragColor.rgb += diffuse;
|
||||
_fragColor.rgb += specular;
|
||||
!>
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// spot_light.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Sam Gateau on 9/18/15.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// Everything about deferred buffer
|
||||
<!<@include DeferredBufferRead.slh@>
|
||||
|
||||
<$declareDeferredCurvature()$>
|
||||
|
||||
// Everything about light
|
||||
<@include model/Light.slh@>
|
||||
<$declareLightBuffer(256)$>
|
||||
uniform lightIndexBuffer {
|
||||
int lightIndex[256];
|
||||
};
|
||||
<@include LightingModel.slh@>
|
||||
|
||||
<@include LightPoint.slh@>
|
||||
<$declareLightingPoint(supportScattering)$>
|
||||
<@include LightSpot.slh@>
|
||||
<$declareLightingSpot(supportScattering)$>
|
||||
|
||||
//uniform vec4 texcoordFrameTransform;
|
||||
!>
|
||||
|
||||
|
||||
//in vec4 _texCoord0;
|
||||
//flat in int instanceID;
|
||||
out vec4 _fragColor;
|
||||
|
||||
void main(void) {
|
||||
_fragColor = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
// DeferredFrameTransform deferredTransform = getDeferredFrameTransform();
|
||||
|
||||
// Grab the fragment data from the uv
|
||||
//vec2 texCoord = _texCoord0.st;/* / _texCoord0.q;
|
||||
/*texCoord *= texcoordFrameTransform.zw;
|
||||
texCoord += texcoordFrameTransform.xy;*/
|
||||
/*
|
||||
vec4 fragPosition = unpackDeferredPositionFromZeye(texCoord);
|
||||
DeferredFragment frag = unpackDeferredFragmentNoPosition(texCoord);
|
||||
|
||||
if (frag.mode == FRAG_MODE_UNLIT) {
|
||||
discard;
|
||||
}
|
||||
|
||||
|
||||
// frag.depthVal = depthValue;
|
||||
frag.position = fragPosition;
|
||||
|
||||
vec4 midNormalCurvature;
|
||||
vec4 lowNormalCurvature;
|
||||
if (frag.mode == FRAG_MODE_SCATTERING) {
|
||||
unpackMidLowNormalCurvature(texCoord, midNormalCurvature, lowNormalCurvature);
|
||||
}
|
||||
|
||||
// Frag pos in world
|
||||
mat4 invViewMat = getViewInverse();
|
||||
vec4 fragPos = invViewMat * fragPosition;
|
||||
|
||||
// Frag to eye vec
|
||||
vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0);
|
||||
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
|
||||
|
||||
int numLights = lightIndex[0];
|
||||
for (int i = 0; i < numLights; i++) {
|
||||
// Need the light now
|
||||
Light light = getLight(lightIndex[i + 1]);
|
||||
bool isSpot = light_isSpot(light);
|
||||
// Clip againgst the light volume and Make the Light vector going from fragment to light center in world space
|
||||
vec4 fragLightVecLen2;
|
||||
vec4 fragLightDirLen;
|
||||
float cosSpotAngle;
|
||||
if (isSpot) {
|
||||
if (!clipFragToLightVolumeSpot(light, fragPos.xyz, fragLightVecLen2, fragLightDirLen, cosSpotAngle)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (!clipFragToLightVolumePoint(light, fragPos.xyz, fragLightVecLen2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
vec3 diffuse;
|
||||
vec3 specular;
|
||||
|
||||
if (isSpot) {
|
||||
evalLightingSpot(diffuse, specular, light,
|
||||
fragLightDirLen.xyzw, cosSpotAngle, fragEyeDir, frag.normal, frag.roughness,
|
||||
frag.metallic, frag.fresnel, frag.albedo, 1.0,
|
||||
frag.scattering, midNormalCurvature, lowNormalCurvature);
|
||||
} else {
|
||||
evalLightingPoint(diffuse, specular, light,
|
||||
fragLightVecLen2.xyz, fragEyeDir, frag.normal, frag.roughness,
|
||||
frag.metallic, frag.fresnel, frag.albedo, 1.0,
|
||||
frag.scattering, midNormalCurvature, lowNormalCurvature);
|
||||
}
|
||||
|
||||
_fragColor.rgb += diffuse;
|
||||
_fragColor.rgb += specular;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
23
libraries/render-utils/src/stencil_drawMask.slf
Normal file
23
libraries/render-utils/src/stencil_drawMask.slf
Normal file
|
@ -0,0 +1,23 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// stencil_drawMask.slf
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Sam Gateau on 5/31/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
|
||||
float aspectRatio = 0.95;
|
||||
|
||||
void main(void) {
|
||||
vec2 pos = varTexCoord0 * 2.0 - vec2(1.0);
|
||||
pos.x = aspectRatio * (pos.x * (pos.x > 0.0 ? 2.0 : -2.0) - 1.0);
|
||||
if (1.0 - dot(pos.xy, pos.xy) > 0.0 ) discard;
|
||||
}
|
|
@ -69,7 +69,7 @@ public:
|
|||
glm::vec3 size() const { return maximum - minimum; }
|
||||
float largestDimension() const { return glm::compMax(size()); }
|
||||
|
||||
/// \return new Extents which is original rotated around orign by rotation
|
||||
/// \return new Extents which is original rotated around origin by rotation
|
||||
Extents getRotated(const glm::quat& rotation) const {
|
||||
Extents temp(minimum, maximum);
|
||||
temp.rotate(rotation);
|
||||
|
|
|
@ -49,7 +49,7 @@ const mat4 Matrices::Z_180 { createMatFromQuatAndPos(Quaternions::Z_180, Vectors
|
|||
glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) {
|
||||
float cosa = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
|
||||
float ox = q2.x, oy = q2.y, oz = q2.z, ow = q2.w, s0, s1;
|
||||
|
||||
|
||||
// adjust signs if necessary
|
||||
if (cosa < 0.0f) {
|
||||
cosa = -cosa;
|
||||
|
@ -58,19 +58,19 @@ glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) {
|
|||
oz = -oz;
|
||||
ow = -ow;
|
||||
}
|
||||
|
||||
|
||||
// calculate coefficients; if the angle is too close to zero, we must fall back
|
||||
// to linear interpolation
|
||||
if ((1.0f - cosa) > EPSILON) {
|
||||
float angle = acosf(cosa), sina = sinf(angle);
|
||||
s0 = sinf((1.0f - proportion) * angle) / sina;
|
||||
s1 = sinf(proportion * angle) / sina;
|
||||
|
||||
|
||||
} else {
|
||||
s0 = 1.0f - proportion;
|
||||
s1 = proportion;
|
||||
}
|
||||
|
||||
|
||||
return glm::normalize(glm::quat(s0 * q1.w + s1 * ow, s0 * q1.x + s1 * ox, s0 * q1.y + s1 * oy, s0 * q1.z + s1 * oz));
|
||||
}
|
||||
|
||||
|
@ -105,10 +105,10 @@ int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm
|
|||
|
||||
int packFloatAngleToTwoByte(unsigned char* buffer, float degrees) {
|
||||
const float ANGLE_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 360.0f);
|
||||
|
||||
|
||||
uint16_t angleHolder = floorf((degrees + 180.0f) * ANGLE_CONVERSION_RATIO);
|
||||
memcpy(buffer, &angleHolder, sizeof(uint16_t));
|
||||
|
||||
|
||||
return sizeof(uint16_t);
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@ int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput
|
|||
quatParts[1] = floorf((quatNormalized.y + 1.0f) * QUAT_PART_CONVERSION_RATIO);
|
||||
quatParts[2] = floorf((quatNormalized.z + 1.0f) * QUAT_PART_CONVERSION_RATIO);
|
||||
quatParts[3] = floorf((quatNormalized.w + 1.0f) * QUAT_PART_CONVERSION_RATIO);
|
||||
|
||||
|
||||
memcpy(buffer, &quatParts, sizeof(quatParts));
|
||||
return sizeof(quatParts);
|
||||
}
|
||||
|
@ -133,12 +133,12 @@ int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput
|
|||
int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatOutput) {
|
||||
uint16_t quatParts[4];
|
||||
memcpy(&quatParts, buffer, sizeof(quatParts));
|
||||
|
||||
|
||||
quatOutput.x = ((quatParts[0] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
|
||||
quatOutput.y = ((quatParts[1] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
|
||||
quatOutput.z = ((quatParts[2] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
|
||||
quatOutput.w = ((quatParts[3] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
|
||||
|
||||
|
||||
return sizeof(quatParts);
|
||||
}
|
||||
|
||||
|
@ -235,7 +235,7 @@ glm::vec3 safeEulerAngles(const glm::quat& q) {
|
|||
atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)),
|
||||
asinf(sy),
|
||||
atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z)));
|
||||
|
||||
|
||||
} else {
|
||||
// not a unique solution; x + z = atan2(-m21, m11)
|
||||
eulers = glm::vec3(
|
||||
|
@ -250,7 +250,7 @@ glm::vec3 safeEulerAngles(const glm::quat& q) {
|
|||
PI_OVER_TWO,
|
||||
-atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)));
|
||||
}
|
||||
|
||||
|
||||
// adjust so that z, rather than y, is in [-pi/2, pi/2]
|
||||
if (eulers.z < -PI_OVER_TWO) {
|
||||
if (eulers.x < 0.0f) {
|
||||
|
@ -265,7 +265,7 @@ glm::vec3 safeEulerAngles(const glm::quat& q) {
|
|||
eulers.y -= PI;
|
||||
}
|
||||
eulers.z += PI;
|
||||
|
||||
|
||||
} else if (eulers.z > PI_OVER_TWO) {
|
||||
if (eulers.x < 0.0f) {
|
||||
eulers.x += PI;
|
||||
|
@ -320,7 +320,7 @@ glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) {
|
|||
for (int i = 0; i < 10; i++) {
|
||||
// store the results of the previous iteration
|
||||
glm::mat3 previous = upper;
|
||||
|
||||
|
||||
// compute average of the matrix with its inverse transpose
|
||||
float sd00 = previous[1][1] * previous[2][2] - previous[2][1] * previous[1][2];
|
||||
float sd10 = previous[0][1] * previous[2][2] - previous[2][1] * previous[0][2];
|
||||
|
@ -334,15 +334,15 @@ glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) {
|
|||
upper[0][0] = +sd00 * hrdet + previous[0][0] * 0.5f;
|
||||
upper[1][0] = -sd10 * hrdet + previous[1][0] * 0.5f;
|
||||
upper[2][0] = +sd20 * hrdet + previous[2][0] * 0.5f;
|
||||
|
||||
|
||||
upper[0][1] = -(previous[1][0] * previous[2][2] - previous[2][0] * previous[1][2]) * hrdet + previous[0][1] * 0.5f;
|
||||
upper[1][1] = +(previous[0][0] * previous[2][2] - previous[2][0] * previous[0][2]) * hrdet + previous[1][1] * 0.5f;
|
||||
upper[2][1] = -(previous[0][0] * previous[1][2] - previous[1][0] * previous[0][2]) * hrdet + previous[2][1] * 0.5f;
|
||||
|
||||
|
||||
upper[0][2] = +(previous[1][0] * previous[2][1] - previous[2][0] * previous[1][1]) * hrdet + previous[0][2] * 0.5f;
|
||||
upper[1][2] = -(previous[0][0] * previous[2][1] - previous[2][0] * previous[0][1]) * hrdet + previous[1][2] * 0.5f;
|
||||
upper[2][2] = +(previous[0][0] * previous[1][1] - previous[1][0] * previous[0][1]) * hrdet + previous[2][2] * 0.5f;
|
||||
|
||||
|
||||
// compute the difference; if it's small enough, we're done
|
||||
glm::mat3 diff = upper - previous;
|
||||
if (diff[0][0] * diff[0][0] + diff[1][0] * diff[1][0] + diff[2][0] * diff[2][0] + diff[0][1] * diff[0][1] +
|
||||
|
@ -352,7 +352,7 @@ glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// now that we have a nice orthogonal matrix, we can extract the rotation quaternion
|
||||
// using the method described in http://en.wikipedia.org/wiki/Rotation_matrix#Conversions
|
||||
float x2 = fabs(1.0f + upper[0][0] - upper[1][1] - upper[2][2]);
|
||||
|
@ -473,7 +473,7 @@ glm::mat4 createMatFromScaleQuatAndPos(const glm::vec3& scale, const glm::quat&
|
|||
glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f));
|
||||
}
|
||||
|
||||
// cancel out roll
|
||||
// cancel out roll
|
||||
glm::quat cancelOutRoll(const glm::quat& q) {
|
||||
glm::vec3 forward = q * Vectors::FRONT;
|
||||
return glm::quat_cast(glm::inverse(glm::lookAt(Vectors::ZERO, forward, Vectors::UP)));
|
||||
|
@ -538,17 +538,16 @@ void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& seconda
|
|||
uAxisOut = glm::normalize(primaryAxis);
|
||||
glm::vec3 normSecondary = glm::normalize(secondaryAxis);
|
||||
|
||||
// if secondaryAxis is parallel with the primaryAxis, pick another axis.
|
||||
// if normSecondary is parallel with the primaryAxis, pick another secondary.
|
||||
const float EPSILON = 1.0e-4f;
|
||||
if (fabsf(fabsf(glm::dot(uAxisOut, secondaryAxis)) - 1.0f) > EPSILON) {
|
||||
// pick a better secondaryAxis.
|
||||
normSecondary = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||
if (fabsf(fabsf(glm::dot(uAxisOut, secondaryAxis)) - 1.0f) > EPSILON) {
|
||||
normSecondary = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
if (fabsf(fabsf(glm::dot(uAxisOut, normSecondary)) - 1.0f) < EPSILON) {
|
||||
normSecondary = Vectors::UNIT_X;
|
||||
if (fabsf(fabsf(glm::dot(uAxisOut, normSecondary)) - 1.0f) < EPSILON) {
|
||||
normSecondary = Vectors::UNIT_Y;
|
||||
}
|
||||
}
|
||||
|
||||
wAxisOut = glm::normalize(glm::cross(uAxisOut, secondaryAxis));
|
||||
wAxisOut = glm::normalize(glm::cross(uAxisOut, normSecondary));
|
||||
vAxisOut = glm::cross(wAxisOut, uAxisOut);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,10 +50,13 @@ const int KILO_PER_MEGA = 1000;
|
|||
|
||||
#define KB_TO_BYTES_SHIFT 10
|
||||
#define MB_TO_BYTES_SHIFT 20
|
||||
#define GB_TO_BYTES_SHIFT 30
|
||||
|
||||
#define GB_TO_BYTES(X) ((size_t)(X) << GB_TO_BYTES_SHIFT)
|
||||
#define MB_TO_BYTES(X) ((size_t)(X) << MB_TO_BYTES_SHIFT)
|
||||
#define KB_TO_BYTES(X) ((size_t)(X) << KB_TO_BYTES_SHIFT)
|
||||
|
||||
#define BYTES_TO_GB(X) (X >> GB_TO_BYTES_SHIFT)
|
||||
#define BYTES_TO_MB(X) (X >> MB_TO_BYTES_SHIFT)
|
||||
#define BYTES_TO_KB(X) (X >> KB_TO_BYTES_SHIFT)
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ public:
|
|||
class RenderArgs {
|
||||
public:
|
||||
enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE };
|
||||
enum RenderSide { MONO, STEREO_LEFT, STEREO_RIGHT };
|
||||
enum DisplayMode { MONO, STEREO_MONITOR, STEREO_HMD };
|
||||
enum DebugFlags {
|
||||
RENDER_DEBUG_NONE = 0,
|
||||
RENDER_DEBUG_HULLS = 1
|
||||
|
@ -87,7 +87,7 @@ public:
|
|||
float sizeScale = 1.0f,
|
||||
int boundaryLevelAdjust = 0,
|
||||
RenderMode renderMode = DEFAULT_RENDER_MODE,
|
||||
RenderSide renderSide = MONO,
|
||||
DisplayMode displayMode = MONO,
|
||||
DebugFlags debugFlags = RENDER_DEBUG_NONE,
|
||||
gpu::Batch* batch = nullptr) :
|
||||
_context(context),
|
||||
|
@ -95,7 +95,7 @@ public:
|
|||
_sizeScale(sizeScale),
|
||||
_boundaryLevelAdjust(boundaryLevelAdjust),
|
||||
_renderMode(renderMode),
|
||||
_renderSide(renderSide),
|
||||
_displayMode(displayMode),
|
||||
_debugFlags(debugFlags),
|
||||
_batch(batch) {
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ public:
|
|||
float _sizeScale = 1.0f;
|
||||
int _boundaryLevelAdjust = 0;
|
||||
RenderMode _renderMode = DEFAULT_RENDER_MODE;
|
||||
RenderSide _renderSide = MONO;
|
||||
DisplayMode _displayMode = MONO;
|
||||
DebugFlags _debugFlags = RENDER_DEBUG_NONE;
|
||||
gpu::Batch* _batch = nullptr;
|
||||
|
||||
|
|
|
@ -13,44 +13,16 @@
|
|||
|
||||
#include <QFile>
|
||||
#include <QStandardPaths>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
|
||||
#include "NumericalConstants.h"
|
||||
#include "PathUtils.h"
|
||||
|
||||
RunningMarker::RunningMarker(QObject* parent, QString name) :
|
||||
_parent(parent),
|
||||
RunningMarker::RunningMarker(QString name) :
|
||||
_name(name)
|
||||
{
|
||||
}
|
||||
|
||||
void RunningMarker::startRunningMarker() {
|
||||
static const int RUNNING_STATE_CHECK_IN_MSECS = MSECS_PER_SECOND;
|
||||
|
||||
// start the nodeThread so its event loop is running
|
||||
_runningMarkerThread = new QThread(_parent);
|
||||
_runningMarkerThread->setObjectName("Running Marker Thread");
|
||||
_runningMarkerThread->start();
|
||||
|
||||
writeRunningMarkerFile(); // write the first file, even before timer
|
||||
|
||||
_runningMarkerTimer = new QTimer();
|
||||
QObject::connect(_runningMarkerTimer, &QTimer::timeout, [=](){
|
||||
writeRunningMarkerFile();
|
||||
});
|
||||
_runningMarkerTimer->start(RUNNING_STATE_CHECK_IN_MSECS);
|
||||
|
||||
// put the time on the thread
|
||||
_runningMarkerTimer->moveToThread(_runningMarkerThread);
|
||||
}
|
||||
|
||||
RunningMarker::~RunningMarker() {
|
||||
deleteRunningMarkerFile();
|
||||
QMetaObject::invokeMethod(_runningMarkerTimer, "stop", Qt::BlockingQueuedConnection);
|
||||
_runningMarkerThread->quit();
|
||||
_runningMarkerTimer->deleteLater();
|
||||
_runningMarkerThread->deleteLater();
|
||||
}
|
||||
|
||||
bool RunningMarker::fileExists() const {
|
||||
|
@ -77,8 +49,3 @@ void RunningMarker::deleteRunningMarkerFile() {
|
|||
QString RunningMarker::getFilePath() const {
|
||||
return QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + _name;
|
||||
}
|
||||
|
||||
QString RunningMarker::getMarkerFilePath(QString name) {
|
||||
return QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + name;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,21 +12,14 @@
|
|||
#ifndef hifi_RunningMarker_h
|
||||
#define hifi_RunningMarker_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
class QThread;
|
||||
class QTimer;
|
||||
|
||||
class RunningMarker {
|
||||
public:
|
||||
RunningMarker(QObject* parent, QString name);
|
||||
RunningMarker(QString name);
|
||||
~RunningMarker();
|
||||
|
||||
void startRunningMarker();
|
||||
|
||||
QString getFilePath() const;
|
||||
static QString getMarkerFilePath(QString name);
|
||||
|
||||
bool fileExists() const;
|
||||
|
||||
|
@ -34,10 +27,7 @@ public:
|
|||
void deleteRunningMarkerFile();
|
||||
|
||||
private:
|
||||
QObject* _parent { nullptr };
|
||||
QString _name;
|
||||
QThread* _runningMarkerThread { nullptr };
|
||||
QTimer* _runningMarkerTimer { nullptr };
|
||||
};
|
||||
|
||||
#endif // hifi_RunningMarker_h
|
||||
|
|
|
@ -50,11 +50,15 @@ static const char* RENDER_CONTROLLERS = "Render Hand Controllers";
|
|||
static const int MIN_PUCK_COUNT = 2;
|
||||
static const int MIN_FEET_AND_HIPS = 3;
|
||||
static const int MIN_FEET_HIPS_CHEST = 4;
|
||||
static const int MIN_FEET_HIPS_HEAD = 4;
|
||||
static const int MIN_FEET_HIPS_SHOULDERS = 5;
|
||||
static const int MIN_FEET_HIPS_CHEST_HEAD = 5;
|
||||
static const int FIRST_FOOT = 0;
|
||||
static const int SECOND_FOOT = 1;
|
||||
static const int HIP = 2;
|
||||
static const int CHEST = 3;
|
||||
static float HEAD_PUCK_Y_OFFSET = -0.0254f;
|
||||
static float HEAD_PUCK_Z_OFFSET = -0.152f;
|
||||
|
||||
const char* ViveControllerManager::NAME { "OpenVR" };
|
||||
|
||||
|
@ -80,6 +84,28 @@ static bool sortPucksXPosition(PuckPosePair firstPuck, PuckPosePair secondPuck)
|
|||
return (firstPuck.second.translation.x < secondPuck.second.translation.x);
|
||||
}
|
||||
|
||||
static bool determineFeetOrdering(const controller::Pose& poseA, const controller::Pose& poseB, glm::vec3 axis, glm::vec3 axisOrigin) {
|
||||
glm::vec3 poseAPosition = poseA.getTranslation();
|
||||
glm::vec3 poseBPosition = poseB.getTranslation();
|
||||
|
||||
glm::vec3 poseAVector = poseAPosition - axisOrigin;
|
||||
glm::vec3 poseBVector = poseBPosition - axisOrigin;
|
||||
|
||||
float poseAProjection = glm::dot(poseAVector, axis);
|
||||
float poseBProjection = glm::dot(poseBVector, axis);
|
||||
return (poseAProjection > poseBProjection);
|
||||
}
|
||||
|
||||
static glm::vec3 getReferenceHeadXAxis(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) {
|
||||
glm::mat4 finalHead = defaultToReferenceMat * defaultHead;
|
||||
return glmExtractRotation(finalHead) * Vectors::UNIT_X;
|
||||
}
|
||||
|
||||
static glm::vec3 getReferenceHeadPosition(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) {
|
||||
glm::mat4 finalHead = defaultToReferenceMat * defaultHead;
|
||||
return extractTranslation(finalHead);
|
||||
}
|
||||
|
||||
static QString deviceTrackingResultToString(vr::ETrackingResult trackingResult) {
|
||||
QString result;
|
||||
auto iterator = TRACKING_RESULT_TO_STRING.find(trackingResult);
|
||||
|
@ -178,6 +204,8 @@ ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : contro
|
|||
_configStringMap[Config::FeetAndHips] = QString("FeetAndHips");
|
||||
_configStringMap[Config::FeetHipsAndChest] = QString("FeetHipsAndChest");
|
||||
_configStringMap[Config::FeetHipsAndShoulders] = QString("FeetHipsAndShoulders");
|
||||
_configStringMap[Config::FeetHipsChestAndHead] = QString("FeetHipsChestAndHead");
|
||||
_configStringMap[Config::FeetHipsAndHead] = QString("FeetHipsAndHead");
|
||||
|
||||
if (openVrSupported()) {
|
||||
createPreferences();
|
||||
|
@ -358,6 +386,23 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr
|
|||
int firstShoulderIndex = 3;
|
||||
int secondShoulderIndex = 4;
|
||||
calibrateShoulders(defaultToReferenceMat, inputCalibration, firstShoulderIndex, secondShoulderIndex);
|
||||
} else if (_config == Config::FeetHipsAndHead && puckCount == MIN_FEET_HIPS_HEAD) {
|
||||
glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration);
|
||||
glm::vec3 headXAxis = getReferenceHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
|
||||
glm::vec3 headPosition = getReferenceHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
|
||||
calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition);
|
||||
calibrateHips(headPuckDefaultToReferenceMat, inputCalibration);
|
||||
calibrateHead(headPuckDefaultToReferenceMat, inputCalibration);
|
||||
_overrideHead = true;
|
||||
} else if (_config == Config::FeetHipsChestAndHead && puckCount == MIN_FEET_HIPS_CHEST_HEAD) {
|
||||
glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration);
|
||||
glm::vec3 headXAxis = getReferenceHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
|
||||
glm::vec3 headPosition = getReferenceHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
|
||||
calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition);
|
||||
calibrateHips(headPuckDefaultToReferenceMat, inputCalibration);
|
||||
calibrateChest(headPuckDefaultToReferenceMat, inputCalibration);
|
||||
calibrateHead(headPuckDefaultToReferenceMat, inputCalibration);
|
||||
_overrideHead = true;
|
||||
} else {
|
||||
qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks";
|
||||
uncalibrate();
|
||||
|
@ -372,6 +417,7 @@ void ViveControllerManager::InputDevice::uncalibrate() {
|
|||
_pucksOffset.clear();
|
||||
_jointToPuckMap.clear();
|
||||
_calibrated = false;
|
||||
_overrideHead = false;
|
||||
}
|
||||
|
||||
void ViveControllerManager::InputDevice::updateCalibratedLimbs() {
|
||||
|
@ -381,6 +427,10 @@ void ViveControllerManager::InputDevice::updateCalibratedLimbs() {
|
|||
_poseStateMap[controller::SPINE2] = addOffsetToPuckPose(controller::SPINE2);
|
||||
_poseStateMap[controller::RIGHT_ARM] = addOffsetToPuckPose(controller::RIGHT_ARM);
|
||||
_poseStateMap[controller::LEFT_ARM] = addOffsetToPuckPose(controller::LEFT_ARM);
|
||||
|
||||
if (_overrideHead) {
|
||||
_poseStateMap[controller::HEAD] = addOffsetToPuckPose(controller::HEAD);
|
||||
}
|
||||
}
|
||||
|
||||
controller::Pose ViveControllerManager::InputDevice::addOffsetToPuckPose(int joint) const {
|
||||
|
@ -446,6 +496,43 @@ void ViveControllerManager::InputDevice::handleHandController(float deltaTime, u
|
|||
}
|
||||
}
|
||||
|
||||
glm::mat4 ViveControllerManager::InputDevice::recalculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration) {
|
||||
glm::mat4 avatarToSensorMat = glm::inverse(inputCalibration.sensorToWorldMat) * inputCalibration.avatarMat;
|
||||
glm::mat4 sensorToAvatarMat = glm::inverse(inputCalibration.avatarMat) * inputCalibration.sensorToWorldMat;
|
||||
size_t headPuckIndex = _validTrackedObjects.size() - 1;
|
||||
controller::Pose headPuckPose = _validTrackedObjects[headPuckIndex].second;
|
||||
glm::mat4 headPuckAvatarMat = createMatFromQuatAndPos(headPuckPose.getRotation(), headPuckPose.getTranslation()) * Matrices::Y_180;
|
||||
glm::vec3 headPuckTranslation = extractTranslation(headPuckAvatarMat);
|
||||
glm::vec3 headPuckZAxis = cancelOutRollAndPitch(glmExtractRotation(headPuckAvatarMat)) * glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
|
||||
// check that the head puck z axis is not parrallel to the world up
|
||||
const float EPSILON = 1.0e-4f;
|
||||
glm::vec3 zAxis = glmExtractRotation(headPuckAvatarMat) * glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
if (fabsf(fabsf(glm::dot(glm::normalize(worldUp), glm::normalize(zAxis))) - 1.0f) < EPSILON) {
|
||||
headPuckZAxis = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
glm::vec3 yPrime = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 xPrime = glm::normalize(glm::cross(worldUp, headPuckZAxis));
|
||||
glm::vec3 zPrime = glm::normalize(glm::cross(xPrime, yPrime));
|
||||
glm::mat4 newHeadPuck = glm::mat4(glm::vec4(xPrime, 0.0f), glm::vec4(yPrime, 0.0f),
|
||||
glm::vec4(zPrime, 0.0f), glm::vec4(headPuckTranslation, 1.0f));
|
||||
|
||||
glm::mat4 headPuckOffset = glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f),
|
||||
glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), glm::vec4(0.0f, HEAD_PUCK_Y_OFFSET, HEAD_PUCK_Z_OFFSET, 1.0f));
|
||||
|
||||
glm::mat4 finalHeadPuck = newHeadPuck * headPuckOffset;
|
||||
|
||||
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat;
|
||||
|
||||
glm::mat4 currentHead = finalHeadPuck * defaultHeadOffset;
|
||||
|
||||
// calculate the defaultToRefrenceXform
|
||||
glm::mat4 defaultToReferenceMat = currentHead * glm::inverse(inputCalibration.defaultHeadMat);
|
||||
return defaultToReferenceMat;
|
||||
}
|
||||
|
||||
void ViveControllerManager::InputDevice::partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPseudoButton, int xPseudoButton, int yPseudoButton) {
|
||||
// Populate the L/RS_CENTER/OUTER pseudo buttons, corresponding to a partition of the L/RS space based on the X/Y values.
|
||||
const float CENTER_DEADBAND = 0.6f;
|
||||
|
@ -633,6 +720,26 @@ void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToRefer
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, glm::vec3 headXAxis, glm::vec3 headPosition) {
|
||||
auto& firstFoot = _validTrackedObjects[FIRST_FOOT];
|
||||
auto& secondFoot = _validTrackedObjects[SECOND_FOOT];
|
||||
controller::Pose& firstFootPose = firstFoot.second;
|
||||
controller::Pose& secondFootPose = secondFoot.second;
|
||||
|
||||
if (determineFeetOrdering(firstFootPose, secondFootPose, headXAxis, headPosition)) {
|
||||
_jointToPuckMap[controller::LEFT_FOOT] = firstFoot.first;
|
||||
_pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, firstFootPose);
|
||||
_jointToPuckMap[controller::RIGHT_FOOT] = secondFoot.first;
|
||||
_pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, secondFootPose);
|
||||
} else {
|
||||
_jointToPuckMap[controller::LEFT_FOOT] = secondFoot.first;
|
||||
_pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, secondFootPose);
|
||||
_jointToPuckMap[controller::RIGHT_FOOT] = firstFoot.first;
|
||||
_pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, firstFootPose);
|
||||
}
|
||||
}
|
||||
|
||||
void ViveControllerManager::InputDevice::calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
|
||||
_jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first;
|
||||
_pucksOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second);
|
||||
|
@ -661,7 +768,19 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo
|
|||
_jointToPuckMap[controller::RIGHT_ARM] = firstShoulder.first;
|
||||
_pucksOffset[firstShoulder.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightArm, firstShoulder.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
|
||||
size_t headIndex = _validTrackedObjects.size() - 1;
|
||||
const PuckPosePair& head = _validTrackedObjects[headIndex];
|
||||
|
||||
// assume the person is wearing the head puck on his/her forehead
|
||||
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat;
|
||||
controller::Pose newHead = head.second.postTransform(defaultHeadOffset);
|
||||
|
||||
_jointToPuckMap[controller::HEAD] = head.first;
|
||||
_pucksOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, newHead);
|
||||
}
|
||||
|
||||
|
||||
void ViveControllerManager::InputDevice::loadSettings() {
|
||||
|
@ -697,6 +816,10 @@ void ViveControllerManager::InputDevice::setConfigFromString(const QString& valu
|
|||
_preferedConfig = Config::FeetHipsAndChest;
|
||||
} else if (value == "FeetHipsAndShoulders") {
|
||||
_preferedConfig = Config::FeetHipsAndShoulders;
|
||||
} else if (value == "FeetHipsChestAndHead") {
|
||||
_preferedConfig = Config::FeetHipsChestAndHead;
|
||||
} else if (value == "FeetHipsAndHead") {
|
||||
_preferedConfig = Config::FeetHipsAndHead;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -705,11 +828,43 @@ void ViveControllerManager::InputDevice::createPreferences() {
|
|||
auto preferences = DependencyManager::get<Preferences>();
|
||||
static const QString VIVE_PUCKS_CONFIG = "Vive Pucks Configuration";
|
||||
|
||||
{
|
||||
static const float MIN_VALUE = -3.0f;
|
||||
static const float MAX_VALUE = 3.0f;
|
||||
static const float STEP = 0.01f;
|
||||
|
||||
auto getter = [this]()->float { return HEAD_PUCK_Y_OFFSET; };
|
||||
auto setter = [this](const float& value) { HEAD_PUCK_Y_OFFSET = value; };
|
||||
|
||||
auto preference = new SpinnerPreference(VIVE_PUCKS_CONFIG, "HeadPuckYOffset", getter, setter);
|
||||
preference->setMin(MIN_VALUE);
|
||||
preference->setMax(MAX_VALUE);
|
||||
preference->setDecimals(3);
|
||||
preference->setStep(STEP);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
{
|
||||
static const float MIN_VALUE = -3.0f;
|
||||
static const float MAX_VALUE = 3.0f;
|
||||
static const float STEP = 0.01f;
|
||||
|
||||
auto getter = [this]()->float { return HEAD_PUCK_Z_OFFSET; };
|
||||
auto setter = [this](const float& value) { HEAD_PUCK_Z_OFFSET = value; };
|
||||
|
||||
auto preference = new SpinnerPreference(VIVE_PUCKS_CONFIG, "HeadPuckXOffset", getter, setter);
|
||||
preference->setMin(MIN_VALUE);
|
||||
preference->setMax(MAX_VALUE);
|
||||
preference->setStep(STEP);
|
||||
preference->setDecimals(3);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = [this]()->QString { return _configStringMap[_preferedConfig]; };
|
||||
auto setter = [this](const QString& value) { setConfigFromString(value); saveSettings(); };
|
||||
auto preference = new ComboBoxPreference(VIVE_PUCKS_CONFIG, "Configuration", getter, setter);
|
||||
QStringList list = {"Auto", "Feet", "FeetAndHips", "FeetHipsAndChest", "FeetHipsAndShoulders"};
|
||||
QStringList list = {"Auto", "Feet", "FeetAndHips", "FeetHipsAndChest", "FeetHipsAndShoulders", "FeetHipsAndHead"};
|
||||
preference->setItems(list);
|
||||
preferences->addPreference(preference);
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ private:
|
|||
void calibrate(const controller::InputCalibrationData& inputCalibration);
|
||||
void uncalibrate();
|
||||
controller::Pose addOffsetToPuckPose(int joint) const;
|
||||
glm::mat4 recalculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration);
|
||||
void updateCalibratedLimbs();
|
||||
bool checkForCalibrationEvent();
|
||||
void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand);
|
||||
|
@ -86,11 +87,14 @@ private:
|
|||
void loadSettings();
|
||||
void saveSettings() const;
|
||||
void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
|
||||
void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, glm::vec3 headXAxis, glm::vec3 headPosition);
|
||||
void calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
|
||||
void calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
|
||||
|
||||
void calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration,
|
||||
int firstShoulderIndex, int secondShoulderIndex);
|
||||
void calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
|
||||
|
||||
|
||||
class FilteredStick {
|
||||
public:
|
||||
|
@ -121,6 +125,8 @@ private:
|
|||
FeetAndHips,
|
||||
FeetHipsAndChest,
|
||||
FeetHipsAndShoulders,
|
||||
FeetHipsChestAndHead,
|
||||
FeetHipsAndHead
|
||||
};
|
||||
Config _config { Config::Auto };
|
||||
Config _preferedConfig { Config::Auto };
|
||||
|
@ -148,6 +154,7 @@ private:
|
|||
bool _calibrated { false };
|
||||
bool _timeTilCalibrationSet { false };
|
||||
bool _calibrate { false };
|
||||
bool _overrideHead { false };
|
||||
mutable std::recursive_mutex _lock;
|
||||
|
||||
QString configToString(Config config);
|
||||
|
|
Binary file not shown.
|
@ -10,7 +10,7 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
/* global Script, Users, Overlays, AvatarList, Controller, Camera, getControllerWorldLocation */
|
||||
/* global Script, Users, Overlays, AvatarList, Controller, Camera, getControllerWorldLocation, UserActivityLogger */
|
||||
|
||||
(function () { // BEGIN LOCAL_SCOPE
|
||||
var button;
|
||||
|
@ -76,6 +76,7 @@
|
|||
// Called from the C++ scripting interface to show the bubble overlay
|
||||
function enteredIgnoreRadius() {
|
||||
createOverlays();
|
||||
UserActivityLogger.bubbleActivated();
|
||||
}
|
||||
|
||||
// Used to set the state of the bubble HUD button
|
||||
|
@ -139,10 +140,14 @@
|
|||
}
|
||||
|
||||
// When the space bubble is toggled...
|
||||
function onBubbleToggled() {
|
||||
var bubbleActive = Users.getIgnoreRadiusEnabled();
|
||||
writeButtonProperties(bubbleActive);
|
||||
if (bubbleActive) {
|
||||
// NOTE: the c++ calls this with just the first param -- we added a second
|
||||
// just for not logging the initial state of the bubble when we startup.
|
||||
function onBubbleToggled(enabled, doNotLog) {
|
||||
writeButtonProperties(enabled);
|
||||
if (doNotLog !== true) {
|
||||
UserActivityLogger.bubbleToggled(enabled);
|
||||
}
|
||||
if (enabled) {
|
||||
createOverlays();
|
||||
} else {
|
||||
hideOverlays();
|
||||
|
@ -163,7 +168,7 @@
|
|||
sortOrder: 4
|
||||
});
|
||||
|
||||
onBubbleToggled();
|
||||
onBubbleToggled(Users.getIgnoreRadiusEnabled(), true); // pass in true so we don't log this initial one in the UserActivity table
|
||||
|
||||
button.clicked.connect(Users.toggleIgnoreRadius);
|
||||
Users.ignoreRadiusEnabledChanged.connect(onBubbleToggled);
|
||||
|
|
|
@ -217,6 +217,32 @@ function hideMarketplace() {
|
|||
// }
|
||||
// }
|
||||
|
||||
function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) {
|
||||
// Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original
|
||||
// position in the given direction.
|
||||
var CORNERS = [
|
||||
{ x: 0, y: 0, z: 0 },
|
||||
{ x: 0, y: 0, z: 1 },
|
||||
{ x: 0, y: 1, z: 0 },
|
||||
{ x: 0, y: 1, z: 1 },
|
||||
{ x: 1, y: 0, z: 0 },
|
||||
{ x: 1, y: 0, z: 1 },
|
||||
{ x: 1, y: 1, z: 0 },
|
||||
{ x: 1, y: 1, z: 1 },
|
||||
];
|
||||
|
||||
// Go through all corners and find least (most negative) distance in front of position.
|
||||
var distance = 0;
|
||||
for (var i = 0, length = CORNERS.length; i < length; i++) {
|
||||
var cornerVector =
|
||||
Vec3.multiplyQbyV(orientation, Vec3.multiplyVbyV(Vec3.subtract(CORNERS[i], registration), dimensions));
|
||||
var cornerDistance = Vec3.dot(cornerVector, direction);
|
||||
distance = Math.min(cornerDistance, distance);
|
||||
}
|
||||
position = Vec3.sum(Vec3.multiply(distance, direction), position);
|
||||
return position;
|
||||
}
|
||||
|
||||
var TOOLS_PATH = Script.resolvePath("assets/images/tools/");
|
||||
var GRABBABLE_ENTITIES_MENU_CATEGORY = "Edit";
|
||||
var GRABBABLE_ENTITIES_MENU_ITEM = "Create Entities As Grabbable";
|
||||
|
@ -234,6 +260,32 @@ var toolBar = (function () {
|
|||
var position = getPositionToCreateEntity();
|
||||
var entityID = null;
|
||||
if (position !== null && position !== undefined) {
|
||||
var direction;
|
||||
if (Camera.mode === "entity" || Camera.mode === "independent") {
|
||||
direction = Camera.orientation;
|
||||
} else {
|
||||
direction = MyAvatar.orientation;
|
||||
}
|
||||
direction = Vec3.multiplyQbyV(direction, Vec3.UNIT_Z);
|
||||
|
||||
var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Web"];
|
||||
if (PRE_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) {
|
||||
// Adjust position of entity per bounding box prior to creating it.
|
||||
var registration = properties.registration;
|
||||
if (registration === undefined) {
|
||||
var DEFAULT_REGISTRATION = { x: 0.5, y: 0.5, z: 0.5 };
|
||||
registration = DEFAULT_REGISTRATION;
|
||||
}
|
||||
|
||||
var orientation = properties.orientation;
|
||||
if (orientation === undefined) {
|
||||
var DEFAULT_ORIENTATION = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
||||
orientation = DEFAULT_ORIENTATION;
|
||||
}
|
||||
|
||||
position = adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation);
|
||||
}
|
||||
|
||||
position = grid.snapToSurface(grid.snapToGrid(position, false, dimensions), dimensions);
|
||||
properties.position = position;
|
||||
if (Menu.isOptionChecked(GRABBABLE_ENTITIES_MENU_ITEM)) {
|
||||
|
@ -243,6 +295,32 @@ var toolBar = (function () {
|
|||
if (properties.type == "ParticleEffect") {
|
||||
selectParticleEntity(entityID);
|
||||
}
|
||||
|
||||
var POST_ADJUST_ENTITY_TYPES = ["Model"];
|
||||
if (POST_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) {
|
||||
// Adjust position of entity per bounding box after it has been created and auto-resized.
|
||||
var initialDimensions = Entities.getEntityProperties(entityID, ["dimensions"]).dimensions;
|
||||
var DIMENSIONS_CHECK_INTERVAL = 200;
|
||||
var MAX_DIMENSIONS_CHECKS = 10;
|
||||
var dimensionsCheckCount = 0;
|
||||
var dimensionsCheckFunction = function () {
|
||||
dimensionsCheckCount++;
|
||||
var properties = Entities.getEntityProperties(entityID, ["dimensions", "registrationPoint", "rotation"]);
|
||||
if (!Vec3.equal(properties.dimensions, initialDimensions)) {
|
||||
position = adjustPositionPerBoundingBox(position, direction, properties.registrationPoint,
|
||||
properties.dimensions, properties.rotation);
|
||||
position = grid.snapToSurface(grid.snapToGrid(position, false, properties.dimensions),
|
||||
properties.dimensions);
|
||||
Entities.editEntity(entityID, {
|
||||
position: position
|
||||
});
|
||||
selectionManager._update();
|
||||
} else if (dimensionsCheckCount < MAX_DIMENSIONS_CHECKS) {
|
||||
Script.setTimeout(dimensionsCheckFunction, DIMENSIONS_CHECK_INTERVAL);
|
||||
}
|
||||
};
|
||||
Script.setTimeout(dimensionsCheckFunction, DIMENSIONS_CHECK_INTERVAL);
|
||||
}
|
||||
} else {
|
||||
Window.notifyEditError("Can't create " + properties.type + ": " +
|
||||
properties.type + " would be out of bounds.");
|
||||
|
@ -564,6 +642,8 @@ var toolBar = (function () {
|
|||
enabled: active
|
||||
}));
|
||||
isActive = active;
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
|
||||
if (!isActive) {
|
||||
entityListTool.setVisible(false);
|
||||
gridTool.setVisible(false);
|
||||
|
@ -572,8 +652,8 @@ var toolBar = (function () {
|
|||
selectionManager.clearSelections();
|
||||
cameraManager.disable();
|
||||
selectionDisplay.triggerMapping.disable();
|
||||
tablet.landscape = false;
|
||||
} else {
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
tablet.loadQMLSource("Edit.qml");
|
||||
UserActivityLogger.enabledEdit();
|
||||
entityListTool.setVisible(true);
|
||||
|
@ -581,6 +661,8 @@ var toolBar = (function () {
|
|||
grid.setEnabled(true);
|
||||
propertiesTool.setVisible(true);
|
||||
selectionDisplay.triggerMapping.enable();
|
||||
print("starting tablet in landscape mode")
|
||||
tablet.landscape = true;
|
||||
// Not sure what the following was meant to accomplish, but it currently causes
|
||||
// everybody else to think that Interface has lost focus overall. fogbugzid:558
|
||||
// Window.setFocus();
|
||||
|
@ -1407,40 +1489,26 @@ function handeMenuEvent(menuItem) {
|
|||
}
|
||||
tooltip.show(false);
|
||||
}
|
||||
function getPositionToCreateEntity() {
|
||||
var HALF_TREE_SCALE = 16384;
|
||||
var direction = Quat.getForward(MyAvatar.orientation);
|
||||
var distance = 1;
|
||||
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(direction, distance));
|
||||
|
||||
var HALF_TREE_SCALE = 16384;
|
||||
|
||||
function getPositionToCreateEntity(extra) {
|
||||
var CREATE_DISTANCE = 2;
|
||||
var position;
|
||||
var delta = extra !== undefined ? extra : 0;
|
||||
if (Camera.mode === "entity" || Camera.mode === "independent") {
|
||||
position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), distance));
|
||||
position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), CREATE_DISTANCE + delta));
|
||||
} else {
|
||||
position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getForward(MyAvatar.orientation), CREATE_DISTANCE + delta));
|
||||
position.y += 0.5;
|
||||
}
|
||||
position.y += 0.5;
|
||||
|
||||
if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) {
|
||||
return null;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
function getPositionToImportEntity() {
|
||||
var dimensions = Clipboard.getContentsDimensions();
|
||||
var HALF_TREE_SCALE = 16384;
|
||||
var direction = Quat.getForward(MyAvatar.orientation);
|
||||
var longest = 1;
|
||||
longest = Math.sqrt(Math.pow(dimensions.x, 2) + Math.pow(dimensions.z, 2));
|
||||
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(direction, longest));
|
||||
|
||||
if (Camera.mode === "entity" || Camera.mode === "independent") {
|
||||
position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), longest));
|
||||
}
|
||||
|
||||
if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
function importSVO(importURL) {
|
||||
if (!Entities.canRez() && !Entities.canRezTmp()) {
|
||||
Window.notifyEditError(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG);
|
||||
|
@ -1458,22 +1526,73 @@ function importSVO(importURL) {
|
|||
|
||||
if (success) {
|
||||
var VERY_LARGE = 10000;
|
||||
var position = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
if (Clipboard.getClipboardContentsLargestDimension() < VERY_LARGE) {
|
||||
position = getPositionToImportEntity();
|
||||
var isLargeImport = Clipboard.getClipboardContentsLargestDimension() >= VERY_LARGE;
|
||||
var position = Vec3.ZERO;
|
||||
if (!isLargeImport) {
|
||||
position = getPositionToCreateEntity(Clipboard.getClipboardContentsLargestDimension() / 2);
|
||||
}
|
||||
if (position !== null && position !== undefined) {
|
||||
var pastedEntityIDs = Clipboard.pasteEntities(position);
|
||||
if (!isLargeImport) {
|
||||
// The first entity in Clipboard gets the specified position with the rest being relative to it. Therefore, move
|
||||
// entities after they're imported so that they're all the correct distance in front of and with geometric mean
|
||||
// centered on the avatar/camera direction.
|
||||
var deltaPosition = Vec3.ZERO;
|
||||
|
||||
var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]);
|
||||
var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"];
|
||||
if (NO_ADJUST_ENTITY_TYPES.indexOf(properties.type) === -1) {
|
||||
var targetDirection;
|
||||
if (Camera.mode === "entity" || Camera.mode === "independent") {
|
||||
targetDirection = Camera.orientation;
|
||||
} else {
|
||||
targetDirection = MyAvatar.orientation;
|
||||
}
|
||||
targetDirection = Vec3.multiplyQbyV(targetDirection, Vec3.UNIT_Z);
|
||||
|
||||
var targetPosition = getPositionToCreateEntity();
|
||||
var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection.
|
||||
var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection.
|
||||
var entityPositions = [];
|
||||
for (var i = 0, length = pastedEntityIDs.length; i < length; i++) {
|
||||
var properties = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions",
|
||||
"registrationPoint", "rotation"]);
|
||||
var adjustedPosition = adjustPositionPerBoundingBox(targetPosition, targetDirection,
|
||||
properties.registrationPoint, properties.dimensions, properties.rotation);
|
||||
var delta = Vec3.subtract(adjustedPosition, properties.position);
|
||||
var distance = Vec3.dot(delta, targetDirection);
|
||||
deltaParallel = Math.min(distance, deltaParallel);
|
||||
deltaPerpendicular = Vec3.sum(Vec3.subtract(delta, Vec3.multiply(distance, targetDirection)),
|
||||
deltaPerpendicular);
|
||||
entityPositions[i] = properties.position;
|
||||
}
|
||||
deltaPerpendicular = Vec3.multiply(1 / pastedEntityIDs.length, deltaPerpendicular);
|
||||
deltaPosition = Vec3.sum(Vec3.multiply(deltaParallel, targetDirection), deltaPerpendicular);
|
||||
}
|
||||
|
||||
if (grid.getSnapToGrid()) {
|
||||
var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["position", "dimensions",
|
||||
"registrationPoint"]);
|
||||
var position = Vec3.sum(deltaPosition, properties.position);
|
||||
position = grid.snapToSurface(grid.snapToGrid(position, false, properties.dimensions,
|
||||
properties.registrationPoint), properties.dimensions, properties.registrationPoint);
|
||||
deltaPosition = Vec3.subtract(position, properties.position);
|
||||
}
|
||||
|
||||
if (!Vec3.equal(deltaPosition, Vec3.ZERO)) {
|
||||
for (var i = 0, length = pastedEntityIDs.length; i < length; i++) {
|
||||
Entities.editEntity(pastedEntityIDs[i], {
|
||||
position: Vec3.sum(deltaPosition, entityPositions[i])
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isActive) {
|
||||
selectionManager.setSelections(pastedEntityIDs);
|
||||
}
|
||||
} else {
|
||||
Window.notifyEditError("Can't import objects: objects would be out of bounds.");
|
||||
Window.notifyEditError("Can't import entities: entities would be out of bounds.");
|
||||
}
|
||||
} else {
|
||||
Window.notifyEditError("There was an error importing the entity file.");
|
||||
|
@ -2100,7 +2219,13 @@ function selectParticleEntity(entityID) {
|
|||
}
|
||||
|
||||
entityListTool.webView.webEventReceived.connect(function (data) {
|
||||
data = JSON.parse(data);
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {
|
||||
print("edit.js: Error parsing JSON: " + e.name + " data " + data)
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.type === 'parent') {
|
||||
parentSelectedEntities();
|
||||
} else if(data.type === 'unparent') {
|
||||
|
|
|
@ -20,7 +20,8 @@ var ICON_FOR_TYPE = {
|
|||
Light: "p",
|
||||
Zone: "o",
|
||||
PolyVox: "",
|
||||
Multiple: ""
|
||||
Multiple: "",
|
||||
PolyLine: ""
|
||||
}
|
||||
|
||||
var EDITOR_TIMEOUT_DURATION = 1500;
|
||||
|
|
|
@ -22,7 +22,9 @@ var DEFAULT_WIDTH = 0.4375;
|
|||
var DEFAULT_VERTICAL_FIELD_OF_VIEW = 45; // degrees
|
||||
var SENSOR_TO_ROOM_MATRIX = -2;
|
||||
var CAMERA_MATRIX = -7;
|
||||
var ROT_Y_180 = {x: 0, y: 1, z: 0, w: 0};
|
||||
var ROT_Y_180 = {x: 0.0, y: 1.0, z: 0, w: 0};
|
||||
var ROT_LANDSCAPE = {x: 1.0, y: 1.0, z: 0, w: 0};
|
||||
var ROT_LANDSCAPE_WINDOW = {x: 0.0, y: 0.0, z: 0.0, w: 0};
|
||||
var ROT_IDENT = {x: 0, y: 0, z: 0, w: 1};
|
||||
var TABLET_TEXTURE_RESOLUTION = { x: 480, y: 706 };
|
||||
var INCHES_TO_METERS = 1 / 39.3701;
|
||||
|
@ -243,29 +245,29 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) {
|
|||
};
|
||||
|
||||
WebTablet.prototype.getDimensions = function() {
|
||||
if (this.landscape) {
|
||||
return { x: this.width * 2, y: this.height, z: this.depth };
|
||||
} else {
|
||||
return { x: this.width, y: this.height, z: this.depth };
|
||||
}
|
||||
return { x: this.width, y: this.height, z: this.depth };
|
||||
};
|
||||
|
||||
WebTablet.prototype.getTabletTextureResolution = function() {
|
||||
if (this.landscape) {
|
||||
return { x: TABLET_TEXTURE_RESOLUTION.x * 2, y: TABLET_TEXTURE_RESOLUTION.y };
|
||||
return { x: TABLET_TEXTURE_RESOLUTION.y , y: TABLET_TEXTURE_RESOLUTION.x };
|
||||
} else {
|
||||
return TABLET_TEXTURE_RESOLUTION;
|
||||
}
|
||||
};
|
||||
|
||||
WebTablet.prototype.setLandscape = function(newLandscapeValue) {
|
||||
if (this.landscape == newLandscapeValue) {
|
||||
if (this.landscape === newLandscapeValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.landscape = newLandscapeValue;
|
||||
Overlays.editOverlay(this.tabletEntityID, { dimensions: this.getDimensions() });
|
||||
Overlays.editOverlay(this.tabletEntityID,
|
||||
{ rotation: this.landscape ? Quat.multiply(Camera.orientation, ROT_LANDSCAPE) :
|
||||
Quat.multiply(Camera.orientation, ROT_Y_180) });
|
||||
Overlays.editOverlay(this.webOverlayID, {
|
||||
resolution: this.getTabletTextureResolution()
|
||||
resolution: this.getTabletTextureResolution(),
|
||||
rotation: Quat.multiply(Camera.orientation, ROT_LANDSCAPE_WINDOW)
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -407,7 +409,7 @@ WebTablet.prototype.calculateWorldAttitudeRelativeToCamera = function (windowPos
|
|||
|
||||
return {
|
||||
position: worldMousePosition,
|
||||
rotation: Quat.multiply(Camera.orientation, ROT_Y_180)
|
||||
rotation: this.landscape ? Quat.multiply(Camera.orientation, ROT_LANDSCAPE) : Quat.multiply(Camera.orientation, ROT_Y_180)
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -109,7 +109,13 @@ EntityListTool = function(opts) {
|
|||
};
|
||||
|
||||
webView.webEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {
|
||||
print("entityList.js: Error parsing JSON: " + e.name + " data " + data)
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.type == "selectionUpdate") {
|
||||
var ids = data.entityIds;
|
||||
var entityIDs = [];
|
||||
|
|
|
@ -79,7 +79,7 @@ Grid = function(opts) {
|
|||
}
|
||||
}
|
||||
|
||||
that.snapToSurface = function(position, dimensions) {
|
||||
that.snapToSurface = function(position, dimensions, registration) {
|
||||
if (!snapToGrid) {
|
||||
return position;
|
||||
}
|
||||
|
@ -88,14 +88,18 @@ Grid = function(opts) {
|
|||
dimensions = { x: 0, y: 0, z: 0 };
|
||||
}
|
||||
|
||||
if (registration === undefined) {
|
||||
registration = { x: 0.5, y: 0.5, z: 0.5 };
|
||||
}
|
||||
|
||||
return {
|
||||
x: position.x,
|
||||
y: origin.y + (dimensions.y / 2),
|
||||
y: origin.y + (registration.y * dimensions.y),
|
||||
z: position.z
|
||||
};
|
||||
}
|
||||
|
||||
that.snapToGrid = function(position, majorOnly, dimensions) {
|
||||
that.snapToGrid = function(position, majorOnly, dimensions, registration) {
|
||||
if (!snapToGrid) {
|
||||
return position;
|
||||
}
|
||||
|
@ -104,6 +108,10 @@ Grid = function(opts) {
|
|||
dimensions = { x: 0, y: 0, z: 0 };
|
||||
}
|
||||
|
||||
if (registration === undefined) {
|
||||
registration = { x: 0.5, y: 0.5, z: 0.5 };
|
||||
}
|
||||
|
||||
var spacing = majorOnly ? majorGridEvery : minorGridEvery;
|
||||
|
||||
position = Vec3.subtract(position, origin);
|
||||
|
@ -112,7 +120,7 @@ Grid = function(opts) {
|
|||
position.y = Math.round(position.y / spacing) * spacing;
|
||||
position.z = Math.round(position.z / spacing) * spacing;
|
||||
|
||||
return Vec3.sum(Vec3.sum(position, Vec3.multiply(0.5, dimensions)), origin);
|
||||
return Vec3.sum(Vec3.sum(position, Vec3.multiplyVbyV(registration, dimensions)), origin);
|
||||
}
|
||||
|
||||
that.snapToSpacing = function(delta, majorOnly) {
|
||||
|
@ -161,9 +169,9 @@ Grid = function(opts) {
|
|||
|
||||
if (data.origin) {
|
||||
var pos = data.origin;
|
||||
pos.x = pos.x === undefined ? origin.x : pos.x;
|
||||
pos.y = pos.y === undefined ? origin.y : pos.y;
|
||||
pos.z = pos.z === undefined ? origin.z : pos.z;
|
||||
pos.x = pos.x === undefined ? origin.x : parseFloat(pos.x);
|
||||
pos.y = pos.y === undefined ? origin.y : parseFloat(pos.y);
|
||||
pos.z = pos.z === undefined ? origin.z : parseFloat(pos.z);
|
||||
that.setPosition(pos, true);
|
||||
}
|
||||
|
||||
|
@ -238,7 +246,13 @@ GridTool = function(opts) {
|
|||
});
|
||||
|
||||
webView.webEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {
|
||||
print("gridTool.js: Error parsing JSON: " + e.name + " data " + data)
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.type == "init") {
|
||||
horizontalGrid.emitUpdate();
|
||||
} else if (data.type == "update") {
|
||||
|
|
|
@ -410,8 +410,15 @@ function takeSnapshot() {
|
|||
Menu.setIsOptionChecked("Overlays", false);
|
||||
}
|
||||
|
||||
var snapActivateSound = SoundCache.getSound(Script.resolvePath("../../resources/sounds/snap.wav"));
|
||||
|
||||
// take snapshot (with no notification)
|
||||
Script.setTimeout(function () {
|
||||
Audio.playSound(snapActivateSound, {
|
||||
position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z },
|
||||
localOnly: true,
|
||||
volume: 1.0
|
||||
});
|
||||
HMD.closeTablet();
|
||||
Script.setTimeout(function () {
|
||||
Window.takeSnapshot(false, includeAnimated, 1.91);
|
||||
|
|
|
@ -191,16 +191,12 @@
|
|||
gTablet.updateAudioBar(currentMicLevel);
|
||||
}
|
||||
|
||||
if (validCheckTime - now > MSECS_PER_SEC/4) {
|
||||
//each 250ms should be just fine
|
||||
if (now - validCheckTime > MSECS_PER_SEC) {
|
||||
validCheckTime = now;
|
||||
updateTabletWidthFromSettings();
|
||||
if (UIWebTablet) {
|
||||
UIWebTablet.setLandscape(landscape);
|
||||
}
|
||||
}
|
||||
|
||||
if (validCheckTime - now > MSECS_PER_SEC) {
|
||||
validCheckTime = now;
|
||||
if (tabletRezzed && UIWebTablet && !tabletIsValid()) {
|
||||
// when we switch domains, the tablet entity gets destroyed and recreated. this causes
|
||||
// the overlay to be deleted, but not recreated. If the overlay is deleted for this or any
|
||||
|
|
|
@ -821,6 +821,17 @@ for (var key in trayIcons) {
|
|||
|
||||
const notificationIcon = path.join(__dirname, '../resources/console-notification.png');
|
||||
|
||||
function isProcessRunning(pid) {
|
||||
try {
|
||||
// Sending a signal of 0 is effectively a NOOP.
|
||||
// If sending the signal is successful, kill will return true.
|
||||
// If the process is not running, an exception will be thrown.
|
||||
return process.kill(pid, 0);
|
||||
} catch (e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function onContentLoaded() {
|
||||
// Disable splash window for now.
|
||||
// maybeShowSplash();
|
||||
|
@ -882,31 +893,18 @@ function onContentLoaded() {
|
|||
startInterface();
|
||||
}
|
||||
|
||||
// If we were launched with the shutdownWatcher option, then we need to watch for the interface app
|
||||
// shutting down. The interface app will regularly update a running state file which we will check.
|
||||
// If the file doesn't exist or stops updating for a significant amount of time, we will shut down.
|
||||
if (argv.shutdownWatcher) {
|
||||
log.debug("Shutdown watcher requested... argv.shutdownWatcher:", argv.shutdownWatcher);
|
||||
var MAX_TIME_SINCE_EDIT = 5000; // 5 seconds between updates
|
||||
var firstAttemptToCheck = new Date().getTime();
|
||||
var shutdownWatchInterval = setInterval(function(){
|
||||
var stats = fs.stat(argv.shutdownWatcher, function(err, stats) {
|
||||
if (err) {
|
||||
var sinceFirstCheck = new Date().getTime() - firstAttemptToCheck;
|
||||
if (sinceFirstCheck > MAX_TIME_SINCE_EDIT) {
|
||||
log.debug("Running state file is missing, assume interface has shutdown... shutting down snadbox.");
|
||||
forcedShutdown();
|
||||
clearTimeout(shutdownWatchInterval);
|
||||
}
|
||||
} else {
|
||||
var sinceEdit = new Date().getTime() - stats.mtime.getTime();
|
||||
if (sinceEdit > MAX_TIME_SINCE_EDIT) {
|
||||
log.debug("Running state of interface hasn't updated in MAX time... shutting down.");
|
||||
forcedShutdown();
|
||||
clearTimeout(shutdownWatchInterval);
|
||||
}
|
||||
}
|
||||
});
|
||||
// If we were launched with the shutdownWith option, then we need to shutdown when that process (pid)
|
||||
// is no longer running.
|
||||
if (argv.shutdownWith) {
|
||||
let pid = argv.shutdownWith;
|
||||
console.log("Shutting down with process: ", pid);
|
||||
let checkProcessInterval = setInterval(function() {
|
||||
let isRunning = isProcessRunning(pid);
|
||||
if (!isRunning) {
|
||||
log.debug("Watched process is no longer running, shutting down");
|
||||
clearTimeout(checkProcessInterval);
|
||||
forcedShutdown();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
# Declare dependencies
|
||||
macro (SETUP_TESTCASE_DEPENDENCIES)
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(shared ktx gpu image)
|
||||
|
||||
set(TARGET_NAME ktx-test)
|
||||
|
||||
if (WIN32)
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217")
|
||||
endif()
|
||||
package_libraries_for_deployment()
|
||||
endmacro ()
|
||||
|
||||
# This is not a testcase -- just set it up as a regular hifi project
|
||||
setup_hifi_project(Quick Gui OpenGL)
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
||||
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image)
|
||||
|
||||
package_libraries_for_deployment()
|
||||
setup_hifi_testcase()
|
||||
|
|
195
tests/ktx/src/KtxTests.cpp
Normal file
195
tests/ktx/src/KtxTests.cpp
Normal file
|
@ -0,0 +1,195 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/07/01
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "KtxTests.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
#include <ktx/KTX.h>
|
||||
#include <gpu/Texture.h>
|
||||
#include <image/Image.h>
|
||||
|
||||
|
||||
QTEST_GUILESS_MAIN(KtxTests)
|
||||
|
||||
QString getRootPath() {
|
||||
static std::once_flag once;
|
||||
static QString result;
|
||||
std::call_once(once, [&] {
|
||||
QFileInfo file(__FILE__);
|
||||
QDir parent = file.absolutePath();
|
||||
result = QDir::cleanPath(parent.currentPath() + "/../../..");
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void KtxTests::initTestCase() {
|
||||
}
|
||||
|
||||
void KtxTests::cleanupTestCase() {
|
||||
}
|
||||
|
||||
void KtxTests::testKhronosCompressionFunctions() {
|
||||
using namespace khronos::gl::texture;
|
||||
QCOMPARE(evalAlignedCompressedBlockCount<4>(0), (uint32_t)0x0);
|
||||
QCOMPARE(evalAlignedCompressedBlockCount<4>(1), (uint32_t)0x1);
|
||||
QCOMPARE(evalAlignedCompressedBlockCount<4>(4), (uint32_t)0x1);
|
||||
QCOMPARE(evalAlignedCompressedBlockCount<4>(5), (uint32_t)0x2);
|
||||
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x00), (uint32_t)0x00);
|
||||
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x01), (uint32_t)0x01);
|
||||
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x04), (uint32_t)0x01);
|
||||
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x05), (uint32_t)0x02);
|
||||
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x1000), (uint32_t)0x400);
|
||||
|
||||
QVERIFY_EXCEPTION_THROWN(evalCompressedBlockCount(InternalFormat::RGBA8, 0x00), std::runtime_error);
|
||||
}
|
||||
|
||||
void KtxTests::testKtxEvalFunctions() {
|
||||
QCOMPARE(sizeof(ktx::Header), (size_t)64);
|
||||
QCOMPARE(ktx::evalPadding(0x0), (uint8_t)0);
|
||||
QCOMPARE(ktx::evalPadding(0x1), (uint8_t)3);
|
||||
QCOMPARE(ktx::evalPadding(0x2), (uint8_t)2);
|
||||
QCOMPARE(ktx::evalPadding(0x3), (uint8_t)1);
|
||||
QCOMPARE(ktx::evalPadding(0x4), (uint8_t)0);
|
||||
QCOMPARE(ktx::evalPadding(0x400), (uint8_t)0);
|
||||
QCOMPARE(ktx::evalPadding(0x401), (uint8_t)3);
|
||||
QCOMPARE(ktx::evalPaddedSize(0x0), 0x0);
|
||||
QCOMPARE(ktx::evalPaddedSize(0x1), 0x4);
|
||||
QCOMPARE(ktx::evalPaddedSize(0x2), 0x4);
|
||||
QCOMPARE(ktx::evalPaddedSize(0x3), 0x4);
|
||||
QCOMPARE(ktx::evalPaddedSize(0x4), 0x4);
|
||||
QCOMPARE(ktx::evalPaddedSize(0x400), 0x400);
|
||||
QCOMPARE(ktx::evalPaddedSize(0x401), 0x404);
|
||||
QCOMPARE(ktx::evalAlignedCount((uint32_t)0x0), (uint32_t)0x0);
|
||||
QCOMPARE(ktx::evalAlignedCount((uint32_t)0x1), (uint32_t)0x1);
|
||||
QCOMPARE(ktx::evalAlignedCount((uint32_t)0x4), (uint32_t)0x1);
|
||||
QCOMPARE(ktx::evalAlignedCount((uint32_t)0x5), (uint32_t)0x2);
|
||||
}
|
||||
|
||||
void KtxTests::testKtxSerialization() {
|
||||
const QString TEST_IMAGE = getRootPath() + "/scripts/developer/tests/cube_texture.png";
|
||||
QImage image(TEST_IMAGE);
|
||||
gpu::TexturePointer testTexture = image::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true);
|
||||
auto ktxMemory = gpu::Texture::serialize(*testTexture);
|
||||
QVERIFY(ktxMemory.get());
|
||||
|
||||
// Serialize the image to a file
|
||||
QTemporaryFile TEST_IMAGE_KTX;
|
||||
{
|
||||
const auto& ktxStorage = ktxMemory->getStorage();
|
||||
QVERIFY(ktx::KTX::validate(ktxStorage));
|
||||
QVERIFY(ktxMemory->isValid());
|
||||
|
||||
auto& outFile = TEST_IMAGE_KTX;
|
||||
if (!outFile.open()) {
|
||||
QFAIL("Unable to open file");
|
||||
}
|
||||
auto ktxSize = ktxStorage->size();
|
||||
outFile.resize(ktxSize);
|
||||
auto dest = outFile.map(0, ktxSize);
|
||||
memcpy(dest, ktxStorage->data(), ktxSize);
|
||||
outFile.unmap(dest);
|
||||
outFile.close();
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
auto ktxStorage = std::make_shared<storage::FileStorage>(TEST_IMAGE_KTX.fileName());
|
||||
QVERIFY(ktx::KTX::validate(ktxStorage));
|
||||
auto ktxFile = ktx::KTX::create(ktxStorage);
|
||||
QVERIFY(ktxFile.get());
|
||||
QVERIFY(ktxFile->isValid());
|
||||
{
|
||||
const auto& memStorage = ktxMemory->getStorage();
|
||||
const auto& fileStorage = ktxFile->getStorage();
|
||||
QVERIFY(memStorage->size() == fileStorage->size());
|
||||
QVERIFY(memStorage->data() != fileStorage->data());
|
||||
QVERIFY(0 == memcmp(memStorage->data(), fileStorage->data(), memStorage->size()));
|
||||
QVERIFY(ktxFile->_images.size() == ktxMemory->_images.size());
|
||||
auto imageCount = ktxFile->_images.size();
|
||||
auto startMemory = ktxMemory->_storage->data();
|
||||
auto startFile = ktxFile->_storage->data();
|
||||
for (size_t i = 0; i < imageCount; ++i) {
|
||||
auto memImages = ktxMemory->_images[i];
|
||||
auto fileImages = ktxFile->_images[i];
|
||||
QVERIFY(memImages._padding == fileImages._padding);
|
||||
QVERIFY(memImages._numFaces == fileImages._numFaces);
|
||||
QVERIFY(memImages._imageSize == fileImages._imageSize);
|
||||
QVERIFY(memImages._faceSize == fileImages._faceSize);
|
||||
QVERIFY(memImages._faceBytes.size() == memImages._numFaces);
|
||||
QVERIFY(fileImages._faceBytes.size() == fileImages._numFaces);
|
||||
auto faceCount = fileImages._numFaces;
|
||||
for (uint32_t face = 0; face < faceCount; ++face) {
|
||||
auto memFace = memImages._faceBytes[face];
|
||||
auto memOffset = memFace - startMemory;
|
||||
auto fileFace = fileImages._faceBytes[face];
|
||||
auto fileOffset = fileFace - startFile;
|
||||
QVERIFY(memOffset % 4 == 0);
|
||||
QVERIFY(memOffset == fileOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
testTexture->setKtxBacking(TEST_IMAGE_KTX.fileName().toStdString());
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
static const QString TEST_FOLDER { "H:/ktx_cacheold" };
|
||||
//static const QString TEST_FOLDER { "C:/Users/bdavis/Git/KTX/testimages" };
|
||||
|
||||
//static const QString EXTENSIONS { "4bbdf8f786470e4ab3e672d44b8e8df2.ktx" };
|
||||
static const QString EXTENSIONS { "*.ktx" };
|
||||
|
||||
int mainTemp(int, char**) {
|
||||
qInstallMessageHandler(messageHandler);
|
||||
auto fileInfoList = QDir { TEST_FOLDER }.entryInfoList(QStringList { EXTENSIONS });
|
||||
for (auto fileInfo : fileInfoList) {
|
||||
qDebug() << fileInfo.filePath();
|
||||
std::shared_ptr<storage::Storage> storage { new storage::FileStorage { fileInfo.filePath() } };
|
||||
|
||||
if (!ktx::KTX::validate(storage)) {
|
||||
qDebug() << "KTX invalid";
|
||||
}
|
||||
|
||||
auto ktxFile = ktx::KTX::create(storage);
|
||||
ktx::KTXDescriptor ktxDescriptor = ktxFile->toDescriptor();
|
||||
|
||||
qDebug() << "Contains " << ktxDescriptor.keyValues.size() << " key value pairs";
|
||||
for (const auto& kv : ktxDescriptor.keyValues) {
|
||||
qDebug() << "\t" << kv._key.c_str();
|
||||
}
|
||||
|
||||
auto offsetToMinMipKV = ktxDescriptor.getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY);
|
||||
if (offsetToMinMipKV) {
|
||||
auto data = storage->data() + ktx::KTX_HEADER_SIZE + offsetToMinMipKV;
|
||||
auto minMipLevelAvailable = *data;
|
||||
qDebug() << "\tMin mip available " << minMipLevelAvailable;
|
||||
assert(minMipLevelAvailable < ktxDescriptor.header.numberOfMipmapLevels);
|
||||
}
|
||||
auto storageSize = storage->size();
|
||||
for (const auto& faceImageDesc : ktxDescriptor.images) {
|
||||
//assert(0 == (faceImageDesc._faceSize % 4));
|
||||
for (const auto& faceOffset : faceImageDesc._faceOffsets) {
|
||||
assert(0 == (faceOffset % 4));
|
||||
auto faceEndOffset = faceOffset + faceImageDesc._faceSize;
|
||||
assert(faceEndOffset <= storageSize);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& faceImage : ktxFile->_images) {
|
||||
for (const ktx::Byte* faceBytes : faceImage._faceBytes) {
|
||||
assert(0 == (reinterpret_cast<size_t>(faceBytes) % 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue