Merge branch 'master' into 21202

This commit is contained in:
David Rowe 2017-06-06 11:18:31 +12:00
commit ad848706ec
145 changed files with 3737 additions and 1465 deletions

View file

@ -410,6 +410,7 @@ void Agent::executeScript() {
bool openedInLastBlock = !_audioGateOpen && audioGateOpen; // the gate just opened
bool closedInLastBlock = _audioGateOpen && !audioGateOpen; // the gate just closed
_audioGateOpen = audioGateOpen;
Q_UNUSED(openedInLastBlock);
// the codec must be flushed to silence before sending silent packets,
// so delay the transition to silent packets by one packet after becoming silent.

View file

@ -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" ] }
]
}

View file

@ -388,8 +388,13 @@ Rectangle {
sortIndicatorColumn: settings.nearbySortIndicatorColumn;
sortIndicatorOrder: settings.nearbySortIndicatorOrder;
onSortIndicatorColumnChanged: {
settings.nearbySortIndicatorColumn = sortIndicatorColumn;
sortModel();
if (sortIndicatorColumn > 2) {
// these are not sortable, switch back to last column
sortIndicatorColumn = settings.nearbySortIndicatorColumn;
} else {
settings.nearbySortIndicatorColumn = sortIndicatorColumn;
sortModel();
}
}
onSortIndicatorOrderChanged: {
settings.nearbySortIndicatorOrder = sortIndicatorOrder;

View file

@ -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);

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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>
@ -144,6 +147,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"
@ -534,6 +538,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<AvatarBookmarks>();
DependencyManager::set<LocationBookmarks>();
DependencyManager::set<Snapshot>();
DependencyManager::set<CloseEventSender>();
return previousSessionCrashed;
}
@ -1459,6 +1464,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) {
@ -1568,6 +1575,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");
@ -1681,6 +1696,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;
@ -1732,6 +1751,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);
}
@ -2183,6 +2211,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);
@ -2382,15 +2413,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;
@ -2404,10 +2436,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()) {
@ -2415,8 +2455,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 {
@ -2429,23 +2471,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.
@ -2774,6 +2833,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;
@ -4488,12 +4558,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) {
@ -6305,21 +6376,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));

View file

@ -407,6 +407,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,

View file

@ -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";

View file

@ -2770,7 +2770,7 @@ glm::mat4 MyAvatar::getLeftFootCalibrationMat() const {
auto leftFootRot = getAbsoluteDefaultJointRotationInObjectFrame(leftFootIndex);
return createMatFromQuatAndPos(leftFootRot, leftFootPos);
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTFOOT_POS, DEFAULT_AVATAR_LEFTFOOT_POS);
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTFOOT_ROT, DEFAULT_AVATAR_LEFTFOOT_POS);
}
}
@ -2782,7 +2782,7 @@ glm::mat4 MyAvatar::getRightFootCalibrationMat() const {
auto rightFootRot = getAbsoluteDefaultJointRotationInObjectFrame(rightFootIndex);
return createMatFromQuatAndPos(rightFootRot, rightFootPos);
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTFOOT_POS, DEFAULT_AVATAR_RIGHTFOOT_POS);
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTFOOT_ROT, DEFAULT_AVATAR_RIGHTFOOT_POS);
}
}
@ -2805,7 +2805,7 @@ glm::mat4 MyAvatar::getLeftArmCalibrationMat() const {
auto leftArmRot = getAbsoluteDefaultJointRotationInObjectFrame(leftArmIndex);
return createMatFromQuatAndPos(leftArmRot, leftArmPos);
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTARM_ROT, DEFAULT_AVATAR_RIGHTARM_POS);
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTARM_ROT, DEFAULT_AVATAR_LEFTARM_POS);
}
}

View file

@ -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>();

View 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;
}

View 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

View file

@ -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);

View file

@ -850,7 +850,10 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
glm::quat sensorToWorldQuat;
unpackOrientationQuatFromSixBytes(data->sensorToWorldQuat, sensorToWorldQuat);
float sensorToWorldScale;
unpackFloatScalarFromSignedTwoByteFixed((int16_t*)&data->sensorToWorldScale, &sensorToWorldScale, SENSOR_TO_WORLD_SCALE_RADIX);
// Grab a local copy of sensorToWorldScale to be able to use the unpack function with a pointer on it,
// a direct pointer on the struct attribute triggers warnings because of potential misalignement.
auto srcSensorToWorldScale = data->sensorToWorldScale;
unpackFloatScalarFromSignedTwoByteFixed((int16_t*)&srcSensorToWorldScale, &sensorToWorldScale, SENSOR_TO_WORLD_SCALE_RADIX);
glm::vec3 sensorToWorldTrans(data->sensorToWorldTrans[0], data->sensorToWorldTrans[1], data->sensorToWorldTrans[2]);
glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans);
if (_sensorToWorldMatrixCache.get() != sensorToWorldMatrix) {

View file

@ -42,8 +42,6 @@ enum class Action {
LEFT_HAND = NUM_COMBINED_AXES,
RIGHT_HAND,
LEFT_ARM,
RIGHT_ARM,
LEFT_FOOT,
RIGHT_FOOT,
HIPS,
@ -103,6 +101,8 @@ enum class Action {
// Bisected aliases for TRANSLATE_CAMERA_Z
BOOM_IN,
BOOM_OUT,
LEFT_ARM,
RIGHT_ARM,
NUM_ACTIONS,

View file

@ -9,7 +9,6 @@
#include "InputRecorder.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QFile>
#include <QDir>
@ -19,13 +18,17 @@
#include <QByteArray>
#include <QStandardPaths>
#include <PathUtils.h>
#include <QUrl>
#include <Gzip.h>
#include <BuildInfo.h>
#include <GLMHelpers.h>
#include <DependencyManager.h>
#include "UserInputMapper.h"
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) {
@ -91,23 +94,27 @@ namespace controller {
}
void exportToFile(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();
}
QJsonObject openFile(const QString& file, bool& status) {
@ -118,10 +125,19 @@ 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();
return object;
}
@ -146,31 +162,47 @@ namespace controller {
_actionStateList.clear();
}
void InputRecorder::saveRecording() {
QJsonObject InputRecorder::recordDataToJson() {
QJsonObject data;
data["frameCount"] = _framesRecorded;
data["version"] = "0.0";
QJsonArray actionArrayList;
QJsonArray poseArrayList;
for(const ActionStates actionState: _actionStateList) {
QJsonArray actionArray;
for (const float value: actionState) {
actionArray.append(value);
for (const auto action: actionState) {
QJsonObject actionJson;
actionJson["name"] = action.first;
actionJson["value"] = action.second;
actionArray.append(actionJson);
}
actionArrayList.append(actionArray);
}
for (const PoseStates poseState: _poseStateList) {
QJsonArray poseArray;
for (const Pose pose: poseState) {
poseArray.append(poseToJsonObject(pose));
for (const auto pose: poseState) {
QJsonObject poseJson;
poseJson["name"] = pose.first;
poseJson["pose"] = poseToJsonObject(pose.second);
poseArray.append(poseJson);
}
poseArrayList.append(poseArray);
}
data["actionList"] = actionArrayList;
data["poseList"] = poseArrayList;
exportToFile(data);
return data;
}
void InputRecorder::saveRecording() {
QJsonObject jsonData = recordDataToJson();
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) {
@ -181,17 +213,20 @@ namespace controller {
resetFrame();
_poseStateList.clear();
_actionStateList.clear();
QString filePath = path;
filePath.remove(0,8);
QUrl urlPath(path);
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(info.absoluteFilePath(), success);
if (success) {
QJsonObject data = openFile(filePath, success);
auto keyValue = data.find("version");
if (success && keyValue != data.end()) {
_framesRecorded = data["frameCount"].toInt();
QJsonArray actionArrayList = data["actionList"].toArray();
QJsonArray poseArrayList = data["poseList"].toArray();
@ -199,30 +234,31 @@ namespace controller {
for (int actionIndex = 0; actionIndex < actionArrayList.size(); actionIndex++) {
QJsonArray actionState = actionArrayList[actionIndex].toArray();
for (int index = 0; index < actionState.size(); index++) {
_currentFrameActions[index] = actionState[index].toDouble();
QJsonObject actionObject = actionState[index].toObject();
_currentFrameActions[actionObject["name"].toString()] = actionObject["value"].toDouble();
}
_actionStateList.push_back(_currentFrameActions);
_currentFrameActions = ActionStates(toInt(Action::NUM_ACTIONS));
_currentFrameActions.clear();
}
for (int poseIndex = 0; poseIndex < poseArrayList.size(); poseIndex++) {
QJsonArray poseState = poseArrayList[poseIndex].toArray();
for (int index = 0; index < poseState.size(); index++) {
_currentFramePoses[index] = jsonObjectToPose(poseState[index].toObject());
QJsonObject poseObject = poseState[index].toObject();
_currentFramePoses[poseObject["name"].toString()] = jsonObjectToPose(poseObject["pose"].toObject());
}
_poseStateList.push_back(_currentFramePoses);
_currentFramePoses = PoseStates(toInt(Action::NUM_ACTIONS));
_currentFramePoses.clear();
}
}
}
_loading = false;
}
void InputRecorder::stopRecording() {
_recording = false;
_framesRecorded = (int)_actionStateList.size();
}
void InputRecorder::startPlayback() {
_playback = true;
_recording = false;
@ -234,41 +270,36 @@ namespace controller {
_playCount = 0;
}
void InputRecorder::setActionState(controller::Action action, float value) {
void InputRecorder::setActionState(const QString& action, float value) {
if (_recording) {
_currentFrameActions[toInt(action)] += value;
_currentFrameActions[action] += value;
}
}
void InputRecorder::setActionState(controller::Action action, const controller::Pose pose) {
void InputRecorder::setActionState(const QString& action, const controller::Pose& pose) {
if (_recording) {
_currentFramePoses[toInt(action)] = pose;
_currentFramePoses[action] = pose;
}
}
void InputRecorder::resetFrame() {
if (_recording) {
for(auto& channel : _currentFramePoses) {
channel = Pose();
}
for(auto& channel : _currentFrameActions) {
channel = 0.0f;
}
_currentFramePoses.clear();
_currentFrameActions.clear();
}
}
float InputRecorder::getActionState(controller::Action action) {
float InputRecorder::getActionState(const QString& action) {
if (_actionStateList.size() > 0 ) {
return _actionStateList[_playCount][toInt(action)];
return _actionStateList[_playCount][action];
}
return 0.0f;
}
controller::Pose InputRecorder::getPoseState(controller::Action action) {
controller::Pose InputRecorder::getPoseState(const QString& action) {
if (_poseStateList.size() > 0) {
return _poseStateList[_playCount][toInt(action)];
return _poseStateList[_playCount][action];
}
return Pose();

View file

@ -12,8 +12,10 @@
#include <mutex>
#include <atomic>
#include <vector>
#include <map>
#include <QString>
#include <QJsonObject>
#include "Pose.h"
#include "Actions.h"
@ -21,8 +23,8 @@
namespace controller {
class InputRecorder {
public:
using PoseStates = std::vector<Pose>;
using ActionStates = std::vector<float>;
using PoseStates = std::map<QString, Pose>;
using ActionStates = std::map<QString, float>;
InputRecorder();
~InputRecorder();
@ -40,20 +42,21 @@ namespace controller {
void resetFrame();
bool isRecording() { return _recording; }
bool isPlayingback() { return (_playback && !_loading); }
void setActionState(controller::Action action, float value);
void setActionState(controller::Action action, const controller::Pose pose);
float getActionState(controller::Action action);
controller::Pose getPoseState(controller::Action action);
void setActionState(const QString& action, float value);
void setActionState(const QString& action, const controller::Pose& pose);
float getActionState(const QString& action);
controller::Pose getPoseState(const QString& action);
QString getSaveDirectory();
void frameTick();
private:
QJsonObject recordDataToJson();
bool _recording { false };
bool _playback { false };
bool _loading { false };
std::vector<PoseStates> _poseStateList = std::vector<PoseStates>();
std::vector<ActionStates> _actionStateList = std::vector<ActionStates>();
PoseStates _currentFramePoses = PoseStates(toInt(Action::NUM_ACTIONS));
ActionStates _currentFrameActions = ActionStates(toInt(Action::NUM_ACTIONS));
PoseStates _currentFramePoses;
ActionStates _currentFrameActions;
int _framesRecorded { 0 };
int _playCount { 0 };

View file

@ -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;

View file

@ -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;

View file

@ -17,31 +17,30 @@ using namespace controller;
void ActionEndpoint::apply(float newValue, const Pointer& source) {
InputRecorder* inputRecorder = InputRecorder::getInstance();
auto userInputMapper = DependencyManager::get<UserInputMapper>();
QString actionName = userInputMapper->getActionName(Action(_input.getChannel()));
if(inputRecorder->isPlayingback()) {
newValue = inputRecorder->getActionState(Action(_input.getChannel()));
newValue = inputRecorder->getActionState(actionName);
}
_currentValue += newValue;
if (_input != Input::INVALID_INPUT) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->deltaActionState(Action(_input.getChannel()), newValue);
}
inputRecorder->setActionState(Action(_input.getChannel()), newValue);
inputRecorder->setActionState(actionName, newValue);
}
void ActionEndpoint::apply(const Pose& value, const Pointer& source) {
_currentPose = value;
InputRecorder* inputRecorder = InputRecorder::getInstance();
inputRecorder->setActionState(Action(_input.getChannel()), _currentPose);
if (inputRecorder->isPlayingback()) {
_currentPose = inputRecorder->getPoseState(Action(_input.getChannel()));
}
auto userInputMapper = DependencyManager::get<UserInputMapper>();
QString actionName = userInputMapper->getActionName(Action(_input.getChannel()));
inputRecorder->setActionState(actionName, _currentPose);
if (!_currentPose.isValid()) {
return;
}
if (_input != Input::INVALID_INPUT) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->setActionState(Action(_input.getChannel()), _currentPose);
}
}

View file

@ -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();
}

View file

@ -21,9 +21,9 @@ namespace controller {
LowVelocityFilter(float rotationConstant, float translationConstant) :
_translationConstant(translationConstant), _rotationConstant(rotationConstant) {}
virtual float apply(float value) const override { return value; }
virtual Pose apply(Pose newPose) const;
virtual bool parseParameters(const QJsonValue& parameters) override;
float apply(float value) const override { return value; }
Pose apply(Pose newPose) const override;
bool parseParameters(const QJsonValue& parameters) override;
private:
float _translationConstant { 0.1f };

View file

@ -192,7 +192,7 @@ void EntityTreeRenderer::update() {
tree->update();
// Handle enter/leave entity logic
bool updated = checkEnterLeaveEntities();
checkEnterLeaveEntities();
// Even if we're not moving the mouse, if we started clicking on an entity and we have
// not yet released the hold then this is still considered a holdingClickOnEntity event

View file

@ -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));

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -367,7 +367,6 @@ void RenderableZoneEntityItem::sceneUpdateRenderItemFromEntity(render::Transacti
bool sunChanged = _keyLightPropertiesChanged;
bool backgroundChanged = _backgroundPropertiesChanged;
bool stageChanged = _stagePropertiesChanged;
bool skyboxChanged = _skyboxPropertiesChanged;
transaction.updateItem<RenderableZoneEntityItemMeta>(_myMetaItem, [=](RenderableZoneEntityItemMeta& data) {

View file

@ -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());
}
}

View file

@ -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();

View file

@ -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.
});

View file

@ -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

View file

@ -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() {

View file

@ -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
};

View file

@ -211,6 +211,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
break;
case gpu::NUINT32:
case gpu::NINT32:
case gpu::COMPRESSED:
case gpu::NUM_TYPES: // quiet compiler
Q_UNREACHABLE();
}
@ -484,6 +485,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
texel.internalFormat = GL_R8_SNORM;
break;
}
case gpu::COMPRESSED:
case gpu::NUM_TYPES: { // quiet compiler
Q_UNREACHABLE();
}
@ -527,6 +529,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
texel.internalFormat = GL_DEPTH_COMPONENT24;
break;
}
case gpu::COMPRESSED:
case gpu::NUM_TYPES: { // quiet compiler
Q_UNREACHABLE();
}
@ -641,6 +644,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
break;
case gpu::NUINT32:
case gpu::NINT32:
case gpu::COMPRESSED:
case gpu::NUM_TYPES: // quiet compiler
Q_UNREACHABLE();
}

View 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)$>
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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));

View file

@ -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;

View file

@ -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;

View file

@ -229,7 +229,7 @@ namespace ktx {
} else {
Image::FaceBytes faceBytes(NUM_CUBEMAPFACES);
auto faceSize = srcImages[l]._faceSize;
for (int face = 0; face < NUM_CUBEMAPFACES; face++) {
for (uint32_t face = 0; face < NUM_CUBEMAPFACES; face++) {
memcpy(currentPtr, srcImages[l]._faceBytes[face], faceSize);
faceBytes[face] = currentPtr;
currentPtr += faceSize;

View file

@ -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) {

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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,

View file

@ -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); }

View file

@ -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);
}

View file

@ -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 };
};

View file

@ -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) {

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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));
}

View file

@ -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 = {});

View file

@ -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();

View file

@ -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;

View file

@ -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()) {

View file

@ -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;

View file

@ -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

View file

@ -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();
}

View file

@ -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;

View file

@ -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);

View file

@ -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() {

View file

@ -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));

View file

@ -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);

View file

@ -112,7 +112,6 @@ void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext,
skybox->render(batch, args->getViewFrustum());
});
args->_batch = nullptr;
gpu::Batch& batch = *args->_batch;
// break;
}

View file

@ -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

View file

@ -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>;

View file

@ -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);

View file

@ -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);
});
}

View file

@ -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

View file

@ -16,6 +16,8 @@
#include <gpu/StandardShaderLib.h>
#include "StencilMaskPass.h"
#include "lightClusters_drawGrid_vert.h"
#include "lightClusters_drawGrid_frag.h"

View file

@ -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
}
});
}

View file

@ -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)

View file

@ -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);

View 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));
}

View 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

View file

@ -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);

View file

@ -15,7 +15,7 @@
#include <gpu/StandardShaderLib.h>
#include <RenderArgs.h>
#include "StencilMaskPass.h"
#include "FramebufferCache.h"
#include "toneMapping_frag.h"

View file

@ -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);
}
@ -169,7 +173,7 @@ void DebugZoneLighting::run(const render::RenderContextPointer& context, const I
batch.setUniformBuffer(ZONE_DEFERRED_TRANSFORM_BUFFER, deferredTransform->getFrameTransformBuffer());
batch.setPipeline(getKeyLightPipeline());
auto numKeys = keyLightStack.size();
auto numKeys = (int) keyLightStack.size();
for (int i = numKeys - 1; i >= 0; i--) {
model.setTranslation(glm::vec3(-4.0, -3.0 + (i * 1.0), -10.0 - (i * 3.0)));
batch.setModelTransform(model);
@ -180,7 +184,7 @@ void DebugZoneLighting::run(const render::RenderContextPointer& context, const I
}
batch.setPipeline(getAmbientPipeline());
auto numAmbients = ambientLightStack.size();
auto numAmbients = (int) ambientLightStack.size();
for (int i = numAmbients - 1; i >= 0; i--) {
model.setTranslation(glm::vec3(0.0, -3.0 + (i * 1.0), -10.0 - (i * 3.0)));
batch.setModelTransform(model);
@ -194,7 +198,7 @@ void DebugZoneLighting::run(const render::RenderContextPointer& context, const I
}
batch.setPipeline(getBackgroundPipeline());
auto numBackgrounds = skyboxStack.size();
auto numBackgrounds = (int) skyboxStack.size();
for (int i = numBackgrounds - 1; i >= 0; i--) {
model.setTranslation(glm::vec3(4.0, -3.0 + (i * 1.0), -10.0 - (i * 3.0)));
batch.setModelTransform(model);

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -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;
!>
}

View file

@ -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;
}
*/
}

View 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;
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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)

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -24,7 +24,7 @@
#include <gl/QOpenGLContextWrapper.h>
#include <PerfStat.h>
#include <ViewFrustum.h>
#include <gpu/gl/GLbackend.h>
#include <gpu/gl/GLBackend.h>
#include <ui-plugins/PluginContainer.h>

View file

@ -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);
@ -175,6 +201,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();
@ -355,6 +383,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();
@ -369,6 +414,7 @@ void ViveControllerManager::InputDevice::uncalibrate() {
_pucksOffset.clear();
_jointToPuckMap.clear();
_calibrated = false;
_overrideHead = false;
}
void ViveControllerManager::InputDevice::updateCalibratedLimbs() {
@ -378,6 +424,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 {
@ -443,6 +493,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;
@ -630,6 +717,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);
@ -658,7 +765,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() {
@ -694,6 +813,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;
}
}
@ -702,11 +825,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);

View file

@ -67,6 +67,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);
@ -84,11 +85,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:
@ -119,6 +123,8 @@ private:
FeetAndHips,
FeetHipsAndChest,
FeetHipsAndShoulders,
FeetHipsChestAndHead,
FeetHipsAndHead
};
Config _config { Config::Auto };
Config _preferedConfig { Config::Auto };
@ -145,6 +151,7 @@ private:
bool _triggersPressedHandled { false };
bool _calibrated { false };
bool _timeTilCalibrationSet { false };
bool _overrideHead { false };
mutable std::recursive_mutex _lock;
QString configToString(Config config);

Some files were not shown because too many files have changed in this diff Show more