fixing merge conflicts

This commit is contained in:
Dante Ruiz 2017-06-06 16:42:08 +01:00
commit 038ffe920a
131 changed files with 3578 additions and 1409 deletions

View file

@ -8,8 +8,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
if (WIN32)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://s3.amazonaws.com/hifi-public/dependencies/nvtt-win-2.1.0.zip
URL_MD5 3ea6eeadbcc69071acf9c49ba565760e
URL http://s3.amazonaws.com/hifi-public/dependencies/nvtt-win-2.1.0.hifi.zip
URL_MD5 10da01cf601f88f6dc12a6bc13c89136
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
@ -29,8 +29,8 @@ else ()
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/nvidia-texture-tools-2.1.0.zip
URL_MD5 81b8fa6a9ee3f986088eb6e2215d6a57
URL http://hifi-public.s3.amazonaws.com/dependencies/nvidia-texture-tools-2.1.0.hifi.zip
URL_MD5 5794b950f8b265a9a41b2839b3bf7ebb
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DNVTT_SHARED=1 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
LOG_DOWNLOAD 1
LOG_CONFIGURE 1

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

@ -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>
@ -145,6 +148,7 @@
#include "InterfaceLogging.h"
#include "LODManager.h"
#include "ModelPackager.h"
#include "networking/CloseEventSender.h"
#include "networking/HFWebEngineProfile.h"
#include "networking/HFTabletWebEngineProfile.h"
#include "networking/FileTypeProfile.h"
@ -536,6 +540,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<AvatarBookmarks>();
DependencyManager::set<LocationBookmarks>();
DependencyManager::set<Snapshot>();
DependencyManager::set<CloseEventSender>();
return previousSessionCrashed;
}
@ -1461,6 +1466,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
updateSystemTabletMode();
connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged);
qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
}
void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
@ -1570,6 +1577,14 @@ void Application::aboutToQuit() {
getActiveDisplayPlugin()->deactivate();
// use the CloseEventSender via a QThread to send an event that says the user asked for the app to close
auto closeEventSender = DependencyManager::get<CloseEventSender>();
QThread* closureEventThread = new QThread(this);
closeEventSender->moveToThread(closureEventThread);
// sendQuitEventAsync will bail immediately if the UserActivityLogger is not enabled
connect(closureEventThread, &QThread::started, closeEventSender.data(), &CloseEventSender::sendQuitEventAsync);
closureEventThread->start();
// Hide Running Scripts dialog so that it gets destroyed in an orderly manner; prevents warnings at shutdown.
DependencyManager::get<OffscreenUi>()->hide("RunningScripts");
@ -1683,6 +1698,10 @@ Application::~Application() {
_physicsEngine->setCharacterController(nullptr);
// the _shapeManager should have zero references
_shapeManager.collectGarbage();
assert(_shapeManager.getNumShapes() == 0);
// shutdown render engine
_main3DScene = nullptr;
_renderEngine = nullptr;
@ -1734,6 +1753,15 @@ Application::~Application() {
_window->deleteLater();
// make sure that the quit event has finished sending before we take the application down
auto closeEventSender = DependencyManager::get<CloseEventSender>();
while (!closeEventSender->hasFinishedQuitEvent() && !closeEventSender->hasTimedOutQuitEvent()) {
// sleep a little so we're not spinning at 100%
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
// quit the thread used by the closure event sender
closeEventSender->thread()->quit();
// Can't log to file passed this point, FileLogger about to be deleted
qInstallMessageHandler(LogHandler::verboseMessageHandler);
}
@ -2185,6 +2213,9 @@ void Application::paintGL() {
});
renderArgs._context->setStereoProjections(eyeProjections);
renderArgs._context->setStereoViews(eyeOffsets);
// Configure the type of display / stereo
renderArgs._displayMode = (isHMDMode() ? RenderArgs::STEREO_HMD : RenderArgs::STEREO_MONITOR);
}
renderArgs._blitFramebuffer = finalFramebuffer;
displaySide(&renderArgs, _myCamera);
@ -2384,15 +2415,16 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
// Check HMD use (may be technically available without being in use)
bool hasHMD = PluginUtils::isHMDAvailable();
bool isUsingHMD = hasHMD && hasHandControllers && _displayPlugin->isHmd();
bool isUsingHMD = _displayPlugin->isHmd();
bool isUsingHMDAndHandControllers = hasHMD && hasHandControllers && isUsingHMD;
Setting::Handle<bool> tutorialComplete{ "tutorialComplete", false };
Setting::Handle<bool> firstRun{ Settings::firstRun, true };
bool isTutorialComplete = tutorialComplete.get();
bool shouldGoToTutorial = isUsingHMD && hasTutorialContent && !isTutorialComplete;
bool shouldGoToTutorial = isUsingHMDAndHandControllers && hasTutorialContent && !isTutorialComplete;
qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMD;
qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMDAndHandControllers;
qCDebug(interfaceapp) << "Tutorial version:" << contentVersion << ", sufficient:" << hasTutorialContent <<
", complete:" << isTutorialComplete << ", should go:" << shouldGoToTutorial;
@ -2406,10 +2438,18 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
const QString TUTORIAL_PATH = "/tutorial_begin";
static const QString SENT_TO_TUTORIAL = "tutorial";
static const QString SENT_TO_PREVIOUS_LOCATION = "previous_location";
static const QString SENT_TO_ENTRY = "entry";
static const QString SENT_TO_SANDBOX = "sandbox";
QString sentTo;
if (shouldGoToTutorial) {
if (sandboxIsRunning) {
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
DependencyManager::get<AddressManager>()->goToLocalSandbox(TUTORIAL_PATH);
sentTo = SENT_TO_TUTORIAL;
} else {
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
if (firstRun.get()) {
@ -2417,8 +2457,10 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
}
if (addressLookupString.isEmpty()) {
DependencyManager::get<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
} else {
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
sentTo = SENT_TO_PREVIOUS_LOCATION;
}
}
} else {
@ -2431,23 +2473,40 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
// If this is a first run we short-circuit the address passed in
if (isFirstRun) {
if (isUsingHMD) {
if (isUsingHMDAndHandControllers) {
if (sandboxIsRunning) {
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
DependencyManager::get<AddressManager>()->goToLocalSandbox();
sentTo = SENT_TO_SANDBOX;
} else {
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
DependencyManager::get<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
}
} else {
DependencyManager::get<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
}
} else {
qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
sentTo = SENT_TO_PREVIOUS_LOCATION;
}
}
UserActivityLogger::getInstance().logAction("startup_sent_to", {
{ "sent_to", sentTo },
{ "sandbox_is_running", sandboxIsRunning },
{ "has_hmd", hasHMD },
{ "has_hand_controllers", hasHandControllers },
{ "is_using_hmd", isUsingHMD },
{ "is_using_hmd_and_hand_controllers", isUsingHMDAndHandControllers },
{ "content_version", contentVersion },
{ "is_tutorial_complete", isTutorialComplete },
{ "has_tutorial_content", hasTutorialContent },
{ "should_go_to_tutorial", shouldGoToTutorial }
});
_connectionMonitor.init();
// After all of the constructor is completed, then set firstRun to false.
@ -2776,6 +2835,17 @@ void Application::keyPressEvent(QKeyEvent* event) {
if (isShifted && isMeta && !isOption) {
Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings);
} else if (!isOption && !isShifted && isMeta) {
AudioInjectorOptions options;
options.localOnly = true;
options.stereo = true;
if (_snapshotSoundInjector) {
_snapshotSoundInjector->setOptions(options);
_snapshotSoundInjector->restart();
} else {
QByteArray samples = _snapshotSound->getByteArray();
_snapshotSoundInjector = AudioInjector::playSound(samples, options);
}
takeSnapshot(true);
}
break;
@ -4490,12 +4560,13 @@ void Application::update(float deltaTime) {
getEntities()->getTree()->withWriteLock([&] {
PerformanceTimer perfTimer("handleOutgoingChanges");
const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates();
_entitySimulation->handleDeactivatedMotionStates(deactivations);
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates();
_entitySimulation->handleChangedMotionStates(outgoingChanges);
avatarManager->handleChangedMotionStates(outgoingChanges);
const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates();
_entitySimulation->handleDeactivatedMotionStates(deactivations);
});
if (!_aboutToQuit) {
@ -6307,21 +6378,6 @@ void Application::loadAddAvatarBookmarkDialog() const {
}
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
//keep sound thread out of event loop scope
AudioInjectorOptions options;
options.localOnly = true;
options.stereo = true;
if (_snapshotSoundInjector) {
_snapshotSoundInjector->setOptions(options);
_snapshotSoundInjector->restart();
} else {
QByteArray samples = _snapshotSound->getByteArray();
_snapshotSoundInjector = AudioInjector::playSound(samples, options);
}
postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
// Get a screenshot and save it
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio));

View file

@ -414,6 +414,12 @@ Menu::Menu() {
#endif
{
auto action = addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderClearKtxCache);
connect(action, &QAction::triggered, []{
Setting::Handle<int>(KTXCache::SETTING_VERSION_NAME, KTXCache::INVALID_VERSION).set(KTXCache::INVALID_VERSION);
});
}
// Developer > Render > LOD Tools
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, 0,

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

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

@ -19,6 +19,7 @@
#include <QStandardPaths>
#include <PathUtils.h>
#include <QUrl>
#include <Gzip.h>
#include <BuildInfo.h>
#include <GLMHelpers.h>
@ -27,7 +28,7 @@
QString SAVE_DIRECTORY = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/" + BuildInfo::MODIFIED_ORGANIZATION + "/" + BuildInfo::INTERFACE_NAME + "/hifi-input-recordings/";
QString FILE_PREFIX_NAME = "input-recording-";
QString COMPRESS_EXTENSION = "json.gz";
QString COMPRESS_EXTENSION = ".json.gz";
namespace controller {
QJsonObject poseToJsonObject(const Pose pose) {
@ -93,23 +94,26 @@ namespace controller {
}
void exportToFile(const QJsonObject& object) {
void exportToFile(const QJsonObject& object, const QString& fileName) {
if (!QDir(SAVE_DIRECTORY).exists()) {
QDir().mkdir(SAVE_DIRECTORY);
}
QString timeStamp = QDateTime::currentDateTime().toString(Qt::ISODate);
timeStamp.replace(":", "-");
QString fileName = SAVE_DIRECTORY + FILE_PREFIX_NAME + timeStamp + COMPRESS_EXTENSION;
qDebug() << fileName;
QFile saveFile (fileName);
if (!saveFile.open(QIODevice::WriteOnly)) {
qWarning() << "could not open file: " << fileName;
return;
}
QJsonDocument saveData(object);
QByteArray compressedData = qCompress(saveData.toJson(QJsonDocument::Compact));
saveFile.write(compressedData);
QByteArray jsonData = saveData.toJson(QJsonDocument::Indented);
QByteArray jsonDataForFile;
if (!gzip(jsonData, jsonDataForFile, -1)) {
qCritical("unable to gzip while saving to json.");
return;
}
saveFile.write(jsonDataForFile);
saveFile.close();
}
@ -121,8 +125,16 @@ namespace controller {
status = false;
return object;
}
QByteArray compressedData = qUncompress(openFile.readAll());
QJsonDocument jsonDoc = QJsonDocument::fromJson(compressedData);
QByteArray compressedData = openFile.readAll();
QByteArray jsonData;
if (!gunzip(compressedData, jsonData)) {
qCritical() << "json file not in gzip format: " << file;
status = false;
return object;
}
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
object = jsonDoc.object();
status = true;
openFile.close();
@ -153,7 +165,7 @@ namespace controller {
QJsonObject InputRecorder::recordDataToJson() {
QJsonObject data;
data["frameCount"] = _framesRecorded;
data["version"] = "1.0";
data["version"] = "0.0";
QJsonArray actionArrayList;
QJsonArray poseArrayList;
@ -187,7 +199,10 @@ namespace controller {
void InputRecorder::saveRecording() {
QJsonObject jsonData = recordDataToJson();
exportToFile(jsonData);
QString timeStamp = QDateTime::currentDateTime().toString(Qt::ISODate);
timeStamp.replace(":", "-");
QString fileName = SAVE_DIRECTORY + FILE_PREFIX_NAME + timeStamp + COMPRESS_EXTENSION;
exportToFile(jsonData, fileName);
}
void InputRecorder::loadRecording(const QString& path) {
@ -202,10 +217,12 @@ namespace controller {
QString filePath = urlPath.toLocalFile();
QFileInfo info(filePath);
QString extension = info.suffix();
if (extension != "gz") {
qWarning() << "can not load file with exentsion of " << extension;
return;
}
bool success = false;
QJsonObject data = openFile(filePath, success);
auto keyValue = data.find("version");
@ -233,34 +250,7 @@ namespace controller {
_poseStateList.push_back(_currentFramePoses);
_currentFramePoses.clear();
}
} else if (success) {
//convert recording to new reacording standard and rewrite file
auto userInputMapper = DependencyManager::get<UserInputMapper>();
_framesRecorded = data["frameCount"].toInt();
QJsonArray actionArrayList = data["actionList"].toArray();
QJsonArray poseArrayList = data["poseList"].toArray();
for (int actionIndex = 0; actionIndex < actionArrayList.size(); actionIndex++) {
QJsonArray actionState = actionArrayList[actionIndex].toArray();
for (int index = 0; index < actionState.size(); index++) {
QString actionName = userInputMapper->getActionName(Action(index));
_currentFrameActions[actionName] = actionState[index].toDouble();
}
_actionStateList.push_back(_currentFrameActions);
_currentFrameActions.clear();
}
for (int poseIndex = 0; poseIndex < poseArrayList.size(); poseIndex++) {
QJsonArray poseState = poseArrayList[poseIndex].toArray();
for (int index = 0; index < poseState.size(); index++) {
QString actionName = userInputMapper->getActionName(Action(index));
_currentFramePoses[actionName] = jsonObjectToPose(poseState[index].toObject());
}
_poseStateList.push_back(_currentFramePoses);
_currentFramePoses.clear();
}
}
}
_loading = false;
}

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

@ -36,9 +36,6 @@ void ActionEndpoint::apply(const Pose& value, const Pointer& source) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
QString actionName = userInputMapper->getActionName(Action(_input.getChannel()));
inputRecorder->setActionState(actionName, _currentPose);
if (inputRecorder->isPlayingback()) {
_currentPose = inputRecorder->getPoseState(actionName);
}
if (!_currentPose.isValid()) {
return;

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

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

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

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

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

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

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

@ -50,11 +50,15 @@ static const char* RENDER_CONTROLLERS = "Render Hand Controllers";
static const int MIN_PUCK_COUNT = 2;
static const int MIN_FEET_AND_HIPS = 3;
static const int MIN_FEET_HIPS_CHEST = 4;
static const int MIN_FEET_HIPS_HEAD = 4;
static const int MIN_FEET_HIPS_SHOULDERS = 5;
static const int MIN_FEET_HIPS_CHEST_HEAD = 5;
static const int FIRST_FOOT = 0;
static const int SECOND_FOOT = 1;
static const int HIP = 2;
static const int CHEST = 3;
static float HEAD_PUCK_Y_OFFSET = -0.0254f;
static float HEAD_PUCK_Z_OFFSET = -0.152f;
const char* ViveControllerManager::NAME { "OpenVR" };
@ -80,6 +84,28 @@ static bool sortPucksXPosition(PuckPosePair firstPuck, PuckPosePair secondPuck)
return (firstPuck.second.translation.x < secondPuck.second.translation.x);
}
static bool determineFeetOrdering(const controller::Pose& poseA, const controller::Pose& poseB, glm::vec3 axis, glm::vec3 axisOrigin) {
glm::vec3 poseAPosition = poseA.getTranslation();
glm::vec3 poseBPosition = poseB.getTranslation();
glm::vec3 poseAVector = poseAPosition - axisOrigin;
glm::vec3 poseBVector = poseBPosition - axisOrigin;
float poseAProjection = glm::dot(poseAVector, axis);
float poseBProjection = glm::dot(poseBVector, axis);
return (poseAProjection > poseBProjection);
}
static glm::vec3 getReferenceHeadXAxis(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) {
glm::mat4 finalHead = defaultToReferenceMat * defaultHead;
return glmExtractRotation(finalHead) * Vectors::UNIT_X;
}
static glm::vec3 getReferenceHeadPosition(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) {
glm::mat4 finalHead = defaultToReferenceMat * defaultHead;
return extractTranslation(finalHead);
}
static QString deviceTrackingResultToString(vr::ETrackingResult trackingResult) {
QString result;
auto iterator = TRACKING_RESULT_TO_STRING.find(trackingResult);
@ -178,6 +204,8 @@ ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : contro
_configStringMap[Config::FeetAndHips] = QString("FeetAndHips");
_configStringMap[Config::FeetHipsAndChest] = QString("FeetHipsAndChest");
_configStringMap[Config::FeetHipsAndShoulders] = QString("FeetHipsAndShoulders");
_configStringMap[Config::FeetHipsChestAndHead] = QString("FeetHipsChestAndHead");
_configStringMap[Config::FeetHipsAndHead] = QString("FeetHipsAndHead");
if (openVrSupported()) {
createPreferences();
@ -358,6 +386,23 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr
int firstShoulderIndex = 3;
int secondShoulderIndex = 4;
calibrateShoulders(defaultToReferenceMat, inputCalibration, firstShoulderIndex, secondShoulderIndex);
} else if (_config == Config::FeetHipsAndHead && puckCount == MIN_FEET_HIPS_HEAD) {
glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration);
glm::vec3 headXAxis = getReferenceHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
glm::vec3 headPosition = getReferenceHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition);
calibrateHips(headPuckDefaultToReferenceMat, inputCalibration);
calibrateHead(headPuckDefaultToReferenceMat, inputCalibration);
_overrideHead = true;
} else if (_config == Config::FeetHipsChestAndHead && puckCount == MIN_FEET_HIPS_CHEST_HEAD) {
glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration);
glm::vec3 headXAxis = getReferenceHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
glm::vec3 headPosition = getReferenceHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat);
calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition);
calibrateHips(headPuckDefaultToReferenceMat, inputCalibration);
calibrateChest(headPuckDefaultToReferenceMat, inputCalibration);
calibrateHead(headPuckDefaultToReferenceMat, inputCalibration);
_overrideHead = true;
} else {
qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks";
uncalibrate();
@ -372,6 +417,7 @@ void ViveControllerManager::InputDevice::uncalibrate() {
_pucksOffset.clear();
_jointToPuckMap.clear();
_calibrated = false;
_overrideHead = false;
}
void ViveControllerManager::InputDevice::updateCalibratedLimbs() {
@ -381,6 +427,10 @@ void ViveControllerManager::InputDevice::updateCalibratedLimbs() {
_poseStateMap[controller::SPINE2] = addOffsetToPuckPose(controller::SPINE2);
_poseStateMap[controller::RIGHT_ARM] = addOffsetToPuckPose(controller::RIGHT_ARM);
_poseStateMap[controller::LEFT_ARM] = addOffsetToPuckPose(controller::LEFT_ARM);
if (_overrideHead) {
_poseStateMap[controller::HEAD] = addOffsetToPuckPose(controller::HEAD);
}
}
controller::Pose ViveControllerManager::InputDevice::addOffsetToPuckPose(int joint) const {
@ -446,6 +496,43 @@ void ViveControllerManager::InputDevice::handleHandController(float deltaTime, u
}
}
glm::mat4 ViveControllerManager::InputDevice::recalculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration) {
glm::mat4 avatarToSensorMat = glm::inverse(inputCalibration.sensorToWorldMat) * inputCalibration.avatarMat;
glm::mat4 sensorToAvatarMat = glm::inverse(inputCalibration.avatarMat) * inputCalibration.sensorToWorldMat;
size_t headPuckIndex = _validTrackedObjects.size() - 1;
controller::Pose headPuckPose = _validTrackedObjects[headPuckIndex].second;
glm::mat4 headPuckAvatarMat = createMatFromQuatAndPos(headPuckPose.getRotation(), headPuckPose.getTranslation()) * Matrices::Y_180;
glm::vec3 headPuckTranslation = extractTranslation(headPuckAvatarMat);
glm::vec3 headPuckZAxis = cancelOutRollAndPitch(glmExtractRotation(headPuckAvatarMat)) * glm::vec3(0.0f, 0.0f, 1.0f);
glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f);
// check that the head puck z axis is not parrallel to the world up
const float EPSILON = 1.0e-4f;
glm::vec3 zAxis = glmExtractRotation(headPuckAvatarMat) * glm::vec3(0.0f, 0.0f, 1.0f);
if (fabsf(fabsf(glm::dot(glm::normalize(worldUp), glm::normalize(zAxis))) - 1.0f) < EPSILON) {
headPuckZAxis = glm::vec3(1.0f, 0.0f, 0.0f);
}
glm::vec3 yPrime = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 xPrime = glm::normalize(glm::cross(worldUp, headPuckZAxis));
glm::vec3 zPrime = glm::normalize(glm::cross(xPrime, yPrime));
glm::mat4 newHeadPuck = glm::mat4(glm::vec4(xPrime, 0.0f), glm::vec4(yPrime, 0.0f),
glm::vec4(zPrime, 0.0f), glm::vec4(headPuckTranslation, 1.0f));
glm::mat4 headPuckOffset = glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f),
glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), glm::vec4(0.0f, HEAD_PUCK_Y_OFFSET, HEAD_PUCK_Z_OFFSET, 1.0f));
glm::mat4 finalHeadPuck = newHeadPuck * headPuckOffset;
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat;
glm::mat4 currentHead = finalHeadPuck * defaultHeadOffset;
// calculate the defaultToRefrenceXform
glm::mat4 defaultToReferenceMat = currentHead * glm::inverse(inputCalibration.defaultHeadMat);
return defaultToReferenceMat;
}
void ViveControllerManager::InputDevice::partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPseudoButton, int xPseudoButton, int yPseudoButton) {
// Populate the L/RS_CENTER/OUTER pseudo buttons, corresponding to a partition of the L/RS space based on the X/Y values.
const float CENTER_DEADBAND = 0.6f;
@ -633,6 +720,26 @@ void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToRefer
}
}
void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, glm::vec3 headXAxis, glm::vec3 headPosition) {
auto& firstFoot = _validTrackedObjects[FIRST_FOOT];
auto& secondFoot = _validTrackedObjects[SECOND_FOOT];
controller::Pose& firstFootPose = firstFoot.second;
controller::Pose& secondFootPose = secondFoot.second;
if (determineFeetOrdering(firstFootPose, secondFootPose, headXAxis, headPosition)) {
_jointToPuckMap[controller::LEFT_FOOT] = firstFoot.first;
_pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, firstFootPose);
_jointToPuckMap[controller::RIGHT_FOOT] = secondFoot.first;
_pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, secondFootPose);
} else {
_jointToPuckMap[controller::LEFT_FOOT] = secondFoot.first;
_pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, secondFootPose);
_jointToPuckMap[controller::RIGHT_FOOT] = firstFoot.first;
_pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, firstFootPose);
}
}
void ViveControllerManager::InputDevice::calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
_jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first;
_pucksOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second);
@ -661,7 +768,19 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo
_jointToPuckMap[controller::RIGHT_ARM] = firstShoulder.first;
_pucksOffset[firstShoulder.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightArm, firstShoulder.second);
}
}
}
void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
size_t headIndex = _validTrackedObjects.size() - 1;
const PuckPosePair& head = _validTrackedObjects[headIndex];
// assume the person is wearing the head puck on his/her forehead
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat;
controller::Pose newHead = head.second.postTransform(defaultHeadOffset);
_jointToPuckMap[controller::HEAD] = head.first;
_pucksOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, newHead);
}
void ViveControllerManager::InputDevice::loadSettings() {
@ -697,6 +816,10 @@ void ViveControllerManager::InputDevice::setConfigFromString(const QString& valu
_preferedConfig = Config::FeetHipsAndChest;
} else if (value == "FeetHipsAndShoulders") {
_preferedConfig = Config::FeetHipsAndShoulders;
} else if (value == "FeetHipsChestAndHead") {
_preferedConfig = Config::FeetHipsChestAndHead;
} else if (value == "FeetHipsAndHead") {
_preferedConfig = Config::FeetHipsAndHead;
}
}
@ -705,11 +828,43 @@ void ViveControllerManager::InputDevice::createPreferences() {
auto preferences = DependencyManager::get<Preferences>();
static const QString VIVE_PUCKS_CONFIG = "Vive Pucks Configuration";
{
static const float MIN_VALUE = -3.0f;
static const float MAX_VALUE = 3.0f;
static const float STEP = 0.01f;
auto getter = [this]()->float { return HEAD_PUCK_Y_OFFSET; };
auto setter = [this](const float& value) { HEAD_PUCK_Y_OFFSET = value; };
auto preference = new SpinnerPreference(VIVE_PUCKS_CONFIG, "HeadPuckYOffset", getter, setter);
preference->setMin(MIN_VALUE);
preference->setMax(MAX_VALUE);
preference->setDecimals(3);
preference->setStep(STEP);
preferences->addPreference(preference);
}
{
static const float MIN_VALUE = -3.0f;
static const float MAX_VALUE = 3.0f;
static const float STEP = 0.01f;
auto getter = [this]()->float { return HEAD_PUCK_Z_OFFSET; };
auto setter = [this](const float& value) { HEAD_PUCK_Z_OFFSET = value; };
auto preference = new SpinnerPreference(VIVE_PUCKS_CONFIG, "HeadPuckXOffset", getter, setter);
preference->setMin(MIN_VALUE);
preference->setMax(MAX_VALUE);
preference->setStep(STEP);
preference->setDecimals(3);
preferences->addPreference(preference);
}
{
auto getter = [this]()->QString { return _configStringMap[_preferedConfig]; };
auto setter = [this](const QString& value) { setConfigFromString(value); saveSettings(); };
auto preference = new ComboBoxPreference(VIVE_PUCKS_CONFIG, "Configuration", getter, setter);
QStringList list = {"Auto", "Feet", "FeetAndHips", "FeetHipsAndChest", "FeetHipsAndShoulders"};
QStringList list = {"Auto", "Feet", "FeetAndHips", "FeetHipsAndChest", "FeetHipsAndShoulders", "FeetHipsAndHead"};
preference->setItems(list);
preferences->addPreference(preference);

View file

@ -69,6 +69,7 @@ private:
void calibrate(const controller::InputCalibrationData& inputCalibration);
void uncalibrate();
controller::Pose addOffsetToPuckPose(int joint) const;
glm::mat4 recalculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration);
void updateCalibratedLimbs();
bool checkForCalibrationEvent();
void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand);
@ -86,11 +87,14 @@ private:
void loadSettings();
void saveSettings() const;
void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, glm::vec3 headXAxis, glm::vec3 headPosition);
void calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
void calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
void calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration,
int firstShoulderIndex, int secondShoulderIndex);
void calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
class FilteredStick {
public:
@ -121,6 +125,8 @@ private:
FeetAndHips,
FeetHipsAndChest,
FeetHipsAndShoulders,
FeetHipsChestAndHead,
FeetHipsAndHead
};
Config _config { Config::Auto };
Config _preferedConfig { Config::Auto };
@ -148,6 +154,7 @@ private:
bool _calibrated { false };
bool _timeTilCalibrationSet { false };
bool _calibrate { false };
bool _overrideHead { false };
mutable std::recursive_mutex _lock;
QString configToString(Config config);

View file

@ -10,7 +10,7 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/* global Script, Users, Overlays, AvatarList, Controller, Camera, getControllerWorldLocation */
/* global Script, Users, Overlays, AvatarList, Controller, Camera, getControllerWorldLocation, UserActivityLogger */
(function () { // BEGIN LOCAL_SCOPE
var button;
@ -76,6 +76,7 @@
// Called from the C++ scripting interface to show the bubble overlay
function enteredIgnoreRadius() {
createOverlays();
UserActivityLogger.bubbleActivated();
}
// Used to set the state of the bubble HUD button
@ -139,10 +140,14 @@
}
// When the space bubble is toggled...
function onBubbleToggled() {
var bubbleActive = Users.getIgnoreRadiusEnabled();
writeButtonProperties(bubbleActive);
if (bubbleActive) {
// NOTE: the c++ calls this with just the first param -- we added a second
// just for not logging the initial state of the bubble when we startup.
function onBubbleToggled(enabled, doNotLog) {
writeButtonProperties(enabled);
if (doNotLog !== true) {
UserActivityLogger.bubbleToggled(enabled);
}
if (enabled) {
createOverlays();
} else {
hideOverlays();
@ -163,7 +168,7 @@
sortOrder: 4
});
onBubbleToggled();
onBubbleToggled(Users.getIgnoreRadiusEnabled(), true); // pass in true so we don't log this initial one in the UserActivity table
button.clicked.connect(Users.toggleIgnoreRadius);
Users.ignoreRadiusEnabledChanged.connect(onBubbleToggled);

View file

@ -217,6 +217,32 @@ function hideMarketplace() {
// }
// }
function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) {
// Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original
// position in the given direction.
var CORNERS = [
{ x: 0, y: 0, z: 0 },
{ x: 0, y: 0, z: 1 },
{ x: 0, y: 1, z: 0 },
{ x: 0, y: 1, z: 1 },
{ x: 1, y: 0, z: 0 },
{ x: 1, y: 0, z: 1 },
{ x: 1, y: 1, z: 0 },
{ x: 1, y: 1, z: 1 },
];
// Go through all corners and find least (most negative) distance in front of position.
var distance = 0;
for (var i = 0, length = CORNERS.length; i < length; i++) {
var cornerVector =
Vec3.multiplyQbyV(orientation, Vec3.multiplyVbyV(Vec3.subtract(CORNERS[i], registration), dimensions));
var cornerDistance = Vec3.dot(cornerVector, direction);
distance = Math.min(cornerDistance, distance);
}
position = Vec3.sum(Vec3.multiply(distance, direction), position);
return position;
}
var TOOLS_PATH = Script.resolvePath("assets/images/tools/");
var GRABBABLE_ENTITIES_MENU_CATEGORY = "Edit";
var GRABBABLE_ENTITIES_MENU_ITEM = "Create Entities As Grabbable";
@ -234,6 +260,32 @@ var toolBar = (function () {
var position = getPositionToCreateEntity();
var entityID = null;
if (position !== null && position !== undefined) {
var direction;
if (Camera.mode === "entity" || Camera.mode === "independent") {
direction = Camera.orientation;
} else {
direction = MyAvatar.orientation;
}
direction = Vec3.multiplyQbyV(direction, Vec3.UNIT_Z);
var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Web"];
if (PRE_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) {
// Adjust position of entity per bounding box prior to creating it.
var registration = properties.registration;
if (registration === undefined) {
var DEFAULT_REGISTRATION = { x: 0.5, y: 0.5, z: 0.5 };
registration = DEFAULT_REGISTRATION;
}
var orientation = properties.orientation;
if (orientation === undefined) {
var DEFAULT_ORIENTATION = Quat.fromPitchYawRollDegrees(0, 0, 0);
orientation = DEFAULT_ORIENTATION;
}
position = adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation);
}
position = grid.snapToSurface(grid.snapToGrid(position, false, dimensions), dimensions);
properties.position = position;
if (Menu.isOptionChecked(GRABBABLE_ENTITIES_MENU_ITEM)) {
@ -243,6 +295,32 @@ var toolBar = (function () {
if (properties.type == "ParticleEffect") {
selectParticleEntity(entityID);
}
var POST_ADJUST_ENTITY_TYPES = ["Model"];
if (POST_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) {
// Adjust position of entity per bounding box after it has been created and auto-resized.
var initialDimensions = Entities.getEntityProperties(entityID, ["dimensions"]).dimensions;
var DIMENSIONS_CHECK_INTERVAL = 200;
var MAX_DIMENSIONS_CHECKS = 10;
var dimensionsCheckCount = 0;
var dimensionsCheckFunction = function () {
dimensionsCheckCount++;
var properties = Entities.getEntityProperties(entityID, ["dimensions", "registrationPoint", "rotation"]);
if (!Vec3.equal(properties.dimensions, initialDimensions)) {
position = adjustPositionPerBoundingBox(position, direction, properties.registrationPoint,
properties.dimensions, properties.rotation);
position = grid.snapToSurface(grid.snapToGrid(position, false, properties.dimensions),
properties.dimensions);
Entities.editEntity(entityID, {
position: position
});
selectionManager._update();
} else if (dimensionsCheckCount < MAX_DIMENSIONS_CHECKS) {
Script.setTimeout(dimensionsCheckFunction, DIMENSIONS_CHECK_INTERVAL);
}
};
Script.setTimeout(dimensionsCheckFunction, DIMENSIONS_CHECK_INTERVAL);
}
} else {
Window.notifyEditError("Can't create " + properties.type + ": " +
properties.type + " would be out of bounds.");
@ -564,6 +642,8 @@ var toolBar = (function () {
enabled: active
}));
isActive = active;
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
if (!isActive) {
entityListTool.setVisible(false);
gridTool.setVisible(false);
@ -572,8 +652,8 @@ var toolBar = (function () {
selectionManager.clearSelections();
cameraManager.disable();
selectionDisplay.triggerMapping.disable();
tablet.landscape = false;
} else {
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
tablet.loadQMLSource("Edit.qml");
UserActivityLogger.enabledEdit();
entityListTool.setVisible(true);
@ -581,6 +661,8 @@ var toolBar = (function () {
grid.setEnabled(true);
propertiesTool.setVisible(true);
selectionDisplay.triggerMapping.enable();
print("starting tablet in landscape mode")
tablet.landscape = true;
// Not sure what the following was meant to accomplish, but it currently causes
// everybody else to think that Interface has lost focus overall. fogbugzid:558
// Window.setFocus();
@ -1407,40 +1489,26 @@ function handeMenuEvent(menuItem) {
}
tooltip.show(false);
}
function getPositionToCreateEntity() {
var HALF_TREE_SCALE = 16384;
var direction = Quat.getForward(MyAvatar.orientation);
var distance = 1;
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(direction, distance));
var HALF_TREE_SCALE = 16384;
function getPositionToCreateEntity(extra) {
var CREATE_DISTANCE = 2;
var position;
var delta = extra !== undefined ? extra : 0;
if (Camera.mode === "entity" || Camera.mode === "independent") {
position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), distance));
position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), CREATE_DISTANCE + delta));
} else {
position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getForward(MyAvatar.orientation), CREATE_DISTANCE + delta));
position.y += 0.5;
}
position.y += 0.5;
if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) {
return null;
}
return position;
}
function getPositionToImportEntity() {
var dimensions = Clipboard.getContentsDimensions();
var HALF_TREE_SCALE = 16384;
var direction = Quat.getForward(MyAvatar.orientation);
var longest = 1;
longest = Math.sqrt(Math.pow(dimensions.x, 2) + Math.pow(dimensions.z, 2));
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(direction, longest));
if (Camera.mode === "entity" || Camera.mode === "independent") {
position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), longest));
}
if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) {
return null;
}
return position;
}
function importSVO(importURL) {
if (!Entities.canRez() && !Entities.canRezTmp()) {
Window.notifyEditError(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG);
@ -1458,22 +1526,73 @@ function importSVO(importURL) {
if (success) {
var VERY_LARGE = 10000;
var position = {
x: 0,
y: 0,
z: 0
};
if (Clipboard.getClipboardContentsLargestDimension() < VERY_LARGE) {
position = getPositionToImportEntity();
var isLargeImport = Clipboard.getClipboardContentsLargestDimension() >= VERY_LARGE;
var position = Vec3.ZERO;
if (!isLargeImport) {
position = getPositionToCreateEntity(Clipboard.getClipboardContentsLargestDimension() / 2);
}
if (position !== null && position !== undefined) {
var pastedEntityIDs = Clipboard.pasteEntities(position);
if (!isLargeImport) {
// The first entity in Clipboard gets the specified position with the rest being relative to it. Therefore, move
// entities after they're imported so that they're all the correct distance in front of and with geometric mean
// centered on the avatar/camera direction.
var deltaPosition = Vec3.ZERO;
var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]);
var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"];
if (NO_ADJUST_ENTITY_TYPES.indexOf(properties.type) === -1) {
var targetDirection;
if (Camera.mode === "entity" || Camera.mode === "independent") {
targetDirection = Camera.orientation;
} else {
targetDirection = MyAvatar.orientation;
}
targetDirection = Vec3.multiplyQbyV(targetDirection, Vec3.UNIT_Z);
var targetPosition = getPositionToCreateEntity();
var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection.
var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection.
var entityPositions = [];
for (var i = 0, length = pastedEntityIDs.length; i < length; i++) {
var properties = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions",
"registrationPoint", "rotation"]);
var adjustedPosition = adjustPositionPerBoundingBox(targetPosition, targetDirection,
properties.registrationPoint, properties.dimensions, properties.rotation);
var delta = Vec3.subtract(adjustedPosition, properties.position);
var distance = Vec3.dot(delta, targetDirection);
deltaParallel = Math.min(distance, deltaParallel);
deltaPerpendicular = Vec3.sum(Vec3.subtract(delta, Vec3.multiply(distance, targetDirection)),
deltaPerpendicular);
entityPositions[i] = properties.position;
}
deltaPerpendicular = Vec3.multiply(1 / pastedEntityIDs.length, deltaPerpendicular);
deltaPosition = Vec3.sum(Vec3.multiply(deltaParallel, targetDirection), deltaPerpendicular);
}
if (grid.getSnapToGrid()) {
var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["position", "dimensions",
"registrationPoint"]);
var position = Vec3.sum(deltaPosition, properties.position);
position = grid.snapToSurface(grid.snapToGrid(position, false, properties.dimensions,
properties.registrationPoint), properties.dimensions, properties.registrationPoint);
deltaPosition = Vec3.subtract(position, properties.position);
}
if (!Vec3.equal(deltaPosition, Vec3.ZERO)) {
for (var i = 0, length = pastedEntityIDs.length; i < length; i++) {
Entities.editEntity(pastedEntityIDs[i], {
position: Vec3.sum(deltaPosition, entityPositions[i])
});
}
}
}
if (isActive) {
selectionManager.setSelections(pastedEntityIDs);
}
} else {
Window.notifyEditError("Can't import objects: objects would be out of bounds.");
Window.notifyEditError("Can't import entities: entities would be out of bounds.");
}
} else {
Window.notifyEditError("There was an error importing the entity file.");
@ -2100,7 +2219,13 @@ function selectParticleEntity(entityID) {
}
entityListTool.webView.webEventReceived.connect(function (data) {
data = JSON.parse(data);
try {
data = JSON.parse(data);
} catch(e) {
print("edit.js: Error parsing JSON: " + e.name + " data " + data)
return;
}
if (data.type === 'parent') {
parentSelectedEntities();
} else if(data.type === 'unparent') {

View file

@ -20,7 +20,8 @@ var ICON_FOR_TYPE = {
Light: "p",
Zone: "o",
PolyVox: "&#xe005;",
Multiple: "&#xe000;"
Multiple: "&#xe000;",
PolyLine: "&#xe01b;"
}
var EDITOR_TIMEOUT_DURATION = 1500;

View file

@ -22,7 +22,9 @@ var DEFAULT_WIDTH = 0.4375;
var DEFAULT_VERTICAL_FIELD_OF_VIEW = 45; // degrees
var SENSOR_TO_ROOM_MATRIX = -2;
var CAMERA_MATRIX = -7;
var ROT_Y_180 = {x: 0, y: 1, z: 0, w: 0};
var ROT_Y_180 = {x: 0.0, y: 1.0, z: 0, w: 0};
var ROT_LANDSCAPE = {x: 1.0, y: 1.0, z: 0, w: 0};
var ROT_LANDSCAPE_WINDOW = {x: 0.0, y: 0.0, z: 0.0, w: 0};
var ROT_IDENT = {x: 0, y: 0, z: 0, w: 1};
var TABLET_TEXTURE_RESOLUTION = { x: 480, y: 706 };
var INCHES_TO_METERS = 1 / 39.3701;
@ -243,29 +245,29 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) {
};
WebTablet.prototype.getDimensions = function() {
if (this.landscape) {
return { x: this.width * 2, y: this.height, z: this.depth };
} else {
return { x: this.width, y: this.height, z: this.depth };
}
return { x: this.width, y: this.height, z: this.depth };
};
WebTablet.prototype.getTabletTextureResolution = function() {
if (this.landscape) {
return { x: TABLET_TEXTURE_RESOLUTION.x * 2, y: TABLET_TEXTURE_RESOLUTION.y };
return { x: TABLET_TEXTURE_RESOLUTION.y , y: TABLET_TEXTURE_RESOLUTION.x };
} else {
return TABLET_TEXTURE_RESOLUTION;
}
};
WebTablet.prototype.setLandscape = function(newLandscapeValue) {
if (this.landscape == newLandscapeValue) {
if (this.landscape === newLandscapeValue) {
return;
}
this.landscape = newLandscapeValue;
Overlays.editOverlay(this.tabletEntityID, { dimensions: this.getDimensions() });
Overlays.editOverlay(this.tabletEntityID,
{ rotation: this.landscape ? Quat.multiply(Camera.orientation, ROT_LANDSCAPE) :
Quat.multiply(Camera.orientation, ROT_Y_180) });
Overlays.editOverlay(this.webOverlayID, {
resolution: this.getTabletTextureResolution()
resolution: this.getTabletTextureResolution(),
rotation: Quat.multiply(Camera.orientation, ROT_LANDSCAPE_WINDOW)
});
};
@ -407,7 +409,7 @@ WebTablet.prototype.calculateWorldAttitudeRelativeToCamera = function (windowPos
return {
position: worldMousePosition,
rotation: Quat.multiply(Camera.orientation, ROT_Y_180)
rotation: this.landscape ? Quat.multiply(Camera.orientation, ROT_LANDSCAPE) : Quat.multiply(Camera.orientation, ROT_Y_180)
};
};

View file

@ -109,7 +109,13 @@ EntityListTool = function(opts) {
};
webView.webEventReceived.connect(function(data) {
data = JSON.parse(data);
try {
data = JSON.parse(data);
} catch(e) {
print("entityList.js: Error parsing JSON: " + e.name + " data " + data)
return;
}
if (data.type == "selectionUpdate") {
var ids = data.entityIds;
var entityIDs = [];

View file

@ -79,7 +79,7 @@ Grid = function(opts) {
}
}
that.snapToSurface = function(position, dimensions) {
that.snapToSurface = function(position, dimensions, registration) {
if (!snapToGrid) {
return position;
}
@ -88,14 +88,18 @@ Grid = function(opts) {
dimensions = { x: 0, y: 0, z: 0 };
}
if (registration === undefined) {
registration = { x: 0.5, y: 0.5, z: 0.5 };
}
return {
x: position.x,
y: origin.y + (dimensions.y / 2),
y: origin.y + (registration.y * dimensions.y),
z: position.z
};
}
that.snapToGrid = function(position, majorOnly, dimensions) {
that.snapToGrid = function(position, majorOnly, dimensions, registration) {
if (!snapToGrid) {
return position;
}
@ -104,6 +108,10 @@ Grid = function(opts) {
dimensions = { x: 0, y: 0, z: 0 };
}
if (registration === undefined) {
registration = { x: 0.5, y: 0.5, z: 0.5 };
}
var spacing = majorOnly ? majorGridEvery : minorGridEvery;
position = Vec3.subtract(position, origin);
@ -112,7 +120,7 @@ Grid = function(opts) {
position.y = Math.round(position.y / spacing) * spacing;
position.z = Math.round(position.z / spacing) * spacing;
return Vec3.sum(Vec3.sum(position, Vec3.multiply(0.5, dimensions)), origin);
return Vec3.sum(Vec3.sum(position, Vec3.multiplyVbyV(registration, dimensions)), origin);
}
that.snapToSpacing = function(delta, majorOnly) {
@ -161,9 +169,9 @@ Grid = function(opts) {
if (data.origin) {
var pos = data.origin;
pos.x = pos.x === undefined ? origin.x : pos.x;
pos.y = pos.y === undefined ? origin.y : pos.y;
pos.z = pos.z === undefined ? origin.z : pos.z;
pos.x = pos.x === undefined ? origin.x : parseFloat(pos.x);
pos.y = pos.y === undefined ? origin.y : parseFloat(pos.y);
pos.z = pos.z === undefined ? origin.z : parseFloat(pos.z);
that.setPosition(pos, true);
}
@ -238,7 +246,13 @@ GridTool = function(opts) {
});
webView.webEventReceived.connect(function(data) {
data = JSON.parse(data);
try {
data = JSON.parse(data);
} catch(e) {
print("gridTool.js: Error parsing JSON: " + e.name + " data " + data)
return;
}
if (data.type == "init") {
horizontalGrid.emitUpdate();
} else if (data.type == "update") {

View file

@ -410,8 +410,15 @@ function takeSnapshot() {
Menu.setIsOptionChecked("Overlays", false);
}
var snapActivateSound = SoundCache.getSound(Script.resolvePath("../../resources/sounds/snap.wav"));
// take snapshot (with no notification)
Script.setTimeout(function () {
Audio.playSound(snapActivateSound, {
position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z },
localOnly: true,
volume: 1.0
});
HMD.closeTablet();
Script.setTimeout(function () {
Window.takeSnapshot(false, includeAnimated, 1.91);

View file

@ -191,16 +191,12 @@
gTablet.updateAudioBar(currentMicLevel);
}
if (validCheckTime - now > MSECS_PER_SEC/4) {
//each 250ms should be just fine
if (now - validCheckTime > MSECS_PER_SEC) {
validCheckTime = now;
updateTabletWidthFromSettings();
if (UIWebTablet) {
UIWebTablet.setLandscape(landscape);
}
}
if (validCheckTime - now > MSECS_PER_SEC) {
validCheckTime = now;
if (tabletRezzed && UIWebTablet && !tabletIsValid()) {
// when we switch domains, the tablet entity gets destroyed and recreated. this causes
// the overlay to be deleted, but not recreated. If the overlay is deleted for this or any

View file

@ -821,6 +821,17 @@ for (var key in trayIcons) {
const notificationIcon = path.join(__dirname, '../resources/console-notification.png');
function isProcessRunning(pid) {
try {
// Sending a signal of 0 is effectively a NOOP.
// If sending the signal is successful, kill will return true.
// If the process is not running, an exception will be thrown.
return process.kill(pid, 0);
} catch (e) {
}
return false;
}
function onContentLoaded() {
// Disable splash window for now.
// maybeShowSplash();
@ -882,31 +893,18 @@ function onContentLoaded() {
startInterface();
}
// If we were launched with the shutdownWatcher option, then we need to watch for the interface app
// shutting down. The interface app will regularly update a running state file which we will check.
// If the file doesn't exist or stops updating for a significant amount of time, we will shut down.
if (argv.shutdownWatcher) {
log.debug("Shutdown watcher requested... argv.shutdownWatcher:", argv.shutdownWatcher);
var MAX_TIME_SINCE_EDIT = 5000; // 5 seconds between updates
var firstAttemptToCheck = new Date().getTime();
var shutdownWatchInterval = setInterval(function(){
var stats = fs.stat(argv.shutdownWatcher, function(err, stats) {
if (err) {
var sinceFirstCheck = new Date().getTime() - firstAttemptToCheck;
if (sinceFirstCheck > MAX_TIME_SINCE_EDIT) {
log.debug("Running state file is missing, assume interface has shutdown... shutting down snadbox.");
forcedShutdown();
clearTimeout(shutdownWatchInterval);
}
} else {
var sinceEdit = new Date().getTime() - stats.mtime.getTime();
if (sinceEdit > MAX_TIME_SINCE_EDIT) {
log.debug("Running state of interface hasn't updated in MAX time... shutting down.");
forcedShutdown();
clearTimeout(shutdownWatchInterval);
}
}
});
// If we were launched with the shutdownWith option, then we need to shutdown when that process (pid)
// is no longer running.
if (argv.shutdownWith) {
let pid = argv.shutdownWith;
console.log("Shutting down with process: ", pid);
let checkProcessInterval = setInterval(function() {
let isRunning = isProcessRunning(pid);
if (!isRunning) {
log.debug("Watched process is no longer running, shutting down");
clearTimeout(checkProcessInterval);
forcedShutdown();
}
}, 1000);
}
}

View file

@ -1,15 +1,9 @@
# Declare dependencies
macro (SETUP_TESTCASE_DEPENDENCIES)
# link in the shared libraries
link_hifi_libraries(shared ktx gpu image)
set(TARGET_NAME ktx-test)
if (WIN32)
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217")
endif()
package_libraries_for_deployment()
endmacro ()
# This is not a testcase -- just set it up as a regular hifi project
setup_hifi_project(Quick Gui OpenGL)
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
# link in the shared libraries
link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image)
package_libraries_for_deployment()
setup_hifi_testcase()

195
tests/ktx/src/KtxTests.cpp Normal file
View file

@ -0,0 +1,195 @@
//
// Created by Bradley Austin Davis on 2016/07/01
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "KtxTests.h"
#include <mutex>
#include <QtTest/QtTest>
#include <ktx/KTX.h>
#include <gpu/Texture.h>
#include <image/Image.h>
QTEST_GUILESS_MAIN(KtxTests)
QString getRootPath() {
static std::once_flag once;
static QString result;
std::call_once(once, [&] {
QFileInfo file(__FILE__);
QDir parent = file.absolutePath();
result = QDir::cleanPath(parent.currentPath() + "/../../..");
});
return result;
}
void KtxTests::initTestCase() {
}
void KtxTests::cleanupTestCase() {
}
void KtxTests::testKhronosCompressionFunctions() {
using namespace khronos::gl::texture;
QCOMPARE(evalAlignedCompressedBlockCount<4>(0), (uint32_t)0x0);
QCOMPARE(evalAlignedCompressedBlockCount<4>(1), (uint32_t)0x1);
QCOMPARE(evalAlignedCompressedBlockCount<4>(4), (uint32_t)0x1);
QCOMPARE(evalAlignedCompressedBlockCount<4>(5), (uint32_t)0x2);
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x00), (uint32_t)0x00);
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x01), (uint32_t)0x01);
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x04), (uint32_t)0x01);
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x05), (uint32_t)0x02);
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x1000), (uint32_t)0x400);
QVERIFY_EXCEPTION_THROWN(evalCompressedBlockCount(InternalFormat::RGBA8, 0x00), std::runtime_error);
}
void KtxTests::testKtxEvalFunctions() {
QCOMPARE(sizeof(ktx::Header), (size_t)64);
QCOMPARE(ktx::evalPadding(0x0), (uint8_t)0);
QCOMPARE(ktx::evalPadding(0x1), (uint8_t)3);
QCOMPARE(ktx::evalPadding(0x2), (uint8_t)2);
QCOMPARE(ktx::evalPadding(0x3), (uint8_t)1);
QCOMPARE(ktx::evalPadding(0x4), (uint8_t)0);
QCOMPARE(ktx::evalPadding(0x400), (uint8_t)0);
QCOMPARE(ktx::evalPadding(0x401), (uint8_t)3);
QCOMPARE(ktx::evalPaddedSize(0x0), 0x0);
QCOMPARE(ktx::evalPaddedSize(0x1), 0x4);
QCOMPARE(ktx::evalPaddedSize(0x2), 0x4);
QCOMPARE(ktx::evalPaddedSize(0x3), 0x4);
QCOMPARE(ktx::evalPaddedSize(0x4), 0x4);
QCOMPARE(ktx::evalPaddedSize(0x400), 0x400);
QCOMPARE(ktx::evalPaddedSize(0x401), 0x404);
QCOMPARE(ktx::evalAlignedCount((uint32_t)0x0), (uint32_t)0x0);
QCOMPARE(ktx::evalAlignedCount((uint32_t)0x1), (uint32_t)0x1);
QCOMPARE(ktx::evalAlignedCount((uint32_t)0x4), (uint32_t)0x1);
QCOMPARE(ktx::evalAlignedCount((uint32_t)0x5), (uint32_t)0x2);
}
void KtxTests::testKtxSerialization() {
const QString TEST_IMAGE = getRootPath() + "/scripts/developer/tests/cube_texture.png";
QImage image(TEST_IMAGE);
gpu::TexturePointer testTexture = image::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true);
auto ktxMemory = gpu::Texture::serialize(*testTexture);
QVERIFY(ktxMemory.get());
// Serialize the image to a file
QTemporaryFile TEST_IMAGE_KTX;
{
const auto& ktxStorage = ktxMemory->getStorage();
QVERIFY(ktx::KTX::validate(ktxStorage));
QVERIFY(ktxMemory->isValid());
auto& outFile = TEST_IMAGE_KTX;
if (!outFile.open()) {
QFAIL("Unable to open file");
}
auto ktxSize = ktxStorage->size();
outFile.resize(ktxSize);
auto dest = outFile.map(0, ktxSize);
memcpy(dest, ktxStorage->data(), ktxSize);
outFile.unmap(dest);
outFile.close();
}
{
auto ktxStorage = std::make_shared<storage::FileStorage>(TEST_IMAGE_KTX.fileName());
QVERIFY(ktx::KTX::validate(ktxStorage));
auto ktxFile = ktx::KTX::create(ktxStorage);
QVERIFY(ktxFile.get());
QVERIFY(ktxFile->isValid());
{
const auto& memStorage = ktxMemory->getStorage();
const auto& fileStorage = ktxFile->getStorage();
QVERIFY(memStorage->size() == fileStorage->size());
QVERIFY(memStorage->data() != fileStorage->data());
QVERIFY(0 == memcmp(memStorage->data(), fileStorage->data(), memStorage->size()));
QVERIFY(ktxFile->_images.size() == ktxMemory->_images.size());
auto imageCount = ktxFile->_images.size();
auto startMemory = ktxMemory->_storage->data();
auto startFile = ktxFile->_storage->data();
for (size_t i = 0; i < imageCount; ++i) {
auto memImages = ktxMemory->_images[i];
auto fileImages = ktxFile->_images[i];
QVERIFY(memImages._padding == fileImages._padding);
QVERIFY(memImages._numFaces == fileImages._numFaces);
QVERIFY(memImages._imageSize == fileImages._imageSize);
QVERIFY(memImages._faceSize == fileImages._faceSize);
QVERIFY(memImages._faceBytes.size() == memImages._numFaces);
QVERIFY(fileImages._faceBytes.size() == fileImages._numFaces);
auto faceCount = fileImages._numFaces;
for (uint32_t face = 0; face < faceCount; ++face) {
auto memFace = memImages._faceBytes[face];
auto memOffset = memFace - startMemory;
auto fileFace = fileImages._faceBytes[face];
auto fileOffset = fileFace - startFile;
QVERIFY(memOffset % 4 == 0);
QVERIFY(memOffset == fileOffset);
}
}
}
}
testTexture->setKtxBacking(TEST_IMAGE_KTX.fileName().toStdString());
}
#if 0
static const QString TEST_FOLDER { "H:/ktx_cacheold" };
//static const QString TEST_FOLDER { "C:/Users/bdavis/Git/KTX/testimages" };
//static const QString EXTENSIONS { "4bbdf8f786470e4ab3e672d44b8e8df2.ktx" };
static const QString EXTENSIONS { "*.ktx" };
int mainTemp(int, char**) {
qInstallMessageHandler(messageHandler);
auto fileInfoList = QDir { TEST_FOLDER }.entryInfoList(QStringList { EXTENSIONS });
for (auto fileInfo : fileInfoList) {
qDebug() << fileInfo.filePath();
std::shared_ptr<storage::Storage> storage { new storage::FileStorage { fileInfo.filePath() } };
if (!ktx::KTX::validate(storage)) {
qDebug() << "KTX invalid";
}
auto ktxFile = ktx::KTX::create(storage);
ktx::KTXDescriptor ktxDescriptor = ktxFile->toDescriptor();
qDebug() << "Contains " << ktxDescriptor.keyValues.size() << " key value pairs";
for (const auto& kv : ktxDescriptor.keyValues) {
qDebug() << "\t" << kv._key.c_str();
}
auto offsetToMinMipKV = ktxDescriptor.getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY);
if (offsetToMinMipKV) {
auto data = storage->data() + ktx::KTX_HEADER_SIZE + offsetToMinMipKV;
auto minMipLevelAvailable = *data;
qDebug() << "\tMin mip available " << minMipLevelAvailable;
assert(minMipLevelAvailable < ktxDescriptor.header.numberOfMipmapLevels);
}
auto storageSize = storage->size();
for (const auto& faceImageDesc : ktxDescriptor.images) {
//assert(0 == (faceImageDesc._faceSize % 4));
for (const auto& faceOffset : faceImageDesc._faceOffsets) {
assert(0 == (faceOffset % 4));
auto faceEndOffset = faceOffset + faceImageDesc._faceSize;
assert(faceEndOffset <= storageSize);
}
}
for (const auto& faceImage : ktxFile->_images) {
for (const ktx::Byte* faceBytes : faceImage._faceBytes) {
assert(0 == (reinterpret_cast<size_t>(faceBytes) % 4));
}
}
}
return 0;
}
#endif

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